From 15aded24139821bd44bf18833815a4bb82443de9 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 29 Jan 2015 17:03:35 +0100 Subject: [PATCH] smatch: Check the kernel's %p format extensions The kernel has a nice printf extension where %p can be followed by certain alphanumerics, causing the pointer argument to be treated and printed specially. Of course, for this to work the pointer must actually point to what is expected, e.g. a struct dentry. For some extensions, there isn't much to check (%pE and %ph can handle pretty much any valid pointer). We also check whether all the alphanumerics make sense (in particular, that %p itself is not followed by a currently-unrecognized character), since a future extension may cause existing code to behave differently. While at it, there are certain other things we can warn about: * Incomplete conversions to pr_*, e.g. pr_warn(KERN_WARNING "blabla"). While many of these can be found with 'git grep', everybody and their dog define their own wrapper macros, so it's sometimes easier to find them after preprocessing. * Format strings containing the name of the caller, while __func__ is also passed (typically via a definition of pr_fmt(fmt) as "%s: " fmt, __func__). * Passing __user pointers to formats which actually dereference the pointer. Note: The list of printf functions is hard-coded (the list has been extracted using a stupid perl script), since sparse currently ignores the format attribute. --- check_kernel_printf.c | 1177 +++++++++++++++++++++++++++++++++++++++++++++++++ check_list.h | 1 + 2 files changed, 1178 insertions(+) create mode 100644 check_kernel_printf.c diff --git a/check_kernel_printf.c b/check_kernel_printf.c new file mode 100644 index 00000000..97a92476 --- /dev/null +++ b/check_kernel_printf.c @@ -0,0 +1,1177 @@ +/* + * Copyright (C) 2015 Rasmus Villemoes. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt + */ + +#include +#include +#include +#include "smatch.h" + +#define spam(args...) do { \ + if (option_spammy) \ + sm_msg(args); \ + } while (0) + +static int my_id; + +/* + * Much of this is taken directly from the kernel (mostly vsprintf.c), + * with a few modifications here and there. + */ + +#define KERN_SOH_ASCII '\001' + +typedef unsigned char u8; +typedef signed short s16; + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */ +#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */ + +enum format_type { + FORMAT_TYPE_NONE, /* Just a string part */ + FORMAT_TYPE_WIDTH, + FORMAT_TYPE_PRECISION, + FORMAT_TYPE_CHAR, + FORMAT_TYPE_STR, + FORMAT_TYPE_PTR, + FORMAT_TYPE_PERCENT_CHAR, + FORMAT_TYPE_INVALID, + FORMAT_TYPE_LONG_LONG, + FORMAT_TYPE_ULONG, + FORMAT_TYPE_LONG, + FORMAT_TYPE_UBYTE, + FORMAT_TYPE_BYTE, + FORMAT_TYPE_USHORT, + FORMAT_TYPE_SHORT, + FORMAT_TYPE_UINT, + FORMAT_TYPE_INT, + FORMAT_TYPE_SIZE_T, + FORMAT_TYPE_PTRDIFF, + FORMAT_TYPE_NRCHARS, /* Reintroduced for this checker */ + FORMAT_TYPE_FLOAT, /* for various floating point formatters */ +}; + +struct printf_spec { + u8 type; /* format_type enum */ + u8 flags; /* flags to number() */ + u8 base; /* number base, 8, 10 or 16 only */ + u8 qualifier; /* number qualifier, one of 'hHlLtzZ' */ + s16 field_width; /* width of output field */ + s16 precision; /* # of digits/chars */ +}; + +static int +skip_atoi(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + + return i; +} + +static int +format_decode(const char *fmt, struct printf_spec *spec) +{ + const char *start = fmt; + + /* we finished early by reading the field width */ + if (spec->type == FORMAT_TYPE_WIDTH) { + if (spec->field_width < 0) { + spec->field_width = -spec->field_width; + spec->flags |= LEFT; + } + spec->type = FORMAT_TYPE_NONE; + goto precision; + } + + /* we finished early by reading the precision */ + if (spec->type == FORMAT_TYPE_PRECISION) { + if (spec->precision < 0) + spec->precision = 0; + + spec->type = FORMAT_TYPE_NONE; + goto qualifier; + } + + /* By default */ + spec->type = FORMAT_TYPE_NONE; + + for (; *fmt ; ++fmt) { + if (*fmt == '%') + break; + } + + /* Return the current non-format string */ + if (fmt != start || !*fmt) + return fmt - start; + + /* Process flags */ + spec->flags = 0; + + while (1) { /* this also skips first '%' */ + bool found = true; + + ++fmt; + + switch (*fmt) { + case '-': spec->flags |= LEFT; break; + case '+': spec->flags |= PLUS; break; + case ' ': spec->flags |= SPACE; break; + case '#': spec->flags |= SPECIAL; break; + case '0': spec->flags |= ZEROPAD; break; + default: found = false; + } + + if (!found) + break; + } + + /* get field width */ + spec->field_width = -1; + + if (isdigit(*fmt)) + spec->field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + spec->type = FORMAT_TYPE_WIDTH; + return ++fmt - start; + } + +precision: + /* get the precision */ + spec->precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + spec->precision = skip_atoi(&fmt); + if (spec->precision < 0) + spec->precision = 0; + } else if (*fmt == '*') { + /* it's the next argument */ + spec->type = FORMAT_TYPE_PRECISION; + return ++fmt - start; + } + } + +qualifier: + /* get the conversion qualifier */ + spec->qualifier = 0; + if (*fmt == 'h' || _tolower(*fmt) == 'l' || + _tolower(*fmt) == 'z' || *fmt == 't') { + spec->qualifier = *fmt++; + if (spec->qualifier == *fmt) { + if (spec->qualifier == 'l') { + spec->qualifier = 'L'; + ++fmt; + } else if (spec->qualifier == 'h') { + spec->qualifier = 'H'; + ++fmt; + } else { + sm_msg("warn: invalid repeated qualifier '%c'", *fmt); + } + } + } + + /* default base */ + spec->base = 10; + switch (*fmt) { + case 'c': + spec->type = FORMAT_TYPE_CHAR; + return ++fmt - start; + + case 's': + spec->type = FORMAT_TYPE_STR; + return ++fmt - start; + + case 'p': + spec->type = FORMAT_TYPE_PTR; + return ++fmt - start; + + case '%': + spec->type = FORMAT_TYPE_PERCENT_CHAR; + return ++fmt - start; + + /* integer number formats - set up the flags and "break" */ + case 'o': + spec->base = 8; + break; + + case 'x': + spec->flags |= SMALL; + + case 'X': + spec->base = 16; + break; + + case 'd': + case 'i': + spec->flags |= SIGN; + case 'u': + break; + + case 'n': + spec->type = FORMAT_TYPE_NRCHARS; + return ++fmt - start; + + case 'a': case 'A': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + spec->type = FORMAT_TYPE_FLOAT; + return ++fmt - start; + + default: + spec->type = FORMAT_TYPE_INVALID; + /* Unlike the kernel code, we 'consume' the invalid + * character so that it can get included in the + * report. After that, we bail out. */ + return ++fmt - start; + } + + if (spec->qualifier == 'L') + spec->type = FORMAT_TYPE_LONG_LONG; + else if (spec->qualifier == 'l') { + if (spec->flags & SIGN) + spec->type = FORMAT_TYPE_LONG; + else + spec->type = FORMAT_TYPE_ULONG; + } else if (_tolower(spec->qualifier) == 'z') { + spec->type = FORMAT_TYPE_SIZE_T; + } else if (spec->qualifier == 't') { + spec->type = FORMAT_TYPE_PTRDIFF; + } else if (spec->qualifier == 'H') { + if (spec->flags & SIGN) + spec->type = FORMAT_TYPE_BYTE; + else + spec->type = FORMAT_TYPE_UBYTE; + } else if (spec->qualifier == 'h') { + if (spec->flags & SIGN) + spec->type = FORMAT_TYPE_SHORT; + else + spec->type = FORMAT_TYPE_USHORT; + } else { + if (spec->flags & SIGN) + spec->type = FORMAT_TYPE_INT; + else + spec->type = FORMAT_TYPE_UINT; + } + + return ++fmt - start; +} + +static int is_struct_tag(struct symbol *type, const char *tag) +{ + return type->type == SYM_STRUCT && type->ident && !strcmp(type->ident->name, tag); +} + +static int is_char_type(struct symbol *type) +{ + return type == &uchar_ctype || type == &char_ctype || type == &schar_ctype; +} + +/* + * I have absolutely no idea if this is how one is supposed to get the + * symbol representing a typedef, but it seems to work. + */ +struct typedef_lookup { + const char *name; + struct symbol *sym; + int failed; +}; + +static struct symbol *_typedef_lookup(const char *name) +{ + struct ident *id; + struct symbol *node; + + id = built_in_ident(name); + if (!id) + return NULL; + node = lookup_symbol(id, NS_TYPEDEF); + if (!node || node->type != SYM_NODE) + return NULL; + return node->ctype.base_type; +} + +static void typedef_lookup(struct typedef_lookup *tl) +{ + if (tl->sym || tl->failed) + return; + tl->sym = _typedef_lookup(tl->name); + if (!tl->sym) { + sm_msg("internal error: could not find typedef '%s'", tl->name); + tl->failed = 1; + } +} + + +static void ip4(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + enum { ENDIAN_BIG, ENDIAN_LITTLE, ENDIAN_HOST } endian = ENDIAN_BIG; + + assert(fmt[0] == 'i' || fmt[0] == 'I'); + assert(fmt[1] == '4'); + + if (isalnum(fmt[2])) { + switch (fmt[2]) { + case 'h': + endian = ENDIAN_HOST; + break; + case 'l': + endian = ENDIAN_LITTLE; + break; + case 'n': + case 'b': + endian = ENDIAN_BIG; + break; + default: + sm_msg("warn: %%p%c4 can only be followed by one of [hnbl], not %c", fmt[0], fmt[2]); + } + if (isalnum(fmt[3])) + sm_msg("warn: %%p%c4 can only be followed by precisely one of [hnbl]", fmt[0]); + } + + + if (type->ctype.modifiers & MOD_NODEREF) + sm_msg("error: passing __user pointer to %%p%c4", fmt[0]); + + /* + * If we have a pointer to char/u8/s8, we expect the caller to + * handle endianness; I don't think there's anything we can + * do. I'd like to check that if we're passed a pointer to a + * __bitwise u32 (most likely a __be32), we should have endian + * == ENDIAN_BIG. But I can't figure out how to get that + * information (it also seems to require ensuring certain + * macros are defined). But struct in_addr certainly consists + * of only a single __be32, so in that case we can do a check. + */ + if (is_char_type(basetype)) + return; + + if (is_struct_tag(basetype, "in_addr") && endian != ENDIAN_BIG) + sm_msg("warn: passing struct in_addr* to %%p%c4%c, is the endianness ok?", fmt[0], fmt[2]); + + /* ... */ +} + +static void ip6(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(fmt[0] == 'i' || fmt[0] == 'I'); + assert(fmt[1] == '6'); + + if (isalnum(fmt[2])) { + if (fmt[2] != 'c') + sm_msg("warn: %%p%c6 can only be followed by c", fmt[0]); + else if (fmt[0] == 'i') + sm_msg("warn: %%pi6 does not allow flag c"); + if (isalnum(fmt[3])) + sm_msg("warn: %%p%c6%c cannot be followed by other alphanumerics", fmt[0], fmt[2]); + } + + if (type->ctype.modifiers & MOD_NODEREF) + sm_msg("error: passing __user pointer to %%p%c6", fmt[0]); +} + +static void ipS(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + const char *f; + + assert(tolower(fmt[0]) == 'i'); + assert(fmt[1] == 'S'); + + for (f = fmt+2; isalnum(*f); ++f) { + /* It's probably too anal checking for duplicate flags. */ + if (!strchr("pfschnbl", *f)) + sm_msg("warn: %%p%cS cannot be followed by %c", fmt[0], *f); + } + + /* + * XXX: Should we also allow passing a pointer to a union, one + * member of which is a struct sockaddr? It may be slightly + * cleaner actually passing &u.raw instead of just &u, though + * the generated code is of course exactly the same. For now, + * we do accept struct sockaddr_in and struct sockaddr_in6, + * since those are easy to handle and rather harmless. + */ + if (!is_struct_tag(basetype, "sockaddr") && + !is_struct_tag(basetype, "sockaddr_in") && + !is_struct_tag(basetype, "sockaddr_in6")) + sm_msg("error: %%p%cS expects argument of type struct sockaddr *, " + "argument %d has type %s", fmt[0], vaidx, type_to_str(type)); +} + +static void hex_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(fmt[0] == 'h'); + if (isalnum(fmt[1])) { + if (!strchr("CDN", fmt[1])) + sm_msg("warn: %%ph cannot be followed by %c", fmt[1]); + if (isalnum(fmt[2])) + sm_msg("warn: %%ph can be followed by at most one of [CDN], and no other alphanumerics"); + } + if (type->ctype.modifiers & MOD_NODEREF) + sm_msg("error: passing __user pointer to %%ph"); +} + +static void escaped_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(fmt[0] == 'E'); + while (isalnum(*++fmt)) { + if (!strchr("achnops", *fmt)) + sm_msg("warn: %%pE can only be followed by a combination of [achnops]"); + } + if (type->ctype.modifiers & MOD_NODEREF) + sm_msg("error: passing __user pointer to %%pE"); +} + +static void resource_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(tolower(fmt[0]) == 'r'); + if (!is_struct_tag(basetype, "resource")) { + sm_msg("error: %%p%c expects argument of type struct resource *, " + "but argument %d has type %s", fmt[0], vaidx, type_to_str(type)); + } + if (isalnum(fmt[1])) + sm_msg("warn: %%p%c cannot be followed by %c", fmt[0], fmt[1]); +} + +static void mac_address_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(tolower(fmt[0]) == 'm'); + if (isalnum(fmt[1])) { + if (!(fmt[1] == 'F' || fmt[1] == 'R')) + sm_msg("warn: %%p%c cannot be followed by %c", fmt[0], fmt[1]); + if (fmt[0] == 'm' && fmt[1] == 'F') + sm_msg("warn: it is pointless to pass flag F to %%pm"); + if (isalnum(fmt[2])) + sm_msg("warn: %%p%c%c cannot be followed by other alphanumeric", fmt[0], fmt[1]); + } + /* Technically, bdaddr_t is a typedef for an anonymous struct, but this still seems to work. */ + if (!is_char_type(basetype) && !is_struct_tag(basetype, "bdaddr_t") && basetype != &void_ctype) { + sm_msg("warn: %%p%c expects argument of type u8 * or bdaddr_t *, argument %d has type %s", + fmt[0], vaidx, type_to_str(type)); + } + if (type->ctype.modifiers & MOD_NODEREF) + sm_msg("error: passing __user pointer to %%p%c", fmt[0]); +} + +static void dentry_file(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + const char *tag; + + assert(tolower(fmt[0]) == 'd'); + tag = fmt[0] == 'd' ? "dentry" : "file"; + + if (isalnum(fmt[1])) { + if (!strchr("234", fmt[1])) + sm_msg("warn: %%p%c can only be followed by one of [234]", fmt[0]); + if (isalnum(fmt[2])) + sm_msg("warn: %%p%c%c cannot be followed by %c", fmt[0], fmt[1], fmt[2]); + } + + if (!is_struct_tag(basetype, tag)) + sm_msg("error: %%p%c expects argument of type struct %s*, argument %d has type %s", + fmt[0], tag, vaidx, type_to_str(type)); +} + +static void va_format(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + assert(fmt[0] == 'V'); + if (isalnum(fmt[1])) + sm_msg("warn: %%pV cannot be followed by any alphanumerics"); + if (!is_struct_tag(basetype, "va_format")) + sm_msg("error: %%pV expects argument of type struct va_format*, argument %d has type %s", vaidx, type_to_str(type)); +} + +static void netdev_feature(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + static struct typedef_lookup netdev = { .name = "netdev_features_t" }; + + assert(fmt[0] == 'N'); + if (fmt[1] != 'F') { + sm_msg("error: %%pN must be followed by F"); + return; + } + if (isalnum(fmt[2])) + sm_msg("warn: %%pNF cannot be followed by %c", fmt[2]); + + typedef_lookup(&netdev); + if (!netdev.sym) + return; + if (basetype != netdev.sym) + sm_msg("error: %%pNF expects argument of type netdev_features_t*, argument %d has type %s", + vaidx, type_to_str(type)); + +} +static void address_val(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx) +{ + static struct typedef_lookup dma = { .name = "dma_addr_t" }; + static struct typedef_lookup phys = { .name = "phys_addr_t" }; + struct typedef_lookup *which = &phys; + const char *suf = ""; + assert(fmt[0] == 'a'); + + if (isalnum(fmt[1])) { + switch (fmt[1]) { + case 'd': + which = &dma; + suf = "d"; + break; + case 'p': + suf = "p"; + break; + default: + sm_msg("error: %%pa can only be followed by one of [dp]"); + } + if (isalnum(fmt[2])) + sm_msg("error: %%pa%c cannot be followed by %c", fmt[1], fmt[2]); + } + + typedef_lookup(which); + if (!which->sym) + return; + if (basetype != which->sym) { + sm_msg("error: %%pa%s expects argument of type %s*, argument %d has type %s", + suf, which->name, vaidx, type_to_str(type)); + } +} + +static void +pointer(const char *fmt, struct expression *arg, int vaidx) +{ + struct symbol *type, *basetype; + + type = get_type(arg); + if (!type) { + sm_msg("warn: could not determine type of argument %d", vaidx); + return; + } + if (!is_ptr_type(type)) { + sm_msg("error: %%p expects pointer argument, but argument %d has type %s", + vaidx, type_to_str(type)); + return; + } + /* Just plain %p, nothing to check. */ + if (!isalnum(*fmt)) + return; + + basetype = get_real_base_type(type); + /* + * Passing a pointer-to-array is harmless, but most likely one + * meant to pass pointer-to-first-element. If basetype is + * array type, we issue a notice and "dereference" the types + * once more. + */ + if (basetype->type == SYM_ARRAY) { + spam("note: passing pointer-to-array; is the address-of redundant?"); + type = basetype; + basetype = get_real_base_type(type); + } + + /* + * We pass both the type and the basetype to the helpers. If, + * for example, the pointer is really a decayed array which is + * passed to %pI4, we might want to check that it is in fact + * an array of four bytes. But most are probably only + * interested in whether the basetype makes sense. Also, the + * pointer may carry some annotation such as __user which + * might be worth checking in the handlers which actually + * dereference the pointer. + */ + + switch (*fmt) { + case 'F': + case 'f': + case 'S': + case 's': + case 'B': + /* Can we do anything sensible? Check that the arg is a function pointer, for example? */ + break; + + case 'R': + case 'r': + resource_string(fmt, type, basetype, vaidx); + break; + case 'M': + case 'm': + mac_address_string(fmt, type, basetype, vaidx); + break; + case 'I': + case 'i': + switch (fmt[1]) { + case '4': + ip4(fmt, type, basetype, vaidx); + break; + case '6': + ip6(fmt, type, basetype, vaidx); + break; + case 'S': + ipS(fmt, type, basetype, vaidx); + break; + default: + sm_msg("warn: %%p%c must be followed by one of [46S]", fmt[0]); + break; + } + break; + /* + * %pE and %ph can handle any valid pointer. We still check + * whether all the subsequent alphanumerics are valid for the + * particular %pX conversion. + */ + case 'E': + escaped_string(fmt, type, basetype, vaidx); + break; + case 'h': + hex_string(fmt, type, basetype, vaidx); + break; + case 'U': /* TODO */ + break; + case 'V': + va_format(fmt, type, basetype, vaidx); + break; + case 'K': /* TODO */ + break; + case 'N': + netdev_feature(fmt, type, basetype, vaidx); + break; + case 'a': + address_val(fmt, type, basetype, vaidx); + break; + case 'D': + case 'd': + dentry_file(fmt, type, basetype, vaidx); + break; + default: + sm_msg("error: unrecognized %%p extension '%c', treated as normal %%p", *fmt); + } +} + +static int +check_format_string(const char *fmt, const char *caller) +{ + const char *f; + + for (f = fmt; *f; ++f) { + unsigned char c = *f; + switch (c) { + case KERN_SOH_ASCII: + /* + * This typically arises from bad conversion + * to pr_*, e.g. pr_warn(KERN_WARNING "something"). + */ + if (f != fmt) + sm_msg("warn: KERN_* level not at start of string"); + /* + * In a very few cases, the level is actually + * computed and passed via %c, as in KERN_SOH + * "%c...". printk explicitly supports + * this. + */ + if (!(('0' <= f[1] && f[1] <= '7') || f[1] == 'd')) + sm_msg("warn: invalid KERN_* level: KERN_SOH_ASCII followed by \\x%02x", (unsigned char)f[1]); + break; + case '\t': + case '\n': + case '\r': + case 0x20 ... 0x7e: + break; + case 0x80 ... 0xff: + sm_msg("warn: format string contains non-ascii character \\x%02x", c); + break; + default: + sm_msg("warn: format string contains unusual character \\x%02x", c); + break; + } + } + + f = strstr(fmt, caller); + if (f && strstr(f+1, caller)) + sm_msg("note: format string contains name of enclosing function '%s' twice", caller); + + return f != NULL; +} + +static int arg_is___func__(struct expression *arg) +{ + if (arg->type != EXPR_SYMBOL) + return 0; + return !strcmp(arg->symbol_name->name, "__func__") || + !strcmp(arg->symbol_name->name, "__FUNCTION__") || + !strcmp(arg->symbol_name->name, "__PRETTY_FUNCTION__"); +} +static int arg_contains_caller(struct expression *arg, const char *caller) +{ + if (arg->type != EXPR_STRING) + return 0; + return strstr(arg->string->data, caller) != NULL; +} + + +static void +check_printf_call(const char *name, struct expression *expr, void *info) +{ + /* + * Note: attribute(printf) uses 1-based indexing, but + * get_argument_from_call_expr() uses 0-based indexing. + */ + int fmtidx = (PTR_INT(info) & 0xff) - 1; + int vaidx = ((PTR_INT(info) >> 8) & 0xff) - 1; + struct printf_spec spec = {0}; + struct expression *fmtexpr; + const char *fmt; + int caller_in_fmt; + const char *caller = get_function(); + + /* + * Calling a v*printf function with a literal format arg is + * extremely rare, so we don't bother doing the only checking + * we could do, namely checking that the format string is + * valid. + */ + if (!vaidx) + return; + + /* + * For the things we use the name of the calling function for, + * it is more appropriate to skip a potential SyS_ prefix; the + * same goes for leading underscores. + */ + if (!strncmp(caller, "SyS_", 4)) + caller += 4; + while (*caller == '_') + ++caller; + + /* If the format is not a string literal, there's nothing we can do. */ + fmtexpr = get_argument_from_call_expr(expr->args, fmtidx); + if (!fmtexpr) { + sm_msg("error: call of %s with no format argument", name); + return; + } + if (fmtexpr->type != EXPR_STRING) { + /* + * These generate far too much noise, and we can't do + * anything about it anyway. Moreover, gcc can easily + * be asked to provide the same info if one really + * wanted it. + */ + if (0) + spam("warn: call of %s with non-constant format argument", name); + /* + * XXX: We could make a special case if the expression + * is EXPR_CONDITIONAL and either branch is a string, + * by calling ourselves recursively with an extra + * piece of information in info. + */ + if (fmtexpr->type == EXPR_CONDITIONAL) + sm_msg("format string given by conditional"); + return; + } + fmt = fmtexpr->string->data; + caller_in_fmt = check_format_string(fmt, caller); + + while (*fmt) { + const char *old_fmt = fmt; + int read = format_decode(fmt, &spec); + struct expression *arg; + + fmt += read; + if (spec.type == FORMAT_TYPE_NONE || + spec.type == FORMAT_TYPE_PERCENT_CHAR) + continue; + + /* + * vaidx is currently the correct 0-based index for + * get_argument_from_call_expr. We post-increment it + * here so that it is the correct 1-based index for + * all the handlers below. This of course requires + * that we handle all FORMAT_TYPE_* things not taking + * an argument above. + */ + arg = get_argument_from_call_expr(expr->args, vaidx++); + + switch (spec.type) { + /* case FORMAT_TYPE_NONE: */ + /* case FORMAT_TYPE_PERCENT_CHAR: */ + /* break; */ + + case FORMAT_TYPE_INVALID: + sm_msg("error: format specifier '%.*s' invalid", read, old_fmt); + return; + + case FORMAT_TYPE_FLOAT: + sm_msg("error: no floats in the kernel; invalid format specifier '%.*s'", read, old_fmt); + return; + + case FORMAT_TYPE_NRCHARS: + sm_msg("error: %%n not supported in kernel"); + return; + + case FORMAT_TYPE_WIDTH: + case FORMAT_TYPE_PRECISION: + /* check int argument */ + break; + + case FORMAT_TYPE_STR: + /* + * If the format string already contains the + * function name, it probably doesn't make + * sense to pass __func__ as well (or rather + * vice versa: If pr_fmt(fmt) has been defined + * to '"%s: " fmt, __func__', it doesn't make + * sense to use a format string containing the + * function name). + * + * This produces a lot of hits. They are not + * false positives, but it is easier to handle + * the things which don't occur that often + * first, so we use spam(). + */ + if (spec.qualifier) + sm_msg("warn: qualifier '%c' ignored for %%s specifier", spec.qualifier); + + if (caller_in_fmt) { + if (arg_is___func__(arg)) + spam("warn: passing __func__ while the format string already contains the name of the function %s", + caller); + else if (arg_contains_caller(arg, caller)) + sm_msg("warn: passing string constant '%s' containing '%s' which is already part of the format string", + arg->string->data, caller); + } + break; + + case FORMAT_TYPE_PTR: + /* This is the most important part: Checking %p extensions. */ + pointer(fmt, arg, vaidx); + while (isalnum(*fmt)) + fmt++; + break; + + case FORMAT_TYPE_CHAR: + if (spec.qualifier) + sm_msg("warn: qualifier '%c' ignored for %%s specifier", spec.qualifier); + + case FORMAT_TYPE_UBYTE: + case FORMAT_TYPE_BYTE: + case FORMAT_TYPE_USHORT: + case FORMAT_TYPE_SHORT: + case FORMAT_TYPE_INT: + /* argument should have integer type of width <= sizeof(int) */ + break; + + case FORMAT_TYPE_UINT: + case FORMAT_TYPE_LONG: + case FORMAT_TYPE_ULONG: + case FORMAT_TYPE_LONG_LONG: + case FORMAT_TYPE_PTRDIFF: + case FORMAT_TYPE_SIZE_T: + break; + } + + + } + + if (get_argument_from_call_expr(expr->args, vaidx)) + sm_msg("warn: excess argument passed to %s", name); +} + + +void check_kernel_printf(int id) +{ + if (option_project != PROJ_KERNEL) + return; + + my_id = id; + +#define printf_hook(func, fmt, first_to_check) \ + add_function_hook(#func, check_printf_call, INT_PTR(fmt + (first_to_check << 8))) + + /* Extracted using stupid perl script. */ + +#if 0 + printf_hook(srm_printk, 1, 2); /* arch/alpha/include/asm/console.h */ + printf_hook(die_if_kernel, 1, 2); /* arch/frv/include/asm/bug.h */ + printf_hook(ia64_mca_printk, 1, 2); /* arch/ia64/include/asm/mca.h */ + printf_hook(nfprint, 1, 2); /* arch/m68k/include/asm/natfeat.h */ + printf_hook(gdbstub_printk, 1, 2); /* arch/mn10300/include/asm/gdb-stub.h */ + printf_hook(DBG, 1, 2); /* arch/powerpc/boot/ps3.c */ + printf_hook(printf, 1, 2); /* arch/powerpc/boot/stdio.h */ + printf_hook(udbg_printf, 1, 2); /* arch/powerpc/include/asm/udbg.h */ + printf_hook(__debug_sprintf_event, 3, 4); /* arch/s390/include/asm/debug.h */ + printf_hook(__debug_sprintf_exception, 3, 4); /* arch/s390/include/asm/debug.h */ + printf_hook(prom_printf, 1, 2); /* arch/sparc/include/asm/oplib_32.h */ + + printf_hook(fail, 1, 2); /* arch/x86/vdso/vdso2c.c */ +#endif + + printf_hook(_ldm_printk, 3, 4); /* block/partitions/ldm.c */ + printf_hook(rbd_warn, 2, 3); /* drivers/block/rbd.c */ + printf_hook(fw_err, 2, 3); /* drivers/firewire/core.h */ + printf_hook(fw_notice, 2, 3); /* drivers/firewire/core.h */ + printf_hook(i915_error_printf, 2, 3); /* drivers/gpu/drm/i915/i915_drv.h */ + printf_hook(i915_handle_error, 3, 4); /* drivers/gpu/drm/i915/i915_drv.h */ + printf_hook(nv_printk_, 3, 4); /* drivers/gpu/drm/nouveau/core/include/core/printk.h */ + printf_hook(host1x_debug_output, 2, 3); /* drivers/gpu/host1x/debug.h */ + printf_hook(callc_debug, 2, 3); /* drivers/isdn/hisax/callc.c */ + printf_hook(link_debug, 3, 4); /* drivers/isdn/hisax/callc.c */ + printf_hook(HiSax_putstatus, 3, 4); /* drivers/isdn/hisax/hisax.h */ + printf_hook(VHiSax_putstatus, 3, 0); /* drivers/isdn/hisax/hisax.h */ + printf_hook(debugl1, 2, 3); /* drivers/isdn/hisax/isdnl1.h */ + printf_hook(l3m_debug, 2, 3); /* drivers/isdn/hisax/isdnl3.c */ + printf_hook(dout_debug, 2, 3); /* drivers/isdn/hisax/st5481_d.c */ + printf_hook(l1m_debug, 2, 3); /* drivers/isdn/hisax/st5481_d.c */ + printf_hook(bch_cache_set_error, 2, 3); /* drivers/md/bcache/bcache.h */ + printf_hook(_tda_printk, 4, 5); /* drivers/media/tuners/tda18271-priv.h */ + printf_hook(i40evf_debug_d, 3, 4); /* drivers/net/ethernet/intel/i40evf/i40e_osdep.h */ + printf_hook(en_print, 3, 4); /* drivers/net/ethernet/mellanox/mlx4/mlx4_en.h */ + printf_hook(_ath_dbg, 3, 4); /* drivers/net/wireless/ath/ath.h */ + printf_hook(ath_printk, 3, 4); /* drivers/net/wireless/ath/ath.h */ + printf_hook(ath10k_dbg, 3, 4); /* drivers/net/wireless/ath/ath10k/debug.h */ + printf_hook(ath10k_err, 2, 3); /* drivers/net/wireless/ath/ath10k/debug.h */ + printf_hook(ath10k_info, 2, 3); /* drivers/net/wireless/ath/ath10k/debug.h */ + printf_hook(ath10k_warn, 2, 3); /* drivers/net/wireless/ath/ath10k/debug.h */ + printf_hook(_ath5k_printk, 3, 4); /* drivers/net/wireless/ath/ath5k/ath5k.h */ + printf_hook(ATH5K_DBG, 3, 4); /* drivers/net/wireless/ath/ath5k/debug.h */ + printf_hook(ATH5K_DBG_UNLIMIT, 3, 4); /* drivers/net/wireless/ath/ath5k/debug.h */ + printf_hook(ath6kl_printk, 2, 3); /* drivers/net/wireless/ath/ath6kl/common.h */ + printf_hook(ath6kl_err, 1, 2); /* drivers/net/wireless/ath/ath6kl/debug.h */ + printf_hook(ath6kl_info, 1, 2); /* drivers/net/wireless/ath/ath6kl/debug.h */ + printf_hook(ath6kl_warn, 1, 2); /* drivers/net/wireless/ath/ath6kl/debug.h */ + printf_hook(wil_dbg_trace, 2, 3); /* drivers/net/wireless/ath/wil6210/wil6210.h */ + printf_hook(wil_err, 2, 3); /* drivers/net/wireless/ath/wil6210/wil6210.h */ + printf_hook(wil_err_ratelimited, 2, 3); /* drivers/net/wireless/ath/wil6210/wil6210.h */ + printf_hook(wil_info, 2, 3); /* drivers/net/wireless/ath/wil6210/wil6210.h */ + printf_hook(b43dbg, 2, 3); /* drivers/net/wireless/b43/b43.h */ + printf_hook(b43err, 2, 3); /* drivers/net/wireless/b43/b43.h */ + printf_hook(b43info, 2, 3); /* drivers/net/wireless/b43/b43.h */ + printf_hook(b43warn, 2, 3); /* drivers/net/wireless/b43/b43.h */ + printf_hook(b43legacydbg, 2, 3); /* drivers/net/wireless/b43legacy/b43legacy.h */ + printf_hook(b43legacyerr, 2, 3); /* drivers/net/wireless/b43legacy/b43legacy.h */ + printf_hook(b43legacyinfo, 2, 3); /* drivers/net/wireless/b43legacy/b43legacy.h */ + printf_hook(b43legacywarn, 2, 3); /* drivers/net/wireless/b43legacy/b43legacy.h */ + printf_hook(__brcmf_dbg, 3, 4); /* drivers/net/wireless/brcm80211/brcmfmac/debug.h */ + printf_hook(__brcmf_err, 2, 3); /* drivers/net/wireless/brcm80211/brcmfmac/debug.h */ + printf_hook(__brcms_crit, 2, 3); /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */ + printf_hook(__brcms_dbg, 4, 5); /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */ + printf_hook(__brcms_err, 2, 3); /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */ + printf_hook(__brcms_info, 2, 3); /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */ + printf_hook(__brcms_warn, 2, 3); /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */ + printf_hook(brcmu_dbg_hex_dump, 3, 4); /* drivers/net/wireless/brcm80211/include/brcmu_utils.h */ + printf_hook(__iwl_crit, 2, 3); /* drivers/net/wireless/iwlwifi/iwl-debug.h */ + printf_hook(__iwl_dbg, 5, 6); /* drivers/net/wireless/iwlwifi/iwl-debug.h */ + printf_hook(__iwl_err, 4, 5); /* drivers/net/wireless/iwlwifi/iwl-debug.h */ + printf_hook(__iwl_info, 2, 3); /* drivers/net/wireless/iwlwifi/iwl-debug.h */ + printf_hook(__iwl_warn, 2, 3); /* drivers/net/wireless/iwlwifi/iwl-debug.h */ + printf_hook(rsi_dbg, 2, 3); /* drivers/net/wireless/rsi/rsi_main.h */ + printf_hook(RTPRINT, 4, 5); /* drivers/net/wireless/rtlwifi/debug.h */ + printf_hook(RT_ASSERT, 2, 3); /* drivers/net/wireless/rtlwifi/debug.h */ + printf_hook(RT_TRACE, 4, 5); /* drivers/net/wireless/rtlwifi/debug.h */ + printf_hook(__of_node_dup, 2, 3); /* drivers/of/of_private.h */ + printf_hook(BNX2FC_HBA_DBG, 2, 3); /* drivers/scsi/bnx2fc/bnx2fc_debug.h */ + printf_hook(BNX2FC_IO_DBG, 2, 3); /* drivers/scsi/bnx2fc/bnx2fc_debug.h */ + printf_hook(BNX2FC_TGT_DBG, 2, 3); /* drivers/scsi/bnx2fc/bnx2fc_debug.h */ + printf_hook(ql_dbg, 4, 5); /* drivers/scsi/qla2xxx/qla_dbg.h */ + printf_hook(ql_dbg_pci, 4, 5); /* drivers/scsi/qla2xxx/qla_dbg.h */ + printf_hook(ql_log, 4, 5); /* drivers/scsi/qla2xxx/qla_dbg.h */ + printf_hook(ql_log_pci, 4, 5); /* drivers/scsi/qla2xxx/qla_dbg.h */ + printf_hook(libcfs_debug_msg, 2, 3); /* drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h */ + printf_hook(libcfs_debug_vmsg2, 4, 5); /* drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h */ + printf_hook(_ldlm_lock_debug, 3, 4); /* drivers/staging/lustre/lustre/include/lustre_dlm.h */ + printf_hook(_debug_req, 3, 4); /* drivers/staging/lustre/lustre/include/lustre_net.h */ + printf_hook(iscsi_change_param_sprintf, 2, 3); /* drivers/target/iscsi/iscsi_target_login.c */ + printf_hook(dbg, 1, 2); /* drivers/tty/serial/samsung.c */ + printf_hook(_usb_stor_dbg, 2, 3); /* drivers/usb/storage/debug.h */ + printf_hook(usb_stor_dbg, 2, 3); /* drivers/usb/storage/debug.h */ + printf_hook(vringh_bad, 1, 2); /* drivers/vhost/vringh.c */ + printf_hook(__adfs_error, 3, 4); /* fs/adfs/adfs.h */ + printf_hook(affs_error, 3, 4); /* fs/affs/affs.h */ + printf_hook(affs_warning, 3, 4); /* fs/affs/affs.h */ + printf_hook(befs_debug, 2, 3); /* fs/befs/befs.h */ + printf_hook(befs_error, 2, 3); /* fs/befs/befs.h */ + printf_hook(befs_warning, 2, 3); /* fs/befs/befs.h */ + printf_hook(__btrfs_panic, 5, 6); /* fs/btrfs/ctree.h */ + printf_hook(__btrfs_std_error, 5, 6); /* fs/btrfs/ctree.h */ + printf_hook(btrfs_printk, 2, 3); /* fs/btrfs/ctree.h */ + printf_hook(cifs_vfs_err, 1, 2); /* fs/cifs/cifs_debug.h */ + printf_hook(__ecryptfs_printk, 1, 2); /* fs/ecryptfs/ecryptfs_kernel.h */ + printf_hook(ext2_error, 3, 4); /* fs/ext2/ext2.h */ + printf_hook(ext2_msg, 3, 4); /* fs/ext2/ext2.h */ + printf_hook(ext3_abort, 3, 4); /* fs/ext3/ext3.h */ + printf_hook(ext3_error, 3, 4); /* fs/ext3/ext3.h */ + printf_hook(ext3_msg, 3, 4); /* fs/ext3/ext3.h */ + printf_hook(ext3_warning, 3, 4); /* fs/ext3/ext3.h */ + printf_hook(__ext4_abort, 4, 5); /* fs/ext4/ext4.h */ + printf_hook(__ext4_error, 4, 5); /* fs/ext4/ext4.h */ + printf_hook(__ext4_error_file, 5, 6); /* fs/ext4/ext4.h */ + printf_hook(__ext4_error_inode, 5, 6); /* fs/ext4/ext4.h */ + printf_hook(__ext4_grp_locked_error, 7, 8); /* fs/ext4/ext4.h */ + printf_hook(__ext4_msg, 3, 4); /* fs/ext4/ext4.h */ + printf_hook(__ext4_warning, 4, 5); /* fs/ext4/ext4.h */ + printf_hook(f2fs_msg, 3, 4); /* fs/f2fs/f2fs.h */ + printf_hook(__fat_fs_error, 3, 4); /* fs/fat/fat.h */ + printf_hook(fat_msg, 3, 4); /* fs/fat/fat.h */ + printf_hook(gfs2_print_dbg, 2, 3); /* fs/gfs2/glock.h */ + printf_hook(gfs2_lm_withdraw, 2, 3); /* fs/gfs2/util.h */ + printf_hook(hpfs_error, 2, 3); /* fs/hpfs/hpfs_fn.h */ + printf_hook(jfs_error, 2, 3); /* fs/jfs/jfs_superblock.h */ + printf_hook(nilfs_error, 3, 4); /* fs/nilfs2/nilfs.h */ + printf_hook(nilfs_warning, 3, 4); /* fs/nilfs2/nilfs.h */ + printf_hook(__ntfs_debug, 4, 5); /* fs/ntfs/debug.h */ + printf_hook(__ntfs_error, 3, 4); /* fs/ntfs/debug.h */ + printf_hook(__ntfs_warning, 3, 4); /* fs/ntfs/debug.h */ + printf_hook(__ocfs2_abort, 3, 4); /* fs/ocfs2/super.h */ + printf_hook(__ocfs2_error, 3, 4); /* fs/ocfs2/super.h */ + printf_hook(_udf_err, 3, 4); /* fs/udf/udfdecl.h */ + printf_hook(_udf_warn, 3, 4); /* fs/udf/udfdecl.h */ + printf_hook(ufs_error, 3, 4); /* fs/ufs/ufs.h */ + printf_hook(ufs_panic, 3, 4); /* fs/ufs/ufs.h */ + printf_hook(ufs_warning, 3, 4); /* fs/ufs/ufs.h */ + printf_hook(xfs_alert, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_alert_tag, 3, 4); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_crit, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_debug, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_emerg, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_err, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_info, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_notice, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(xfs_warn, 2, 3); /* fs/xfs/xfs_message.h */ + printf_hook(warn_slowpath_fmt, 3, 4); /* include/asm-generic/bug.h */ + printf_hook(warn_slowpath_fmt_taint, 4, 5); /* include/asm-generic/bug.h */ + printf_hook(drm_err, 1, 2); /* include/drm/drmP.h */ + printf_hook(drm_ut_debug_printk, 2, 3); /* include/drm/drmP.h */ + printf_hook(__acpi_handle_debug, 3, 4); /* include/linux/acpi.h */ + printf_hook(acpi_handle_printk, 3, 4); /* include/linux/acpi.h */ + printf_hook(audit_log, 4, 5); /* include/linux/audit.h */ + printf_hook(audit_log_format, 2, 3); /* include/linux/audit.h */ + printf_hook(bdi_register, 3, 4); /* include/linux/backing-dev.h */ + printf_hook(__trace_note_message, 2, 3); /* include/linux/blktrace_api.h */ + printf_hook(_dev_info, 2, 3); /* include/linux/device.h */ + printf_hook(dev_alert, 2, 3); /* include/linux/device.h */ + printf_hook(dev_crit, 2, 3); /* include/linux/device.h */ + printf_hook(dev_emerg, 2, 3); /* include/linux/device.h */ + printf_hook(dev_err, 2, 3); /* include/linux/device.h */ + printf_hook(dev_notice, 2, 3); /* include/linux/device.h */ + printf_hook(dev_printk, 3, 4); /* include/linux/device.h */ + printf_hook(dev_printk_emit, 3, 4); /* include/linux/device.h */ + printf_hook(dev_set_name, 2, 3); /* include/linux/device.h */ + printf_hook(dev_vprintk_emit, 3, 0); /* include/linux/device.h */ + printf_hook(dev_warn, 2, 3); /* include/linux/device.h */ + printf_hook(device_create, 5, 6); /* include/linux/device.h */ + printf_hook(device_create_with_groups, 6, 7); /* include/linux/device.h */ + printf_hook(devm_kasprintf, 3, 4); /* include/linux/device.h */ + printf_hook(__dynamic_dev_dbg, 3, 4); /* include/linux/dynamic_debug.h */ + printf_hook(__dynamic_netdev_dbg, 3, 4); /* include/linux/dynamic_debug.h */ + printf_hook(__dynamic_pr_debug, 2, 3); /* include/linux/dynamic_debug.h */ + printf_hook(__simple_attr_check_format, 1, 2); /* include/linux/fs.h */ + printf_hook(fscache_init_cache, 3, 4); /* include/linux/fscache-cache.h */ + printf_hook(gameport_set_phys, 2, 3); /* include/linux/gameport.h */ + printf_hook(iio_trigger_alloc, 1, 2); /* include/linux/iio/trigger.h */ + printf_hook(__check_printsym_format, 1, 2); /* include/linux/kallsyms.h */ + printf_hook(kdb_printf, 1, 2); /* include/linux/kdb.h */ + printf_hook(vkdb_printf, 1, 0); /* include/linux/kdb.h */ + printf_hook(____trace_printk_check_format, 1, 2); /* include/linux/kernel.h */ + printf_hook(__trace_bprintk, 2, 3); /* include/linux/kernel.h */ + printf_hook(__trace_printk, 2, 3); /* include/linux/kernel.h */ + printf_hook(kasprintf, 2, 3); /* include/linux/kernel.h */ + printf_hook(panic, 1, 2); /* include/linux/kernel.h */ + printf_hook(scnprintf, 3, 4); /* include/linux/kernel.h */ + printf_hook(snprintf, 3, 4); /* include/linux/kernel.h */ + printf_hook(sprintf, 2, 3); /* include/linux/kernel.h */ + printf_hook(trace_printk, 1, 2); /* include/linux/kernel.h */ + printf_hook(vscnprintf, 3, 0); /* include/linux/kernel.h */ + printf_hook(vsnprintf, 3, 0); /* include/linux/kernel.h */ + printf_hook(vsprintf, 2, 0); /* include/linux/kernel.h */ + printf_hook(vmcoreinfo_append_str, 1, 2); /* include/linux/kexec.h */ + printf_hook(__request_module, 2, 3); /* include/linux/kmod.h */ + printf_hook(add_uevent_var, 2, 3); /* include/linux/kobject.h */ + printf_hook(kobject_add, 3, 4); /* include/linux/kobject.h */ + printf_hook(kobject_init_and_add, 4, 5); /* include/linux/kobject.h */ + printf_hook(kobject_set_name, 2, 3); /* include/linux/kobject.h */ + printf_hook(kthread_create_on_node, 4, 5); /* include/linux/kthread.h */ + printf_hook(__ata_ehi_push_desc, 2, 3); /* include/linux/libata.h */ + printf_hook(ata_dev_printk, 3, 4); /* include/linux/libata.h */ + printf_hook(ata_ehi_push_desc, 2, 3); /* include/linux/libata.h */ + printf_hook(ata_link_printk, 3, 4); /* include/linux/libata.h */ + printf_hook(ata_port_desc, 2, 3); /* include/linux/libata.h */ + printf_hook(ata_port_printk, 3, 4); /* include/linux/libata.h */ + printf_hook(warn_alloc_failed, 3, 4); /* include/linux/mm.h */ + printf_hook(mmiotrace_printk, 1, 2); /* include/linux/mmiotrace.h */ + printf_hook(netdev_alert, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_crit, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_emerg, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_err, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_info, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_notice, 2, 3); /* include/linux/netdevice.h */ + printf_hook(netdev_printk, 3, 4); /* include/linux/netdevice.h */ + printf_hook(netdev_warn, 2, 3); /* include/linux/netdevice.h */ + printf_hook(early_printk, 1, 2); /* include/linux/printk.h */ + printf_hook(no_printk, 1, 2); /* include/linux/printk.h */ + printf_hook(printk, 1, 2); /* include/linux/printk.h */ + printf_hook(printk_deferred, 1, 2); /* include/linux/printk.h */ + printf_hook(printk_emit, 5, 6); /* include/linux/printk.h */ + printf_hook(vprintk, 1, 0); /* include/linux/printk.h */ + printf_hook(vprintk_emit, 5, 0); /* include/linux/printk.h */ + printf_hook(__quota_error, 3, 4); /* include/linux/quotaops.h */ + printf_hook(seq_buf_printf, 2, 3); /* include/linux/seq_buf.h */ + printf_hook(seq_buf_vprintf, 2, 0); /* include/linux/seq_buf.h */ + printf_hook(seq_printf, 2, 3); /* include/linux/seq_file.h */ + printf_hook(seq_vprintf, 2, 0); /* include/linux/seq_file.h */ + printf_hook(bprintf, 3, 4); /* include/linux/string.h */ + printf_hook(trace_seq_printf, 2, 3); /* include/linux/trace_seq.h */ + printf_hook(trace_seq_vprintf, 2, 0); /* include/linux/trace_seq.h */ + printf_hook(__alloc_workqueue_key, 1, 6); /* include/linux/workqueue.h */ + printf_hook(set_worker_desc, 1, 2); /* include/linux/workqueue.h */ + printf_hook(_p9_debug, 3, 4); /* include/net/9p/9p.h */ + printf_hook(bt_err, 1, 2); /* include/net/bluetooth/bluetooth.h */ + printf_hook(bt_info, 1, 2); /* include/net/bluetooth/bluetooth.h */ + printf_hook(nf_ct_helper_log, 3, 4); /* include/net/netfilter/nf_conntrack_helper.h */ + printf_hook(nf_log_buf_add, 2, 3); /* include/net/netfilter/nf_log.h */ + printf_hook(nf_log_packet, 8, 9); /* include/net/netfilter/nf_log.h */ + printf_hook(SOCK_DEBUG, 2, 3); /* include/net/sock.h */ + printf_hook(__snd_printk, 4, 5); /* include/sound/core.h */ + printf_hook(_snd_printd, 2, 3); /* include/sound/core.h */ + printf_hook(snd_printd, 1, 2); /* include/sound/core.h */ + printf_hook(snd_printdd, 1, 2); /* include/sound/core.h */ + printf_hook(snd_iprintf, 2, 3); /* include/sound/info.h */ + printf_hook(snd_seq_create_kernel_client, 3, 4); /* include/sound/seq_kernel.h */ + printf_hook(xen_raw_printk, 1, 2); /* include/xen/hvc-console.h */ + printf_hook(xenbus_dev_error, 3, 4); /* include/xen/xenbus.h */ + printf_hook(xenbus_dev_fatal, 3, 4); /* include/xen/xenbus.h */ + printf_hook(xenbus_printf, 4, 5); /* include/xen/xenbus.h */ + printf_hook(xenbus_watch_pathfmt, 4, 5); /* include/xen/xenbus.h */ + printf_hook(batadv_fdebug_log, 2, 3); /* net/batman-adv/debugfs.c */ + printf_hook(_batadv_dbg, 4, 5); /* net/batman-adv/main.h */ + printf_hook(batadv_debug_log, 2, 3); /* net/batman-adv/main.h */ + printf_hook(__sdata_dbg, 2, 3); /* net/mac80211/debug.h */ + printf_hook(__sdata_err, 1, 2); /* net/mac80211/debug.h */ + printf_hook(__sdata_info, 1, 2); /* net/mac80211/debug.h */ + printf_hook(__wiphy_dbg, 3, 4); /* net/mac80211/debug.h */ + printf_hook(mac80211_format_buffer, 4, 5); /* net/mac80211/debugfs.h */ + printf_hook(__rds_conn_error, 2, 3); /* net/rds/rds.h */ + printf_hook(rdsdebug, 1, 2); /* net/rds/rds.h */ + printf_hook(printl, 1, 2); /* net/sctp/probe.c */ + printf_hook(svc_printk, 2, 3); /* net/sunrpc/svc.c */ + printf_hook(tomoyo_io_printf, 2, 3); /* security/tomoyo/common.c */ + printf_hook(tomoyo_supervisor, 2, 3); /* security/tomoyo/common.h */ + printf_hook(tomoyo_write_log, 2, 3); /* security/tomoyo/common.h */ + printf_hook(cmp_error, 2, 3); /* sound/firewire/cmp.c */ +} diff --git a/check_list.h b/check_list.h index 528a3c3e..69558441 100644 --- a/check_list.h +++ b/check_list.h @@ -118,6 +118,7 @@ CK(check_buffer_too_small_for_struct) /* CK(register_template) */ /* kernel specific */ +CK(check_kernel_printf) CK(check_locking) CK(check_puts_argument) CK(check_err_ptr) -- 2.11.4.GIT