#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include "smatch.h"
#include "smatch_slist.h"
#define spam(args...) do { \
if (option_spammy) \
sm_msg(args); \
} while (0)
static int my_id;
#define KERN_SOH_ASCII '\001'
typedef unsigned char u8;
typedef signed short s16;
#define SIGN 1
#define LEFT 2
#define PLUS 4
#define SPACE 8
#define ZEROPAD 16
#define SMALL 32
#define SPECIAL 64
enum format_type {
FORMAT_TYPE_NONE,
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,
FORMAT_TYPE_FLOAT,
};
struct printf_spec {
unsigned int type:8;
signed int field_width:24;
unsigned int flags:8;
unsigned int base:8;
signed int precision:16;
} __packed;
#define FIELD_WIDTH_MAX ((1 << 23) - 1)
#define PRECISION_MAX ((1 << 15) - 1)
extern char __check_printf_spec[1-2*(sizeof(struct printf_spec) != 8)];
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;
char qualifier;
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;
}
if (spec->type == FORMAT_TYPE_PRECISION) {
if (spec->precision < 0)
spec->precision = 0;
spec->type = FORMAT_TYPE_NONE;
goto qualifier;
}
spec->type = FORMAT_TYPE_NONE;
for (; *fmt ; ++fmt) {
if (*fmt == '%')
break;
}
if (fmt != start || !*fmt)
return fmt - start;
spec->flags = 0;
while (1) {
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;
}
spec->field_width = -1;
if (isdigit(*fmt))
spec->field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
spec->type = FORMAT_TYPE_WIDTH;
return ++fmt - start;
}
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 == '*') {
spec->type = FORMAT_TYPE_PRECISION;
return ++fmt - start;
}
}
qualifier:
qualifier = 0;
if (*fmt == 'h' || _tolower(*fmt) == 'l' ||
_tolower(*fmt) == 'z' || *fmt == 't') {
qualifier = *fmt++;
if (qualifier == *fmt) {
if (qualifier == 'l') {
qualifier = 'L';
++fmt;
} else if (qualifier == 'h') {
qualifier = 'H';
++fmt;
} else {
sm_warning("invalid repeated qualifier '%c'", *fmt);
}
}
}
spec->base = 10;
switch (*fmt) {
case 'c':
if (qualifier)
sm_warning("qualifier '%c' ignored for %%c specifier", qualifier);
spec->type = FORMAT_TYPE_CHAR;
return ++fmt - start;
case 's':
if (qualifier && qualifier != 'l')
sm_warning("qualifier '%c' ignored for %%s specifier", qualifier);
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;
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;
return ++fmt - start;
}
if (qualifier == 'L')
spec->type = FORMAT_TYPE_LONG_LONG;
else if (qualifier == 'l') {
if (spec->flags & SIGN)
spec->type = FORMAT_TYPE_LONG;
else
spec->type = FORMAT_TYPE_ULONG;
} else if (_tolower(qualifier) == 'z') {
spec->type = FORMAT_TYPE_SIZE_T;
} else if (qualifier == 't') {
spec->type = FORMAT_TYPE_PTRDIFF;
} else if (qualifier == 'H') {
if (spec->flags & SIGN)
spec->type = FORMAT_TYPE_BYTE;
else
spec->type = FORMAT_TYPE_UBYTE;
} else if (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 has_struct_tag(struct symbol *type, const char *tag)
{
struct symbol *tmp;
if (type->type == SYM_STRUCT)
return is_struct_tag(type, tag);
if (type->type == SYM_UNION) {
FOR_EACH_PTR(type->symbol_list, tmp) {
tmp = get_real_base_type(tmp);
if (tmp && is_struct_tag(tmp, tag))
return 1;
} END_FOR_EACH_PTR(tmp);
}
return 0;
}
static int is_char_type(struct symbol *type)
{
return type == &uchar_ctype || type == &char_ctype || type == &schar_ctype;
}
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 get_real_base_type(node);
}
static void typedef_lookup(struct typedef_lookup *tl)
{
if (tl->sym || tl->failed)
return;
tl->sym = _typedef_lookup(tl->name);
if (!tl->sym) {
sm_perror(" 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_warning("'%%p%c4' can only be followed by one of [hnbl], not '%c'", fmt[0], fmt[2]);
}
if (isalnum(fmt[3]))
sm_warning("'%%p%c4' can only be followed by precisely one of [hnbl]", fmt[0]);
}
if (type->ctype.modifiers & MOD_NODEREF)
sm_error("passing __user pointer to '%%p%c4'", fmt[0]);
if (is_char_type(basetype))
return;
if (is_struct_tag(basetype, "in_addr") && endian != ENDIAN_BIG)
sm_warning("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_warning("'%%p%c6' can only be followed by c", fmt[0]);
else if (fmt[0] == 'i')
sm_warning("'%%pi6' does not allow flag c");
if (isalnum(fmt[3]))
sm_warning("'%%p%c6%c' cannot be followed by other alphanumerics", fmt[0], fmt[2]);
}
if (type->ctype.modifiers & MOD_NODEREF)
sm_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) {
if (!strchr("pfschnbl", *f))
sm_warning("'%%p%cS' cannot be followed by '%c'", fmt[0], *f);
}
if (!has_struct_tag(basetype, "sockaddr") &&
!has_struct_tag(basetype, "sockaddr_in") &&
!has_struct_tag(basetype, "sockaddr_in6") &&
!has_struct_tag(basetype, "__kernel_sockaddr_storage"))
sm_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_warning("'%%ph' cannot be followed by '%c'", fmt[1]);
if (isalnum(fmt[2]))
sm_warning("'%%ph' can be followed by at most one of [CDN], and no other alphanumerics");
}
if (type->ctype.modifiers & MOD_NODEREF)
sm_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_warning("%%pE can only be followed by a combination of [achnops]");
}
if (type->ctype.modifiers & MOD_NODEREF)
sm_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_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_warning("'%%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_warning("'%%p%c' cannot be followed by '%c'", fmt[0], fmt[1]);
if (fmt[0] == 'm' && fmt[1] == 'F')
sm_warning("it is pointless to pass flag F to %%pm");
if (isalnum(fmt[2]))
sm_warning("'%%p%c%c' cannot be followed by other alphanumeric", fmt[0], fmt[1]);
}
if (!is_char_type(basetype) && !is_struct_tag(basetype, "bdaddr_t") && basetype != &void_ctype) {
sm_warning("'%%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_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_warning("'%%p%c' can only be followed by one of [234]", fmt[0]);
if (isalnum(fmt[2]))
sm_warning("'%%p%c%c' cannot be followed by '%c'", fmt[0], fmt[1], fmt[2]);
}
if (!is_struct_tag(basetype, tag))
sm_error("'%%p%c' expects argument of type struct '%s*', argument %d has type '%s'",
fmt[0], tag, vaidx, type_to_str(type));
}
static void time_and_date(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
{
assert(tolower(fmt[0]) == 't');
if (fmt[1] == 'R' && !is_struct_tag(basetype, "rtc_time"))
sm_error("'%%ptR' expects argument of type struct 'rtc_time', argument %d has type '%s'",
vaidx, type_to_str(type));
}
static void check_clock(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
{
assert(fmt[0] == 'C');
if (isalnum(fmt[1])) {
if (!strchr("nr", fmt[1]))
sm_warning("'%%pC' can only be followed by one of [nr]");
if (isalnum(fmt[2]))
sm_warning("'%%pC%c' cannot be followed by '%c'", fmt[1], fmt[2]);
}
if (!is_struct_tag(basetype, "clk"))
sm_error("'%%pC' expects argument of type 'struct clk*', argument %d has type '%s'",
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_warning("%%pV cannot be followed by any alphanumerics");
if (!is_struct_tag(basetype, "va_format"))
sm_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_error("%%pN must be followed by 'F'");
return;
}
if (isalnum(fmt[2]))
sm_warning("%%pNF cannot be followed by '%c'", fmt[2]);
typedef_lookup(&netdev);
if (!netdev.sym)
return;
if (basetype != netdev.sym)
sm_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_error("'%%pa' can only be followed by one of [dp]");
}
if (isalnum(fmt[2]))
sm_error("'%%pa%c' cannot be followed by '%c'", fmt[1], fmt[2]);
}
typedef_lookup(which);
if (!which->sym)
return;
if (basetype != which->sym) {
sm_error("'%%pa%s' expects argument of type '%s*', argument %d has type '%s'",
suf, which->name, vaidx, type_to_str(type));
}
}
static void block_device(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
{
const char *tag = "block_device";
assert(fmt[0] == 'g');
if (isalnum(fmt[1])) {
sm_warning("%%pg cannot be followed by '%c'", fmt[1]);
}
if (!is_struct_tag(basetype, tag))
sm_error("'%%p%c' expects argument of type struct '%s*', argument %d has type '%s'",
fmt[0], tag, vaidx, type_to_str(type));
}
static void flag_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
{
static struct typedef_lookup gfp = { .name = "gfp_t" };
assert(fmt[0] == 'G');
if (!isalnum(fmt[1])) {
sm_error("%%pG must be followed by one of [gpv]");
return;
}
switch (fmt[1]) {
case 'p':
case 'v':
if (basetype != &ulong_ctype)
sm_error("'%%pG%c' expects argument of type 'unsigned long *', argument %d has type '%s'",
fmt[1], vaidx, type_to_str(type));
break;
case 'g':
typedef_lookup(&gfp);
if (basetype != gfp.sym)
sm_error("'%%pGg' expects argument of type 'gfp_t *', argument %d has type '%s'",
vaidx, type_to_str(type));
break;
default:
sm_error("'%%pG' must be followed by one of [gpv]");
}
}
static void device_node_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
{
if (fmt[1] != 'F') {
sm_error("%%pO can only be followed by 'F'");
return;
}
if (!is_struct_tag(basetype, "device_node"))
sm_error("'%%pOF' expects argument of type 'struct device_node*', argument %d has type '%s'",
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_warning("could not determine type of argument %d", vaidx);
return;
}
if (!is_ptr_type(type)) {
sm_error("%%p expects pointer argument, but argument %d has type '%s'",
vaidx, type_to_str(type));
return;
}
if (*fmt == 'e')
fmt++;
if (!isalnum(*fmt))
return;
basetype = get_real_base_type(type);
if (is_void_type(basetype))
return;
if (basetype->type == SYM_ARRAY) {
spam("note: passing pointer-to-array; is the address-of redundant?");
type = basetype;
basetype = get_real_base_type(type);
}
switch (*fmt) {
case 'b':
case 'F':
case 'f':
case 'S':
case 's':
case 'B':
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_warning("'%%p%c' must be followed by one of [46S]", fmt[0]);
break;
}
break;
case 'E':
escaped_string(fmt, type, basetype, vaidx);
break;
case 'h':
hex_string(fmt, type, basetype, vaidx);
break;
case 'U':
break;
case 'V':
va_format(fmt, type, basetype, vaidx);
break;
case 'K':
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;
case 't':
time_and_date(fmt, type, basetype, vaidx);
break;
case 'C':
check_clock(fmt, type, basetype, vaidx);
break;
case 'g':
block_device(fmt, type, basetype, vaidx);
break;
case 'G':
flag_string(fmt, type, basetype, vaidx);
break;
case 'O':
device_node_string(fmt, type, basetype, vaidx);
break;
case 'x':
break;
default:
sm_error("unrecognized %%p extension '%c', treated as normal %%p", *fmt);
}
}
static void
hexbyte(const char *fmt, int fmt_len, struct expression *arg, int vaidx, struct printf_spec spec)
{
struct symbol *type;
if (spec.field_width != 2 && spec.precision != 2)
return;
if (spec.base != 16)
return;
type = get_type(arg);
if (!type) {
sm_warning("could not determine type of argument %d", vaidx);
return;
}
if (type == &char_ctype || type == &schar_ctype)
sm_warning("argument %d to %.*s specifier has type '%s'",
vaidx, fmt_len, fmt, type_to_str(type));
}
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:
if (f != fmt)
sm_warning("KERN_* level not at start of string");
if (!(('0' <= f[1] && f[1] <= '7') ||
f[1] == 'd' ||
f[1] == 'c' ||
(f[1] == '%' && f[2] == 'c')))
sm_warning("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_warning("format string contains non-ascii character '\\x%02x'", c);
break;
case 0x08:
if (f == fmt)
break;
default:
sm_warning("format string contains unusual character '\\x%02x'", c);
break;
}
}
f = strstr(fmt, caller);
if (f && strstr(f+1, caller))
sm_warning("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 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 int unknown_format(struct expression *expr)
{
struct state_list *slist;
slist = get_strings(expr);
if (!slist)
return 1;
if (slist_has_state(slist, &undefined))
return 1;
free_slist(&slist);
return 0;
}
static bool has_hex_prefix(const char *orig_fmt, const char *old_fmt)
{
return old_fmt >= orig_fmt + 2 &&
old_fmt[-2] == '0' && _tolower(old_fmt[-1]) == 'x';
}
static bool is_integer_specifier(int type)
{
switch (type) {
case FORMAT_TYPE_LONG_LONG:
case FORMAT_TYPE_ULONG:
case FORMAT_TYPE_LONG:
case FORMAT_TYPE_UBYTE:
case FORMAT_TYPE_BYTE:
case FORMAT_TYPE_USHORT:
case FORMAT_TYPE_SHORT:
case FORMAT_TYPE_UINT:
case FORMAT_TYPE_INT:
case FORMAT_TYPE_SIZE_T:
case FORMAT_TYPE_PTRDIFF:
return true;
default:
return false;
}
}
static int
is_cast_expr(struct expression *expr)
{
if (!expr)
return 0;
switch (expr->type) {
case EXPR_CAST:
case EXPR_FORCE_CAST:
return 1;
default:
return 0;
}
}
static void
check_cast_from_pointer(const char *fmt, int len, struct expression *arg, int va_idx)
{
if (!is_cast_expr(arg))
return;
while (is_cast_expr(arg))
arg = arg->cast_expression;
if (is_ptr_type(get_final_type(arg)))
sm_warning("argument %d to %.*s specifier is cast from pointer",
va_idx, len, fmt);
}
static void
do_check_printf_call(const char *caller, const char *name, struct expression *callexpr, struct expression *fmtexpr, int vaidx)
{
struct printf_spec spec = {0};
const char *fmt, *orig_fmt;
int caller_in_fmt;
fmtexpr = strip_parens(fmtexpr);
if (fmtexpr->type == EXPR_CONDITIONAL) {
do_check_printf_call(caller, name, callexpr, fmtexpr->cond_true ? : fmtexpr->conditional, vaidx);
do_check_printf_call(caller, name, callexpr, fmtexpr->cond_false, vaidx);
return;
}
if (fmtexpr->type == EXPR_SYMBOL) {
struct symbol *sym = fmtexpr->symbol;
if (sym && 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) {
if (!unknown_format(fmtexpr))
return;
spam("warn: call of '%s' with non-constant format argument", name);
return;
}
orig_fmt = 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;
arg = get_argument_from_call_expr(callexpr->args, vaidx++);
if (spec.flags & SPECIAL && has_hex_prefix(orig_fmt, old_fmt))
sm_warning("'%.2s' prefix is redundant when # flag is used", old_fmt-2);
if (is_integer_specifier(spec.type)) {
if (spec.base != 16 && has_hex_prefix(orig_fmt, old_fmt))
sm_warning("'%.2s' prefix is confusing together with '%.*s' specifier",
old_fmt-2, (int)(fmt-old_fmt), old_fmt);
check_cast_from_pointer(old_fmt, read, arg, vaidx);
}
switch (spec.type) {
case FORMAT_TYPE_INVALID:
sm_error("format specifier '%.*s' invalid", read, old_fmt);
return;
case FORMAT_TYPE_FLOAT:
sm_error("no floats in the kernel; invalid format specifier '%.*s'", read, old_fmt);
return;
case FORMAT_TYPE_NRCHARS:
sm_error("%%n not supported in kernel");
return;
case FORMAT_TYPE_WIDTH:
case FORMAT_TYPE_PRECISION:
break;
case FORMAT_TYPE_STR:
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_warning("passing string constant '%s' containing '%s' which is already part of the format string",
arg->string->data, caller);
}
break;
case FORMAT_TYPE_PTR:
pointer(fmt, arg, vaidx);
while (isalnum(*fmt))
fmt++;
break;
case FORMAT_TYPE_CHAR:
case FORMAT_TYPE_UBYTE:
case FORMAT_TYPE_BYTE:
case FORMAT_TYPE_USHORT:
case FORMAT_TYPE_SHORT:
case FORMAT_TYPE_INT:
break;
case FORMAT_TYPE_UINT:
hexbyte(old_fmt, fmt-old_fmt, arg, vaidx, spec);
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(callexpr->args, vaidx))
sm_warning("excess argument passed to '%s'", name);
}
static void
check_printf_call(const char *name, struct expression *callexpr, void *_info)
{
int info = PTR_INT(_info);
int fmtidx = (info & 0xff) - 1;
int vaidx = ((info >> 8) & 0xff) - 1;
struct expression *fmtexpr;
const char *caller = get_function();
if (!caller)
return;
if (vaidx < 0)
return;
if (!strncmp(caller, "SyS_", 4))
caller += 4;
while (*caller == '_')
++caller;
fmtexpr = get_argument_from_call_expr(callexpr->args, fmtidx);
if (!fmtexpr) {
sm_error("call of '%s' with no format argument", name);
return;
}
do_check_printf_call(caller, name, callexpr, fmtexpr, vaidx);
}
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)))
#if 0
printf_hook(srm_printk, 1, 2);
printf_hook(die_if_kernel, 1, 2);
printf_hook(ia64_mca_printk, 1, 2);
printf_hook(nfprint, 1, 2);
printf_hook(gdbstub_printk, 1, 2);
printf_hook(DBG, 1, 2);
printf_hook(printf, 1, 2);
printf_hook(udbg_printf, 1, 2);
printf_hook(__debug_sprintf_event, 3, 4);
printf_hook(__debug_sprintf_exception, 3, 4);
printf_hook(prom_printf, 1, 2);
printf_hook(fail, 1, 2);
#endif
printf_hook(_ldm_printk, 3, 4);
printf_hook(rbd_warn, 2, 3);
printf_hook(fw_err, 2, 3);
printf_hook(fw_notice, 2, 3);
printf_hook(i915_error_printf, 2, 3);
printf_hook(i915_handle_error, 3, 4);
printf_hook(nv_printk_, 3, 4);
printf_hook(host1x_debug_output, 2, 3);
printf_hook(callc_debug, 2, 3);
printf_hook(link_debug, 3, 4);
printf_hook(HiSax_putstatus, 3, 4);
printf_hook(VHiSax_putstatus, 3, 0);
printf_hook(debugl1, 2, 3);
printf_hook(l3m_debug, 2, 3);
printf_hook(dout_debug, 2, 3);
printf_hook(l1m_debug, 2, 3);
printf_hook(bch_cache_set_error, 2, 3);
printf_hook(_tda_printk, 4, 5);
printf_hook(i40evf_debug_d, 3, 4);
printf_hook(en_print, 3, 4);
printf_hook(_ath_dbg, 3, 4);
printf_hook(ath_printk, 3, 4);
printf_hook(ath10k_dbg, 3, 4);
printf_hook(ath10k_err, 2, 3);
printf_hook(ath10k_info, 2, 3);
printf_hook(ath10k_warn, 2, 3);
printf_hook(_ath5k_printk, 3, 4);
printf_hook(ATH5K_DBG, 3, 4);
printf_hook(ATH5K_DBG_UNLIMIT, 3, 4);
printf_hook(ath6kl_printk, 2, 3);
printf_hook(ath6kl_err, 1, 2);
printf_hook(ath6kl_info, 1, 2);
printf_hook(ath6kl_warn, 1, 2);
printf_hook(wil_dbg_trace, 2, 3);
printf_hook(wil_err, 2, 3);
printf_hook(wil_err_ratelimited, 2, 3);
printf_hook(wil_info, 2, 3);
printf_hook(b43dbg, 2, 3);
printf_hook(b43err, 2, 3);
printf_hook(b43info, 2, 3);
printf_hook(b43warn, 2, 3);
printf_hook(b43legacydbg, 2, 3);
printf_hook(b43legacyerr, 2, 3);
printf_hook(b43legacyinfo, 2, 3);
printf_hook(b43legacywarn, 2, 3);
printf_hook(__brcmf_dbg, 3, 4);
printf_hook(__brcmf_err, 2, 3);
printf_hook(__brcms_crit, 2, 3);
printf_hook(__brcms_dbg, 4, 5);
printf_hook(__brcms_err, 2, 3);
printf_hook(__brcms_info, 2, 3);
printf_hook(__brcms_warn, 2, 3);
printf_hook(brcmu_dbg_hex_dump, 3, 4);
printf_hook(__iwl_crit, 2, 3);
printf_hook(__iwl_dbg, 5, 6);
printf_hook(__iwl_err, 4, 5);
printf_hook(__iwl_info, 2, 3);
printf_hook(__iwl_warn, 2, 3);
printf_hook(rsi_dbg, 2, 3);
printf_hook(RTPRINT, 4, 5);
printf_hook(RT_ASSERT, 2, 3);
printf_hook(RT_TRACE, 4, 5);
printf_hook(__of_node_dup, 2, 3);
printf_hook(BNX2FC_HBA_DBG, 2, 3);
printf_hook(BNX2FC_IO_DBG, 2, 3);
printf_hook(BNX2FC_TGT_DBG, 2, 3);
printf_hook(ql_dbg, 4, 5);
printf_hook(ql_dbg_pci, 4, 5);
printf_hook(ql_log, 4, 5);
printf_hook(ql_log_pci, 4, 5);
printf_hook(libcfs_debug_msg, 2, 3);
printf_hook(libcfs_debug_vmsg2, 4, 5);
printf_hook(_ldlm_lock_debug, 3, 4);
printf_hook(_debug_req, 3, 4);
printf_hook(iscsi_change_param_sprintf, 2, 3);
printf_hook(dbg, 1, 2);
printf_hook(_usb_stor_dbg, 2, 3);
printf_hook(usb_stor_dbg, 2, 3);
printf_hook(vringh_bad, 1, 2);
printf_hook(__adfs_error, 3, 4);
printf_hook(affs_error, 3, 4);
printf_hook(affs_warning, 3, 4);
printf_hook(befs_debug, 2, 3);
printf_hook(befs_error, 2, 3);
printf_hook(befs_warning, 2, 3);
printf_hook(__btrfs_panic, 5, 6);
printf_hook(__btrfs_std_error, 5, 6);
printf_hook(btrfs_printk, 2, 3);
printf_hook(cifs_vfs_err, 1, 2);
printf_hook(__ecryptfs_printk, 1, 2);
printf_hook(ext2_error, 3, 4);
printf_hook(ext2_msg, 3, 4);
printf_hook(ext3_abort, 3, 4);
printf_hook(ext3_error, 3, 4);
printf_hook(ext3_msg, 3, 4);
printf_hook(ext3_warning, 3, 4);
printf_hook(__ext4_abort, 4, 5);
printf_hook(__ext4_error, 4, 5);
printf_hook(__ext4_error_file, 5, 6);
printf_hook(__ext4_error_inode, 5, 6);
printf_hook(__ext4_grp_locked_error, 7, 8);
printf_hook(__ext4_msg, 3, 4);
printf_hook(__ext4_warning, 4, 5);
printf_hook(f2fs_msg, 3, 4);
printf_hook(__fat_fs_error, 3, 4);
printf_hook(fat_msg, 3, 4);
printf_hook(gfs2_print_dbg, 2, 3);
printf_hook(gfs2_lm_withdraw, 2, 3);
printf_hook(hpfs_error, 2, 3);
printf_hook(jfs_error, 2, 3);
printf_hook(nilfs_error, 3, 4);
printf_hook(nilfs_warning, 3, 4);
printf_hook(__ntfs_debug, 4, 5);
printf_hook(__ntfs_error, 3, 4);
printf_hook(__ntfs_warning, 3, 4);
printf_hook(__ocfs2_abort, 3, 4);
printf_hook(__ocfs2_error, 3, 4);
printf_hook(_udf_err, 3, 4);
printf_hook(_udf_warn, 3, 4);
printf_hook(ufs_error, 3, 4);
printf_hook(ufs_panic, 3, 4);
printf_hook(ufs_warning, 3, 4);
printf_hook(xfs_alert, 2, 3);
printf_hook(xfs_alert_tag, 3, 4);
printf_hook(xfs_crit, 2, 3);
printf_hook(xfs_debug, 2, 3);
printf_hook(xfs_emerg, 2, 3);
printf_hook(xfs_err, 2, 3);
printf_hook(xfs_info, 2, 3);
printf_hook(xfs_notice, 2, 3);
printf_hook(xfs_warn, 2, 3);
printf_hook(warn_slowpath_fmt, 3, 4);
printf_hook(warn_slowpath_fmt_taint, 4, 5);
printf_hook(drm_err, 1, 2);
printf_hook(drm_ut_debug_printk, 2, 3);
printf_hook(__acpi_handle_debug, 3, 4);
printf_hook(acpi_handle_printk, 3, 4);
printf_hook(audit_log, 4, 5);
printf_hook(audit_log_format, 2, 3);
printf_hook(bdi_register, 3, 4);
printf_hook(__trace_note_message, 2, 3);
printf_hook(_dev_info, 2, 3);
printf_hook(dev_alert, 2, 3);
printf_hook(dev_crit, 2, 3);
printf_hook(dev_emerg, 2, 3);
printf_hook(dev_err, 2, 3);
printf_hook(dev_notice, 2, 3);
printf_hook(dev_printk, 3, 4);
printf_hook(dev_printk_emit, 3, 4);
printf_hook(dev_set_name, 2, 3);
printf_hook(dev_vprintk_emit, 3, 0);
printf_hook(dev_warn, 2, 3);
printf_hook(device_create, 5, 6);
printf_hook(device_create_with_groups, 6, 7);
printf_hook(devm_kasprintf, 3, 4);
printf_hook(__dynamic_dev_dbg, 3, 4);
printf_hook(__dynamic_netdev_dbg, 3, 4);
printf_hook(__dynamic_pr_debug, 2, 3);
printf_hook(__simple_attr_check_format, 1, 2);
printf_hook(fscache_init_cache, 3, 4);
printf_hook(gameport_set_phys, 2, 3);
printf_hook(iio_trigger_alloc, 1, 2);
printf_hook(__check_printsym_format, 1, 2);
printf_hook(kdb_printf, 1, 2);
printf_hook(vkdb_printf, 1, 0);
printf_hook(____trace_printk_check_format, 1, 2);
printf_hook(__trace_bprintk, 2, 3);
printf_hook(__trace_printk, 2, 3);
printf_hook(kasprintf, 2, 3);
printf_hook(panic, 1, 2);
printf_hook(scnprintf, 3, 4);
printf_hook(snprintf, 3, 4);
printf_hook(sprintf, 2, 3);
printf_hook(trace_printk, 1, 2);
printf_hook(vscnprintf, 3, 0);
printf_hook(vsnprintf, 3, 0);
printf_hook(vsprintf, 2, 0);
printf_hook(vmcoreinfo_append_str, 1, 2);
printf_hook(__request_module, 2, 3);
printf_hook(add_uevent_var, 2, 3);
printf_hook(kobject_add, 3, 4);
printf_hook(kobject_init_and_add, 4, 5);
printf_hook(kobject_set_name, 2, 3);
printf_hook(kthread_create_on_node, 4, 5);
printf_hook(__ata_ehi_push_desc, 2, 3);
printf_hook(ata_dev_printk, 3, 4);
printf_hook(ata_ehi_push_desc, 2, 3);
printf_hook(ata_link_printk, 3, 4);
printf_hook(ata_port_desc, 2, 3);
printf_hook(ata_port_printk, 3, 4);
printf_hook(warn_alloc_failed, 3, 4);
printf_hook(mmiotrace_printk, 1, 2);
printf_hook(netdev_alert, 2, 3);
printf_hook(netdev_crit, 2, 3);
printf_hook(netdev_emerg, 2, 3);
printf_hook(netdev_err, 2, 3);
printf_hook(netdev_info, 2, 3);
printf_hook(netdev_notice, 2, 3);
printf_hook(netdev_printk, 3, 4);
printf_hook(netdev_warn, 2, 3);
printf_hook(early_printk, 1, 2);
printf_hook(no_printk, 1, 2);
printf_hook(printk, 1, 2);
printf_hook(printk_deferred, 1, 2);
printf_hook(printk_emit, 5, 6);
printf_hook(vprintk, 1, 0);
printf_hook(vprintk_emit, 5, 0);
printf_hook(__quota_error, 3, 4);
printf_hook(seq_buf_printf, 2, 3);
printf_hook(seq_buf_vprintf, 2, 0);
printf_hook(seq_printf, 2, 3);
printf_hook(seq_vprintf, 2, 0);
printf_hook(bprintf, 3, 4);
printf_hook(trace_seq_printf, 2, 3);
printf_hook(trace_seq_vprintf, 2, 0);
printf_hook(__alloc_workqueue_key, 1, 6);
printf_hook(set_worker_desc, 1, 2);
printf_hook(_p9_debug, 3, 4);
printf_hook(bt_err, 1, 2);
printf_hook(bt_info, 1, 2);
printf_hook(nf_ct_helper_log, 3, 4);
printf_hook(nf_log_buf_add, 2, 3);
printf_hook(nf_log_packet, 8, 9);
printf_hook(SOCK_DEBUG, 2, 3);
printf_hook(__snd_printk, 4, 5);
printf_hook(_snd_printd, 2, 3);
printf_hook(snd_printd, 1, 2);
printf_hook(snd_printdd, 1, 2);
printf_hook(snd_iprintf, 2, 3);
printf_hook(snd_seq_create_kernel_client, 3, 4);
printf_hook(xen_raw_printk, 1, 2);
printf_hook(xenbus_dev_error, 3, 4);
printf_hook(xenbus_dev_fatal, 3, 4);
printf_hook(xenbus_printf, 4, 5);
printf_hook(xenbus_watch_pathfmt, 4, 5);
printf_hook(batadv_fdebug_log, 2, 3);
printf_hook(_batadv_dbg, 4, 5);
printf_hook(batadv_debug_log, 2, 3);
printf_hook(__sdata_dbg, 2, 3);
printf_hook(__sdata_err, 1, 2);
printf_hook(__sdata_info, 1, 2);
printf_hook(__wiphy_dbg, 3, 4);
printf_hook(mac80211_format_buffer, 4, 5);
printf_hook(__rds_conn_error, 2, 3);
printf_hook(rdsdebug, 1, 2);
printf_hook(printl, 1, 2);
printf_hook(svc_printk, 2, 3);
printf_hook(tomoyo_io_printf, 2, 3);
printf_hook(tomoyo_supervisor, 2, 3);
printf_hook(tomoyo_write_log, 2, 3);
printf_hook(cmp_error, 2, 3);
}