From 69e38336b1b16300300eccc81b432b3437d90f0b Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 11 Feb 2015 22:20:38 +0100 Subject: [PATCH] check_kernel_printf.c: Allow const char[] format arguments If the format argument is a const char[] with a literal initializer we might as well do the checking also in that case (gcc also does that). Similarly if the format argument is const char* const (gcc apparently does not do that). This removes around 100 messages from an allmodconfig, and of course provides coverage of those same call sites. In most cases, however, I think it would be more appropriate to insert the format string directly where it is used. The linker merges identical string constants, so there's nothing saved by using an array variable (on the contrary, this wastes a little space on the variable name itself), and it is easier for a human to immediately verify that everything is ok, instead of having to search back through the file to see how foo is defined. --- check_kernel_printf.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/check_kernel_printf.c b/check_kernel_printf.c index 212aea40..2408c5a0 100644 --- a/check_kernel_printf.c +++ b/check_kernel_printf.c @@ -731,6 +731,37 @@ static int arg_contains_caller(struct expression *arg, const char *caller) return strstr(arg->string->data, caller) != NULL; } +static int is_array_of_const_char(struct symbol *sym) +{ + struct symbol *base = sym->ctype.base_type; + if (base->type != SYM_ARRAY) + return 0; + if (!(base->ctype.modifiers & MOD_CONST)) + return 0; + if (!is_char_type(base->ctype.base_type)) { + spam("weird: format argument is array of const %s", type_to_str(base->ctype.base_type)); + return 0; + } + return 1; +} + +static int is_const_pointer_to_const_char(struct symbol *sym) +{ + struct symbol *base = sym->ctype.base_type; + if (!(sym->ctype.modifiers & MOD_CONST)) + return 0; + if (base->type != SYM_PTR) + return 0; + if (!(base->ctype.modifiers & MOD_CONST)) + return 0; + if (!is_char_type(base->ctype.base_type)) { + spam("weird: format argument is pointer to const %s", type_to_str(base->ctype.base_type)); + return 0; + } + return 1; +} + + static void do_check_printf_call(const char *caller, const char *name, struct expression *callexpr, struct expression *fmtexpr, int vaidx) { @@ -744,11 +775,32 @@ do_check_printf_call(const char *caller, const char *name, struct expression *ca do_check_printf_call(caller, name, callexpr, fmtexpr->cond_false, vaidx); return; } - /* XXX: Handle the case of a static const char[] argument. */ + if (fmtexpr->type == EXPR_SYMBOL) { + /* + * If the symbol has an initializer, we can handle + * + * const char foo[] = "abc"; and + * const char * const foo = "abc"; + * + * We simply replace fmtexpr with the initializer + * expression. If foo is not one of the above, or if + * the initializer expression is somehow not a string + * literal, fmtexpr->type != EXPR_STRING will trigger + * below and we'll spam+return. + */ + struct symbol *sym = fmtexpr->symbol; + if (sym->initializer && + (is_array_of_const_char(sym) || + is_const_pointer_to_const_char(sym))) { + fmtexpr = strip_parens(sym->initializer); + } + } + if (fmtexpr->type != EXPR_STRING) { /* - * Since we're now handling the case of ?:, we don't - * get as much noise. It's still spammy, though. + * Since we're now handling both ?: and static const + * char[] arguments, we don't get as much noise. It's + * still spammy, though. */ spam("warn: call of '%s' with non-constant format argument", name); return; -- 2.11.4.GIT