From patchwork Wed Apr 3 15:35:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884021 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3C07B922 for ; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2327C28787 for ; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1783728972; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C4EC228787 for ; Wed, 3 Apr 2019 15:35:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726084AbfDCPf4 (ORCPT ); Wed, 3 Apr 2019 11:35:56 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51741 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726269AbfDCPf4 (ORCPT ); Wed, 3 Apr 2019 11:35:56 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VL-SS; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhvd-00067b-UG; Wed, 03 Apr 2019 16:35:53 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 1/6] validation: ignore temporary ~ files Date: Wed, 3 Apr 2019 16:35:47 +0100 Message-Id: <20190403153552.23461-2-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Ignore any ~ files left in the directory. Signed-off-by: Ben Dooks --- validation/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/validation/.gitignore b/validation/.gitignore index 77276ba..e82b70e 100644 --- a/validation/.gitignore +++ b/validation/.gitignore @@ -2,3 +2,4 @@ *.diff *.got *.expected +*~ From patchwork Wed Apr 3 15:35:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884027 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5FBA4922 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48A4928770 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3D4B4287F4; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AE61128770 for ; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725959AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51727 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726074AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VN-K5; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhvd-00067d-VG; Wed, 03 Apr 2019 16:35:53 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 2/6] parse: initial parsing of __attribute__((format)) Date: Wed, 3 Apr 2019 16:35:48 +0100 Message-Id: <20190403153552.23461-3-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add code to parse the __attribute__((format)) used to indicate that a variadic function takes a printf-style format string and where those are. Save the data in ctype ready for checking when such an function is encoutered. Signed-off-by: Ben Dooks --- Fixes since v1: - moved to using ctype in base_type to store infromation - fixed formatting issues - updated check for bad format arguments - reduced the line count to unsigned short to save space Fixes since v2: - correctly use the decl->ctype to store printf information - fixed issues with checking format positions for va_list code - parse but ignore scanf formatting for now Fixes since v4: - deal with function pointers losing format info --- parse.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- symbol.h | 2 ++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/parse.c b/parse.c index f291e24..42ba457 100644 --- a/parse.c +++ b/parse.c @@ -87,7 +87,7 @@ static attr_t attribute_address_space, attribute_context, attribute_designated_init, attribute_transparent_union, ignore_attribute, - attribute_mode, attribute_force; + attribute_mode, attribute_force, attribute_format; typedef struct symbol *to_mode_t(struct symbol *); @@ -136,6 +136,10 @@ static void asm_modifier_inline(struct token *token, unsigned long *mods) asm_modifier(token, mods, MOD_INLINE); } +enum { + FmtPrintf = 0, FmtScanf, +}; + static struct symbol_op typedef_op = { .type = KW_MODIFIER, .declarator = typedef_specifier, @@ -386,6 +390,10 @@ static struct symbol_op attr_force_op = { .attribute = attribute_force, }; +static struct symbol_op attr_format = { + .attribute = attribute_format, +}; + static struct symbol_op address_space_op = { .attribute = attribute_address_space, }; @@ -445,6 +453,16 @@ static struct symbol_op mode_word_op = { .to_mode = to_word_mode }; +static struct symbol_op attr_printf_op = { + .type = KW_FORMAT, + .class = FmtPrintf, +}; + +static struct symbol_op attr_scanf_op = { + .type = KW_FORMAT, + .class = FmtScanf, +}; + /* Using NS_TYPEDEF will also make the keyword a reserved one */ static struct init_keyword { const char *name; @@ -570,6 +588,10 @@ static struct init_keyword { {"externally_visible", NS_KEYWORD, .op = &ext_visible_op }, {"__externally_visible__", NS_KEYWORD, .op = &ext_visible_op }, + { "format", NS_KEYWORD, .op = &attr_format }, + { "printf", NS_KEYWORD, .op = &attr_printf_op }, + { "scanf", NS_KEYWORD, .op = &attr_scanf_op }, + { "mode", NS_KEYWORD, .op = &mode_op }, { "__mode__", NS_KEYWORD, .op = &mode_op }, { "QI", NS_KEYWORD, .op = &mode_QI_op }, @@ -1172,6 +1194,74 @@ static struct token *attribute_address_space(struct token *token, struct symbol return token; } +static int invalid_printf_format_args(long long start, long long at) +{ + return start < 0 || at < 0 || (start == at && start > 0) || + (start == 0 && at == 0); +} + +static struct token *attribute_format(struct token *token, struct symbol *attr, struct decl_state *ctx) +{ + struct expression *args[3]; + struct symbol *fmt_sym = NULL; + int argc = 0; + + /* expecting format ( type, start, va_args at) */ + + token = expect(token, '(', "after format attribute"); + while (!match_op(token, ')')) { + struct expression *expr = NULL; + + if (argc == 0) { + if (token_type(token) == TOKEN_IDENT) + fmt_sym = lookup_keyword(token->ident, NS_KEYWORD); + + if (!fmt_sym || !fmt_sym->op || + fmt_sym->op->type != KW_FORMAT) { + sparse_error(token->pos, + "unknown format type '%s'\n", + show_ident(token->ident)); + fmt_sym = NULL; + } + } + + token = conditional_expression(token, &expr); + if (!expr) + break; + if (argc < 3) + args[argc++] = expr; + if (!match_op(token, ',')) + break; + token = token->next; + } + + if (argc != 3 || !fmt_sym) { + warning(token->pos, "incorrect format attribute"); + } else if (fmt_sym->op->class != FmtPrintf) { + /* skip anything that isn't printf for the moment */ + warning(token->pos, "only printf format attribute supported"); + } else { + long long start, at; + + start = get_expression_value(args[2]); + at = get_expression_value(args[1]); + + if (invalid_printf_format_args(start, at)) { + warning(token->pos, "bad format positions"); + } else if (start == 0) { + /* nothing to do here, is va_list function */ + } else if (start < at) { + warning(token->pos, "format cannot be after va_args"); + } else { + ctx->ctype.printf_va_start = start; + ctx->ctype.printf_msg = at; + } + } + + token = expect(token, ')', "after format attribute"); + return token; +} + static struct symbol *to_QI_mode(struct symbol *ctype) { if (ctype->ctype.base_type != &int_type) @@ -2981,8 +3071,18 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis if (!(decl->ctype.modifiers & MOD_STATIC)) decl->ctype.modifiers |= MOD_EXTERN; + + base_type->ctype.printf_msg = decl->ctype.printf_msg; + base_type->ctype.printf_va_start = decl->ctype.printf_va_start; } else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) { sparse_error(token->pos, "void declaration"); + } else if (base_type && base_type->type == SYM_PTR) { + // think this is correct to do // + struct symbol *ptr_base = get_base_type(base_type); + if (ptr_base) { + ptr_base->ctype.printf_msg = decl->ctype.printf_msg; + ptr_base->ctype.printf_va_start = decl->ctype.printf_va_start; + } } if (base_type == &incomplete_ctype) { warning(decl->pos, "'%s' has implicit type", show_ident(decl->ident)); diff --git a/symbol.h b/symbol.h index ac43b31..7bb6f29 100644 --- a/symbol.h +++ b/symbol.h @@ -86,6 +86,7 @@ enum keyword { KW_SHORT = 1 << 7, KW_LONG = 1 << 8, KW_EXACT = 1 << 9, + KW_FORMAT = 1 << 10, }; struct context { @@ -103,6 +104,7 @@ struct ctype { struct context_list *contexts; struct ident *as; struct symbol *base_type; + unsigned short printf_va_start, printf_msg; }; struct decl_state { From patchwork Wed Apr 3 15:35:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884031 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 22705922 for ; Wed, 3 Apr 2019 15:36:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0857728770 for ; Wed, 3 Apr 2019 15:36:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F0D4728827; Wed, 3 Apr 2019 15:36:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0E02828770 for ; Wed, 3 Apr 2019 15:36:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726458AbfDCPgA (ORCPT ); Wed, 3 Apr 2019 11:36:00 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51742 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726329AbfDCPgA (ORCPT ); Wed, 3 Apr 2019 11:36:00 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VO-TN; Wed, 03 Apr 2019 16:35:55 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhve-00067f-00; Wed, 03 Apr 2019 16:35:54 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 3/6] evaluate: check variadic argument types against formatting info Date: Wed, 3 Apr 2019 16:35:49 +0100 Message-Id: <20190403153552.23461-4-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The variadic argumnet code did not check any of the variadic arguments as it did not previously know the possible type. Now we have the possible formatting information stored in the ctype, we can do some checks on the printf formatting types. Signed-off-by: Ben Dooks --- Fixes since v1: - Split out the format-string -> symbol code - Use symbol_list for the symbols from format parsing - Changed to follow the new parsing code and ctype use - Merged the unsigned-int/long types together Fixes since v2: - Check for printf_va_start before checking variadic-list - Tidy the type code and fix a couple of bugs with %l and %ll - Fix function names in working through printf arguments. - Tidy documentation Fixes since v3: - Added positional arguments - Also added precision and width specifiers Fixes since v4: - Stop copying the format string - Removed void data pointer - Suggested code cleanups Fixes since v5: - Rewritten format parsing code - Updates for handling kernel printk formatting - Fix parsing issues with ')' characters Notes: - Awaiting new pointer-list accessing code - %p still generates an address-space mismatch - how do we deal with the kernel's attempt to make printk format all types? hack: ) will end format too --- evaluate.c | 420 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) diff --git a/evaluate.c b/evaluate.c index d9cd41d..d6e8415 100644 --- a/evaluate.c +++ b/evaluate.c @@ -2319,6 +2319,420 @@ static struct symbol *evaluate_alignof(struct expression *expr) return size_t_ctype; } +struct format_type; +struct format_type { + const char *format; + int (*test)(struct format_type *fmt, struct expression **expr, struct symbol *ctype, struct symbol **target, const char **typediff); + void *data; +}; + +struct format_state { + struct expression *expr; + unsigned int va_start; + unsigned int fmt_index; + unsigned int arg_index; + unsigned int used_position: 1; +}; + +static int printf_fmt_numtype(struct format_type *fmt, struct expression **expr, struct symbol *ctype, struct symbol **target, const char **typediff) +{ + struct symbol *type = fmt->data; + *target = type; + return ctype == type; +} + +static int printf_fmt_string(struct format_type *fmt, struct expression **expr, struct symbol *ctype, struct symbol **target, const char **typediff) +{ + *target = &string_ctype; + return check_assignment_types(*target, expr, typediff); +} + +static int printf_fmt_pointer(struct format_type *fmt, struct expression **expr, struct symbol *ctype, struct symbol **target, const char **typediff) +{ + *target = &ptr_ctype; + return check_assignment_types(*target, expr, typediff); +} + +static int printf_fmt_print_pointer(struct format_type *fmt, struct expression **expr, struct symbol *ctype, struct symbol **target, const char **typediff) +{ + int ret; + *target = &ptr_ctype; + ret =check_assignment_types(*target, expr, typediff); + if (ret == 0) { + /* if just printing, ignore address-space mismatches */ + if (strcmp(*typediff, "different address spaces") == 0) + ret = 1; + } + return ret; +} + +static struct format_type printf_fmt_ptr_ref = { "p", .test = printf_fmt_pointer, }; + +static struct expression *get_expression_n(struct expression_list *args, int nr) +{ + return ptr_list_nth_entry((struct ptr_list *)args, nr); +} + +static struct format_type ret; + +static struct format_type *parse_printf_get_fmt(const char *msg, const char **msgout) +{ + struct format_type *type = &ret; + const char *ptr = msg; + int szmod=0; + + type->test = NULL; + *msgout = ptr; + + if (*ptr == 's') { + ptr++; + type->test = printf_fmt_string; + } else if (*ptr == 'c') { + ptr++; + type->test = printf_fmt_numtype; + type->data = &char_ctype; + } else if (*ptr == 'p') { + ptr++; + type->test = printf_fmt_print_pointer; + //todo - check if there's anything after these? + if (*ptr == 'x' || *ptr == 'X') { + ptr++; + } else if (isalpha(*ptr)) { + // probably sxomething that /is/ being de-referenced + ptr++; + type->test = printf_fmt_pointer; + } + } else if (*ptr == 'z') { + ptr++; + if (*ptr == 'd') { + ptr++; + type->test = printf_fmt_numtype; + type->data = &long_ctype; + } else if (*ptr == 'u' || *ptr == 'x') { + ptr++; + type->test = printf_fmt_numtype; + type->data = &ulong_ctype; + } + } else { + if (*ptr == 'l') { + szmod++; + ptr++; + if (*ptr == 'l') { + szmod++; + ptr++; + } + } else { + if (*ptr == 'h') { // short/char to int + szmod = -1; + ptr++; + if (*ptr == 'h') // promotion from char + ptr++; + } + if (*ptr == 't') { // ptrdiff_t + szmod = 2; + ptr++; + } + if (*ptr == 'j') { // intmax_t + szmod = 1; + ptr++; + } + } + + if (*ptr == 'x' || *ptr == 'X' || *ptr == 'u' || *ptr == 'o') { + ptr++; + type->test = printf_fmt_numtype; + switch (szmod) { + case -1: + type->data = &ushort_ctype; + break; + case 0: + type->data = &uint_ctype; + break; + case 1: + type->data = &ulong_ctype; + break; + case 2: + type->data = &ullong_ctype; + break; + default: + type->test = NULL; + } + } else if (*ptr == 'i' || *ptr == 'd') { + ptr++; + type->test = printf_fmt_numtype; + switch (szmod) { + case -1: + type->data = &short_ctype; + break; + case 0: + type->data = &int_ctype; + break; + case 1: + type->data = &long_ctype; + break; + case 2: + type->data = &llong_ctype; + break; + default: + type->test = NULL; + } + } else if (*ptr == 'f' || *ptr == 'g' || *ptr == 'F' || *ptr == 'G') { + type->test = printf_fmt_numtype; + type->data = &double_ctype; + ptr++; + } else if (*ptr == 'L') { + type->test = printf_fmt_numtype; + type->data = &ldouble_ctype; + ptr++; + } else if (*ptr == 'n') { /* pointer to an de-referenced int/etc */ + // todo - we should construct pointer to int/etc // + type->test = printf_fmt_pointer; + ptr++; + } else { + // anything else here? + } + } + + if (type->test == NULL) + return NULL; + + *msgout = ptr; + return type; +} + +static int is_printf_flag(char ch) +{ + return ch == '0' || ch == '+' || ch == '-' || ch == ' ' || ch == '#'; +} + +static int printf_check_position(const char **fmt) +{ + const char *ptr= *fmt; + + if (!isdigit(*ptr)) + return -1; + while (isdigit(*ptr)) + ptr++; + if (*ptr == '$') { + const char *pos = *fmt; + *fmt = ptr+1; + return strtoul(pos, NULL, 10); + } + return -1; +} + +static void parse_format_printf_checkpos(struct format_state *state, const char *which) +{ + if (state->used_position) + warning(state->expr->pos, + "format %d: %s: no position specified", + state->arg_index-1, which); +} + +static int parse_format_printf_argfield(const char **fmtptr, struct format_state *state, struct expression_list *args, int *pos, const char *which) +{ + struct expression *expr; + struct symbol *ctype; + const char *fmt = *fmtptr; + int argpos = -1; + + /* check for simple digit-string width/precision specifier first */ + if (*fmt != '*') { + while (isdigit(*fmt)) + fmt++; + *fmtptr = fmt; + return 0; + } + + fmt++; + argpos = printf_check_position(&fmt); + + if (argpos > 0) { + argpos += state->va_start - 1; + state->used_position = 1; + } else { + argpos = (*pos)++; + state->arg_index++; + parse_format_printf_checkpos(state, which); + } + + *fmtptr = fmt; + expr = get_expression_n(args, argpos-1); + if (!expr) { + warning(state->expr->pos, "%s: no argument at position %d", which, argpos); + return 1; + } + + /* check the vale we got was int/uint type */ + ctype = evaluate_expression(expr); + if (ctype) { + struct symbol *source, *target = &int_ctype; + + source = degenerate(expr); + + if (source != &int_ctype && source != &uint_ctype) { + warning(expr->pos, "incorrect type for %s argument %d", which, argpos); + info(expr->pos, " expected %s", show_typename(target)); + info(expr->pos, " got %s", show_typename(source)); + } + } + + return 0; +} + +/* printf format parsing code + * + * this code currently does not: + * - check castable types (such as int vs long vs long long) + * - validate all arguments specified are also used... + */ +static int parse_format_printf(const char **fmtstring, + struct format_state *state, + struct expression_list *args) +{ + struct format_type *type; + struct expression *expr; + const char *fmt = *fmtstring; + const char *fmtpost = NULL; + int pos = state->arg_index; + int error = 0; + int ret; + + if (!fmt) { + warning(state->expr->pos, "no format string passed"); + return -1; + } + + /* trivial check for %% */ + fmt++; + if (fmt[0] == '%') { + *fmtstring = fmt+1; + return 0; + } + + state->arg_index++; + state->fmt_index++; + + ret = printf_check_position(&fmt); + if (ret == 0) { + /* we got an invalid position argument */ + error++; + } else if (ret < 0) { + parse_format_printf_checkpos(state, "position"); + } else { + state->used_position = 1; + pos = ret + state->va_start - 1; + } + + /* get rid of any formatting flag bits */ + while (is_printf_flag(*fmt)) + fmt++; + + /* now there is the posibility of a width specifier */ + if (parse_format_printf_argfield(&fmt, state, args, &pos, "width")) + error++; + + /* now we might have the precision specifier */ + if (*fmt == '.') { + fmt++; + if (parse_format_printf_argfield(&fmt, state, args, &pos, "position")) + error++; + } + + type = parse_printf_get_fmt(fmt, &fmtpost); + + if (!type && fmt[0] == 'p') + type = &printf_fmt_ptr_ref; /* probably some extension */ + + if (type) { + struct symbol *ctype, *source, *target = NULL; + const char *typediff = "different types"; + int ret; + + *fmtstring = fmtpost; + expr = get_expression_n(args, pos-1); + if (!expr) { + /* no argument, but otherwise valid argument string */ + warning(state->expr->pos, "no argument at position '%d'", pos); + return 0; + } + + ctype = evaluate_expression(expr); + if (!ctype) + return -3; + + source = degenerate(expr); + ret = (type->test)(type, &expr, ctype, &target, &typediff); + if (!target) /* shouldn't happen, but catch anyway */ + return -4; + + if (ret == 0) { + warning(expr->pos, "incorrect type in argument %d (%s)", pos, typediff); + info(expr->pos, " expected %s", show_typename(target)); + info(expr->pos, " got %s", show_typename(source)); + } + } else { + /* try and find the end of this */ + fmtpost = *fmtstring; + while (*fmtpost > ' ') + fmtpost++; + warning(state->expr->pos, "cannot evaluate type '%.*s'", + (int)(fmtpost - *fmtstring), *fmtstring); + *fmtstring += 1; + return -1; + } + + return 1; +} + +/* attempt to run through a printf format string and work out the types + * it specifies. The format is parsed from the __attribute__(format()) + * in the parser code which stores the positions of the message and arg + * start in the ctype. + */ +static void evaluate_format_printf(struct symbol *fn, struct expression_list *head) +{ + struct format_state state = { }; + struct expression *expr; + const char *fmt_string = NULL; + + expr = get_expression_n(head, fn->ctype.printf_msg-1); + if (!expr) + return; + if (expr->string && expr->string->length) + fmt_string = expr->string->data; + if (!fmt_string) { + struct symbol *sym = evaluate_expression(expr); + + /* attempt to find initialiser for this */ + if (sym && sym->initializer && sym->initializer->string) + fmt_string = sym->initializer->string->data; + } + + state.expr = expr; + state.va_start = fn->ctype.printf_va_start; + state.arg_index = fn->ctype.printf_va_start; + + if (!fmt_string) { + warning(expr->pos, "not a format string?"); + } else { + const char *string = fmt_string; + int fail = 0; + + for (; string[0] != '\0'; string++) { + if (string[0] != '%') + continue; + if (parse_format_printf(&string, &state, head) < 0) + fail++; + string--; + } + + if (fail > 0) + /* format string may have '\n' etc embedded in it */ + warning(expr->pos, "cannot evaluate format string"); + } +} + static int evaluate_arguments(struct symbol *fn, struct expression_list *head) { struct expression *expr; @@ -2326,6 +2740,12 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head) struct symbol *argtype; int i = 1; + /* do this first, otherwise the arugment info may get lost or changed + * later on in the evaluation loop. + */ + if (fn->ctype.printf_va_start) + evaluate_format_printf(fn, head); + PREPARE_PTR_LIST(argument_types, argtype); FOR_EACH_PTR (head, expr) { struct expression **p = THIS_ADDRESS(expr); From patchwork Wed Apr 3 15:35:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884023 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7E75818E8 for ; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 656A728770 for ; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 598C328787; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F180C287F4 for ; Wed, 3 Apr 2019 15:35:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726269AbfDCPf4 (ORCPT ); Wed, 3 Apr 2019 11:35:56 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51740 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726264AbfDCPf4 (ORCPT ); Wed, 3 Apr 2019 11:35:56 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VQ-Qg; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhve-00067h-1b; Wed, 03 Apr 2019 16:35:54 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 4/6] add -Wformat Date: Wed, 3 Apr 2019 16:35:50 +0100 Message-Id: <20190403153552.23461-5-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add option to enable/disable format checking (and default it to on) Signed-off-by: Ben Dooks --- Changes since v1: - Default -Wformat off --- evaluate.c | 2 +- lib.c | 2 ++ lib.h | 1 + sparse.1 | 9 +++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/evaluate.c b/evaluate.c index d6e8415..a21bc3f 100644 --- a/evaluate.c +++ b/evaluate.c @@ -2743,7 +2743,7 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head) /* do this first, otherwise the arugment info may get lost or changed * later on in the evaluation loop. */ - if (fn->ctype.printf_va_start) + if (Wformat && fn->ctype.printf_va_start) evaluate_format_printf(fn, head); PREPARE_PTR_LIST(argument_types, argtype); diff --git a/lib.c b/lib.c index 83e6a1e..b3bb2e6 100644 --- a/lib.c +++ b/lib.c @@ -269,6 +269,7 @@ int Wimplicit_int = 1; int Winit_cstring = 0; int Wint_to_pointer_cast = 1; int Wenum_mismatch = 1; +int Wformat = 0; int Wsparse_error = 0; int Wmemcpy_max_count = 1; int Wnon_pointer_null = 1; @@ -651,6 +652,7 @@ static const struct flag warnings[] = { { "implicit-int", &Wimplicit_int }, { "init-cstring", &Winit_cstring }, { "int-to-pointer-cast", &Wint_to_pointer_cast }, + { "format", &Wformat }, { "memcpy-max-count", &Wmemcpy_max_count }, { "non-pointer-null", &Wnon_pointer_null }, { "old-initializer", &Wold_initializer }, diff --git a/lib.h b/lib.h index 322408b..c606807 100644 --- a/lib.h +++ b/lib.h @@ -152,6 +152,7 @@ extern int Wdefault_bitfield_sign; extern int Wdesignated_init; extern int Wdo_while; extern int Wenum_mismatch; +extern int Wformat; extern int Wsparse_error; extern int Wimplicit_int; extern int Winit_cstring; diff --git a/sparse.1 b/sparse.1 index 78d0b1e..1bf4e3c 100644 --- a/sparse.1 +++ b/sparse.1 @@ -260,6 +260,15 @@ trouble. Sparse does not issue these warnings by default. . .TP +.B \-Wformat +Warn about parameter mis-match to any variadic function which specifies +where the format string is specified with the +.BI __attribute__((format( type, message, va_start ))) +attribute. + +Sparse does not issues these warnings by default. To turn them on, use +\fB\-Wno\-format\fR +.TP .B \-Wmemcpy\-max\-count Warn about call of \fBmemcpy()\fR, \fBmemset()\fR, \fBcopy_from_user()\fR, or \fBcopy_to_user()\fR with a large compile-time byte count. From patchwork Wed Apr 3 15:35:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884029 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A76E817E0 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9110428787 for ; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 85CDC287F4; Wed, 3 Apr 2019 15:35:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CEE6428787 for ; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726074AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51739 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726155AbfDCPf6 (ORCPT ); Wed, 3 Apr 2019 11:35:58 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VM-Po; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhve-00067j-2s; Wed, 03 Apr 2019 16:35:54 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 5/6] tests: add varargs printf format tests Date: Wed, 3 Apr 2019 16:35:51 +0100 Message-Id: <20190403153552.23461-6-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add some tests for the new printf format checking code. Note, these do not all pass yet. Signed-off-by: Ben Dooks --- validation/varargs-format-addrspace1.c | 63 ++++++++++++++++++++++++++ validation/varargs-format-bad.c | 20 ++++++++ validation/varargs-format-checking.c | 20 ++++++++ validation/varargs-format-position.c | 31 +++++++++++++ validation/varargs-format-prefix.c | 19 ++++++++ validation/varargs-format-tests.c | 37 +++++++++++++++ 6 files changed, 190 insertions(+) create mode 100644 validation/varargs-format-addrspace1.c create mode 100644 validation/varargs-format-bad.c create mode 100644 validation/varargs-format-checking.c create mode 100644 validation/varargs-format-position.c create mode 100644 validation/varargs-format-prefix.c create mode 100644 validation/varargs-format-tests.c diff --git a/validation/varargs-format-addrspace1.c b/validation/varargs-format-addrspace1.c new file mode 100644 index 0000000..deb7ad3 --- /dev/null +++ b/validation/varargs-format-addrspace1.c @@ -0,0 +1,63 @@ + +extern int variadic(char *msg, ...) __attribute__((format (printf, 1, 2))); +extern int variadic2(char *msg, int , ...) __attribute__((format (printf, 1, 3))); +extern int variadic3(int, char *msg, ...) __attribute__((format (printf, 2, 3))); + +static void test(void) { + void __attribute__((noderef, address_space(1))) *a; + void *b; + + variadic("%s\n", a); + variadic("%s\n", b); + variadic("%s %s\n", b, a); + variadic2("%s %s\n", 1, b, a); + variadic3(1, "%s %s\n", b, a); + variadic3(1, "%s %p\n", b, a); +} + +static char __msg[] = "%s %p"; + +static void test2(void) { + void __attribute__((noderef, address_space(1))) *a; + void *b; + int (*ptr)(char *msg, ...) __attribute__((format (printf, 1, 2))) = variadic; + int (*ptr2)(char *msg, ...) __attribute__((format (printf, 1, 2))); + int (*ptr3)(int c, char *msg, ...) __attribute__((format (printf, 2, 3))); + + variadic(__msg, a, b); + ptr("hello %s %s", a, b); + ptr2("hello %s %s", a, b); + ptr3(1, "hello %s %s", a, b); +} + +/* + * check-name: variadic formatting test with address-space to %s + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-addrspace1.c:10:26: warning: incorrect type in argument 2 (different address spaces) +varargs-format-addrspace1.c:10:26: expected char * +varargs-format-addrspace1.c:10:26: got void [noderef] *a +varargs-format-addrspace1.c:12:32: warning: incorrect type in argument 3 (different address spaces) +varargs-format-addrspace1.c:12:32: expected char * +varargs-format-addrspace1.c:12:32: got void [noderef] *a +varargs-format-addrspace1.c:13:36: warning: incorrect type in argument 4 (different address spaces) +varargs-format-addrspace1.c:13:36: expected char * +varargs-format-addrspace1.c:13:36: got void [noderef] *a +varargs-format-addrspace1.c:14:36: warning: incorrect type in argument 4 (different address spaces) +varargs-format-addrspace1.c:14:36: expected char * +varargs-format-addrspace1.c:14:36: got void [noderef] *a +varargs-format-addrspace1.c:27:25: warning: incorrect type in argument 2 (different address spaces) +varargs-format-addrspace1.c:27:25: expected char * +varargs-format-addrspace1.c:27:25: got void [noderef] *a +varargs-format-addrspace1.c:28:28: warning: incorrect type in argument 2 (different address spaces) +varargs-format-addrspace1.c:28:28: expected char * +varargs-format-addrspace1.c:28:28: got void [noderef] *a +varargs-format-addrspace1.c:29:29: warning: incorrect type in argument 2 (different address spaces) +varargs-format-addrspace1.c:29:29: expected char * +varargs-format-addrspace1.c:29:29: got void [noderef] *a +varargs-format-addrspace1.c:30:32: warning: incorrect type in argument 3 (different address spaces) +varargs-format-addrspace1.c:30:32: expected char * +varargs-format-addrspace1.c:30:32: got void [noderef] *a + * check-error-end + */ diff --git a/validation/varargs-format-bad.c b/validation/varargs-format-bad.c new file mode 100644 index 0000000..12b11f4 --- /dev/null +++ b/validation/varargs-format-bad.c @@ -0,0 +1,20 @@ + +extern int variadic(char *msg, ...) __attribute__((format (printf, 0, 0))); +extern int variadic2(char *msg, int , ...) __attribute__((format (printf, 2, 2))); +extern int variadic3(char *msg, int , ...) __attribute__((format (printf, 2, 1))); + +static void test(void) { +} + +/* + * check-name: variadic formatting test with bad formatting parameters + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-bad.c:2:72: warning: bad format positions +varargs-format-bad.c:3:79: warning: bad format positions +varargs-format-bad.c:4:79: warning: format cannot be after va_args +* check-error-end + */ + + diff --git a/validation/varargs-format-checking.c b/validation/varargs-format-checking.c new file mode 100644 index 0000000..4444800 --- /dev/null +++ b/validation/varargs-format-checking.c @@ -0,0 +1,20 @@ + +extern void pf(char *msg, ...) __attribute__((format (printf, 1, 2))); + +static void test(void) { + pf("%u %lu %llu\n", 1U, 1UL, 1ULL); + pf("%d %ld %lld\n", 1, 1L, 1LL); + pf("%x %lx %llx\n", 1U, 1UL, 1ULL); + pf("%d %ld %lld\n", 1, 1L, 1L); +} + +/* + * check-name: variadic formatting test type checking + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-checking.c:8:36: warning: incorrect type in argument 4 (different types) +varargs-format-checking.c:8:36: expected long long +varargs-format-checking.c:8:36: got long + * check-error-end + */ diff --git a/validation/varargs-format-position.c b/validation/varargs-format-position.c new file mode 100644 index 0000000..cb57f64 --- /dev/null +++ b/validation/varargs-format-position.c @@ -0,0 +1,31 @@ + +extern void pf(char *msg, ...) __attribute__((format (printf, 1, 2))); + +static void test(void) { + pf("%2$d %u\n", 1U, 1L); + pf("%3$d %2$u\n", 1U, 1); + pf("%1$d %2$d\n", 1L, 1); +} + +/* + * check-name: variadic formatting test position checking + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-position.c:5:29: warning: incorrect type in argument 3 (different types) +varargs-format-position.c:5:29: expected int +varargs-format-position.c:5:29: got long +varargs-format-position.c:5:12: warning: format 3: position: no position specified +varargs-format-position.c:5:29: warning: incorrect type in argument 3 (different types) +varargs-format-position.c:5:29: expected unsigned int +varargs-format-position.c:5:29: got long +varargs-format-position.c:6:12: warning: no argument at position '4' +varargs-format-position.c:6:31: warning: incorrect type in argument 3 (different types) +varargs-format-position.c:6:31: expected unsigned int +varargs-format-position.c:6:31: got int +varargs-format-position.c:7:27: warning: incorrect type in argument 2 (different types) +varargs-format-position.c:7:27: expected int +varargs-format-position.c:7:27: got long + * check-error-end + * + */ diff --git a/validation/varargs-format-prefix.c b/validation/varargs-format-prefix.c new file mode 100644 index 0000000..ff0f0b8 --- /dev/null +++ b/validation/varargs-format-prefix.c @@ -0,0 +1,19 @@ + +extern int __attribute__((format (printf, 1, 2))) variadic(char *msg, ...); + +static int test(void) { + void __attribute__((noderef, address_space(1))) *a; + + variadic("%s\n", a); +} + +/* + * check-name: variadic formatting test prefix based __attribute__ + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-prefix.c:7:26: warning: incorrect type in argument 2 (different address spaces) +varargs-format-prefix.c:7:26: expected char * +varargs-format-prefix.c:7:26: got void [noderef] *a + * check-error-end + */ diff --git a/validation/varargs-format-tests.c b/validation/varargs-format-tests.c new file mode 100644 index 0000000..3154ec1 --- /dev/null +++ b/validation/varargs-format-tests.c @@ -0,0 +1,37 @@ + +extern void pf(char *msg, ...) __attribute__((format (printf, 1, 2))); + +static int test(void) +{ + pf("%*d\n", 5, 10); /* value 10, print width is 5 */ + pf("%2$*1$d\n", 5, 10); /* value 10, print width is 5 */ + pf("%3$*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$-*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$*2$-d\n", 1, 5, 10); /* bad, the "-" shouldn't be before the 'd' */ + pf("%3$ *2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$+*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$0+*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$+0*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$+#*2$d\n", 1, 5, 10); /* ok, skipping the '1' */ + pf("%3$+#*2$.5d\n", 1, 5, 10); /* ok, skipping the '1' */ + + /* go with some precision as well as width strings */ + pf("%2$+*1$.6d\n", 5, 10); /* ok */ + pf("%2$+*1$.*3$d\n", 5, 10, 6); /* ok */ + pf("%2$+*3$.*1$d\n", 6, 10, 5); /* ok */ + pf("%2$+*1$.*d\n", 5, 10, 6); /* not ok */ + + pf("%s", "msg"); + return 0; +} + +/* + * check-name: variadic formatting tests for width/precisions + * check-command: sparse -Wformat $file + * + * check-error-start +varargs-format-tests.c:10:12: warning: cannot evaluate type '%3$*2$-d' +varargs-format-tests.c:10:12: warning: cannot evaluate format string +varargs-format-tests.c:22:12: warning: format 3: position: no position specified + * check-error-end + */ From patchwork Wed Apr 3 15:35:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Dooks X-Patchwork-Id: 10884025 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 97500922 for ; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7F7A128770 for ; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 73D2F28795; Wed, 3 Apr 2019 15:35:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DBE1728770 for ; Wed, 3 Apr 2019 15:35:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726264AbfDCPf5 (ORCPT ); Wed, 3 Apr 2019 11:35:57 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:51735 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725959AbfDCPf5 (ORCPT ); Wed, 3 Apr 2019 11:35:57 -0400 Received: from [167.98.27.226] (helo=rainbowdash.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1hBhve-0005VP-Ov; Wed, 03 Apr 2019 16:35:54 +0100 Received: from ben by rainbowdash.codethink.co.uk with local (Exim 4.92) (envelope-from ) id 1hBhve-00067l-4A; Wed, 03 Apr 2019 16:35:54 +0100 From: Ben Dooks To: linux-sparse@vger.kernel.org Cc: Ben Dooks Subject: [PATCH 6/6] evaluate: correct order of arguments Date: Wed, 3 Apr 2019 16:35:52 +0100 Message-Id: <20190403153552.23461-7-ben.dooks@codethink.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190403153552.23461-1-ben.dooks@codethink.co.uk> References: <20190403153552.23461-1-ben.dooks@codethink.co.uk> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Ben Dooks The original update to evaluate.c did the va-arg checking before the standard checks. This is due to degernate() removing expr->string so save the string before the loop and then use it afterwards. -> to push back into evaluate.c if no other options available. --- evaluate.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/evaluate.c b/evaluate.c index a21bc3f..29c3470 100644 --- a/evaluate.c +++ b/evaluate.c @@ -2685,20 +2685,14 @@ static int parse_format_printf(const char **fmtstring, return 1; } -/* attempt to run through a printf format string and work out the types - * it specifies. The format is parsed from the __attribute__(format()) - * in the parser code which stores the positions of the message and arg - * start in the ctype. - */ -static void evaluate_format_printf(struct symbol *fn, struct expression_list *head) +static const char *get_printf_fmt(struct symbol *fn, struct expression_list *head) { - struct format_state state = { }; struct expression *expr; - const char *fmt_string = NULL; + const char *fmt_string; expr = get_expression_n(head, fn->ctype.printf_msg-1); if (!expr) - return; + return NULL; if (expr->string && expr->string->length) fmt_string = expr->string->data; if (!fmt_string) { @@ -2709,6 +2703,23 @@ static void evaluate_format_printf(struct symbol *fn, struct expression_list *he fmt_string = sym->initializer->string->data; } + return fmt_string; +} + +/* attempt to run through a printf format string and work out the types + * it specifies. The format is parsed from the __attribute__(format()) + * in the parser code which stores the positions of the message and arg + * start in the ctype. + */ +static void evaluate_format_printf(const char *fmt_string, struct symbol *fn, struct expression_list *head) +{ + struct format_state state = { }; + struct expression *expr; + + expr = get_expression_n(head, fn->ctype.printf_msg-1); + if (!expr) + return; + state.expr = expr; state.va_start = fn->ctype.printf_va_start; state.arg_index = fn->ctype.printf_va_start; @@ -2737,14 +2748,16 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head) { struct expression *expr; struct symbol_list *argument_types = fn->arguments; + static const char *fmt_string = NULL; struct symbol *argtype; int i = 1; /* do this first, otherwise the arugment info may get lost or changed - * later on in the evaluation loop. + * later on in the evaluation loop by degenerate() */ if (Wformat && fn->ctype.printf_va_start) - evaluate_format_printf(fn, head); + fmt_string =get_printf_fmt(fn, head); + PREPARE_PTR_LIST(argument_types, argtype); FOR_EACH_PTR (head, expr) { @@ -2782,6 +2795,10 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head) NEXT_PTR_LIST(argtype); } END_FOR_EACH_PTR(expr); FINISH_PTR_LIST(argtype); + + if (Wformat && fn->ctype.printf_va_start) + evaluate_format_printf(fmt_string, fn, head); + return 1; }