xref: /illumos-gate/usr/src/tools/smatch/src/check_kernel_printf.c (revision b3263c9871d056ea54cca24eaeedd5a41fd333de)
11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2015 Rasmus Villemoes.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
18*b3263c98SJohn Levon #define _GNU_SOURCE
19*b3263c98SJohn Levon 
201f5207b7SJohn Levon #include <assert.h>
211f5207b7SJohn Levon #include <ctype.h>
221f5207b7SJohn Levon #include <string.h>
231f5207b7SJohn Levon #include "smatch.h"
241f5207b7SJohn Levon #include "smatch_slist.h"
251f5207b7SJohn Levon 
261f5207b7SJohn Levon #define spam(args...) do {			\
271f5207b7SJohn Levon 	if (option_spammy)			\
281f5207b7SJohn Levon 		sm_msg(args);			\
291f5207b7SJohn Levon 	} while (0)
301f5207b7SJohn Levon 
311f5207b7SJohn Levon static int my_id;
321f5207b7SJohn Levon 
331f5207b7SJohn Levon /*
341f5207b7SJohn Levon  * Much of this is taken directly from the kernel (mostly vsprintf.c),
351f5207b7SJohn Levon  * with a few modifications here and there.
361f5207b7SJohn Levon  */
371f5207b7SJohn Levon 
381f5207b7SJohn Levon #define KERN_SOH_ASCII  '\001'
391f5207b7SJohn Levon 
401f5207b7SJohn Levon typedef unsigned char u8;
411f5207b7SJohn Levon typedef signed short s16;
421f5207b7SJohn Levon 
431f5207b7SJohn Levon #define SIGN	1		/* unsigned/signed, must be 1 */
441f5207b7SJohn Levon #define LEFT	2		/* left justified */
451f5207b7SJohn Levon #define PLUS	4		/* show plus */
461f5207b7SJohn Levon #define SPACE	8		/* space if plus */
471f5207b7SJohn Levon #define ZEROPAD	16		/* pad with zero, must be 16 == '0' - ' ' */
481f5207b7SJohn Levon #define SMALL	32		/* use lowercase in hex (must be 32 == 0x20) */
491f5207b7SJohn Levon #define SPECIAL	64		/* prefix hex with "0x", octal with "0" */
501f5207b7SJohn Levon 
511f5207b7SJohn Levon enum format_type {
521f5207b7SJohn Levon 	FORMAT_TYPE_NONE, /* Just a string part */
531f5207b7SJohn Levon 	FORMAT_TYPE_WIDTH,
541f5207b7SJohn Levon 	FORMAT_TYPE_PRECISION,
551f5207b7SJohn Levon 	FORMAT_TYPE_CHAR,
561f5207b7SJohn Levon 	FORMAT_TYPE_STR,
571f5207b7SJohn Levon 	FORMAT_TYPE_PTR,
581f5207b7SJohn Levon 	FORMAT_TYPE_PERCENT_CHAR,
591f5207b7SJohn Levon 	FORMAT_TYPE_INVALID,
601f5207b7SJohn Levon 	FORMAT_TYPE_LONG_LONG,
611f5207b7SJohn Levon 	FORMAT_TYPE_ULONG,
621f5207b7SJohn Levon 	FORMAT_TYPE_LONG,
631f5207b7SJohn Levon 	FORMAT_TYPE_UBYTE,
641f5207b7SJohn Levon 	FORMAT_TYPE_BYTE,
651f5207b7SJohn Levon 	FORMAT_TYPE_USHORT,
661f5207b7SJohn Levon 	FORMAT_TYPE_SHORT,
671f5207b7SJohn Levon 	FORMAT_TYPE_UINT,
681f5207b7SJohn Levon 	FORMAT_TYPE_INT,
691f5207b7SJohn Levon 	FORMAT_TYPE_SIZE_T,
701f5207b7SJohn Levon 	FORMAT_TYPE_PTRDIFF,
711f5207b7SJohn Levon 	FORMAT_TYPE_NRCHARS, /* Reintroduced for this checker */
721f5207b7SJohn Levon 	FORMAT_TYPE_FLOAT, /* for various floating point formatters */
731f5207b7SJohn Levon };
741f5207b7SJohn Levon 
751f5207b7SJohn Levon struct printf_spec {
761f5207b7SJohn Levon 	unsigned int	type:8;		/* format_type enum */
771f5207b7SJohn Levon 	signed int	field_width:24;	/* width of output field */
781f5207b7SJohn Levon 	unsigned int	flags:8;	/* flags to number() */
791f5207b7SJohn Levon 	unsigned int	base:8;		/* number base, 8, 10 or 16 only */
801f5207b7SJohn Levon 	signed int	precision:16;	/* # of digits/chars */
811f5207b7SJohn Levon } __packed;
821f5207b7SJohn Levon #define FIELD_WIDTH_MAX ((1 << 23) - 1)
831f5207b7SJohn Levon #define PRECISION_MAX ((1 << 15) - 1)
841f5207b7SJohn Levon extern char __check_printf_spec[1-2*(sizeof(struct printf_spec) != 8)];
851f5207b7SJohn Levon 
861f5207b7SJohn Levon static int
skip_atoi(const char ** s)871f5207b7SJohn Levon skip_atoi(const char **s)
881f5207b7SJohn Levon {
891f5207b7SJohn Levon 	int i = 0;
901f5207b7SJohn Levon 
911f5207b7SJohn Levon 	while (isdigit(**s))
921f5207b7SJohn Levon 		i = i*10 + *((*s)++) - '0';
931f5207b7SJohn Levon 
941f5207b7SJohn Levon 	return i;
951f5207b7SJohn Levon }
961f5207b7SJohn Levon 
971f5207b7SJohn Levon static int
format_decode(const char * fmt,struct printf_spec * spec)981f5207b7SJohn Levon format_decode(const char *fmt, struct printf_spec *spec)
991f5207b7SJohn Levon {
1001f5207b7SJohn Levon 	const char *start = fmt;
1011f5207b7SJohn Levon 	char qualifier;
1021f5207b7SJohn Levon 
1031f5207b7SJohn Levon 	/* we finished early by reading the field width */
1041f5207b7SJohn Levon 	if (spec->type == FORMAT_TYPE_WIDTH) {
1051f5207b7SJohn Levon 		if (spec->field_width < 0) {
1061f5207b7SJohn Levon 			spec->field_width = -spec->field_width;
1071f5207b7SJohn Levon 			spec->flags |= LEFT;
1081f5207b7SJohn Levon 		}
1091f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_NONE;
1101f5207b7SJohn Levon 		goto precision;
1111f5207b7SJohn Levon 	}
1121f5207b7SJohn Levon 
1131f5207b7SJohn Levon 	/* we finished early by reading the precision */
1141f5207b7SJohn Levon 	if (spec->type == FORMAT_TYPE_PRECISION) {
1151f5207b7SJohn Levon 		if (spec->precision < 0)
1161f5207b7SJohn Levon 			spec->precision = 0;
1171f5207b7SJohn Levon 
1181f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_NONE;
1191f5207b7SJohn Levon 		goto qualifier;
1201f5207b7SJohn Levon 	}
1211f5207b7SJohn Levon 
1221f5207b7SJohn Levon 	/* By default */
1231f5207b7SJohn Levon 	spec->type = FORMAT_TYPE_NONE;
1241f5207b7SJohn Levon 
1251f5207b7SJohn Levon 	for (; *fmt ; ++fmt) {
1261f5207b7SJohn Levon 		if (*fmt == '%')
1271f5207b7SJohn Levon 			break;
1281f5207b7SJohn Levon 	}
1291f5207b7SJohn Levon 
1301f5207b7SJohn Levon 	/* Return the current non-format string */
1311f5207b7SJohn Levon 	if (fmt != start || !*fmt)
1321f5207b7SJohn Levon 		return fmt - start;
1331f5207b7SJohn Levon 
1341f5207b7SJohn Levon 	/* Process flags */
1351f5207b7SJohn Levon 	spec->flags = 0;
1361f5207b7SJohn Levon 
1371f5207b7SJohn Levon 	while (1) { /* this also skips first '%' */
1381f5207b7SJohn Levon 		bool found = true;
1391f5207b7SJohn Levon 
1401f5207b7SJohn Levon 		++fmt;
1411f5207b7SJohn Levon 
1421f5207b7SJohn Levon 		switch (*fmt) {
1431f5207b7SJohn Levon 		case '-': spec->flags |= LEFT;    break;
1441f5207b7SJohn Levon 		case '+': spec->flags |= PLUS;    break;
1451f5207b7SJohn Levon 		case ' ': spec->flags |= SPACE;   break;
1461f5207b7SJohn Levon 		case '#': spec->flags |= SPECIAL; break;
1471f5207b7SJohn Levon 		case '0': spec->flags |= ZEROPAD; break;
1481f5207b7SJohn Levon 		default:  found = false;
1491f5207b7SJohn Levon 		}
1501f5207b7SJohn Levon 
1511f5207b7SJohn Levon 		if (!found)
1521f5207b7SJohn Levon 			break;
1531f5207b7SJohn Levon 	}
1541f5207b7SJohn Levon 
1551f5207b7SJohn Levon 	/* get field width */
1561f5207b7SJohn Levon 	spec->field_width = -1;
1571f5207b7SJohn Levon 
1581f5207b7SJohn Levon 	if (isdigit(*fmt))
1591f5207b7SJohn Levon 		spec->field_width = skip_atoi(&fmt);
1601f5207b7SJohn Levon 	else if (*fmt == '*') {
1611f5207b7SJohn Levon 		/* it's the next argument */
1621f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_WIDTH;
1631f5207b7SJohn Levon 		return ++fmt - start;
1641f5207b7SJohn Levon 	}
1651f5207b7SJohn Levon 
1661f5207b7SJohn Levon precision:
1671f5207b7SJohn Levon 	/* get the precision */
1681f5207b7SJohn Levon 	spec->precision = -1;
1691f5207b7SJohn Levon 	if (*fmt == '.') {
1701f5207b7SJohn Levon 		++fmt;
1711f5207b7SJohn Levon 		if (isdigit(*fmt)) {
1721f5207b7SJohn Levon 			spec->precision = skip_atoi(&fmt);
1731f5207b7SJohn Levon 			if (spec->precision < 0)
1741f5207b7SJohn Levon 				spec->precision = 0;
1751f5207b7SJohn Levon 		} else if (*fmt == '*') {
1761f5207b7SJohn Levon 			/* it's the next argument */
1771f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_PRECISION;
1781f5207b7SJohn Levon 			return ++fmt - start;
1791f5207b7SJohn Levon 		}
1801f5207b7SJohn Levon 	}
1811f5207b7SJohn Levon 
1821f5207b7SJohn Levon qualifier:
1831f5207b7SJohn Levon 	/* get the conversion qualifier */
1841f5207b7SJohn Levon 	qualifier = 0;
1851f5207b7SJohn Levon 	if (*fmt == 'h' || _tolower(*fmt) == 'l' ||
1861f5207b7SJohn Levon 	    _tolower(*fmt) == 'z' || *fmt == 't') {
1871f5207b7SJohn Levon 		qualifier = *fmt++;
1881f5207b7SJohn Levon 		if (qualifier == *fmt) {
1891f5207b7SJohn Levon 			if (qualifier == 'l') {
1901f5207b7SJohn Levon 				qualifier = 'L';
1911f5207b7SJohn Levon 				++fmt;
1921f5207b7SJohn Levon 			} else if (qualifier == 'h') {
1931f5207b7SJohn Levon 				qualifier = 'H';
1941f5207b7SJohn Levon 				++fmt;
1951f5207b7SJohn Levon 			} else {
1961f5207b7SJohn Levon 				sm_warning("invalid repeated qualifier '%c'", *fmt);
1971f5207b7SJohn Levon 			}
1981f5207b7SJohn Levon 		}
1991f5207b7SJohn Levon 	}
2001f5207b7SJohn Levon 
2011f5207b7SJohn Levon 	/* default base */
2021f5207b7SJohn Levon 	spec->base = 10;
2031f5207b7SJohn Levon 	switch (*fmt) {
2041f5207b7SJohn Levon 	case 'c':
2051f5207b7SJohn Levon 		if (qualifier)
2061f5207b7SJohn Levon 			sm_warning("qualifier '%c' ignored for %%c specifier", qualifier);
2071f5207b7SJohn Levon 
2081f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_CHAR;
2091f5207b7SJohn Levon 		return ++fmt - start;
2101f5207b7SJohn Levon 
2111f5207b7SJohn Levon 	case 's':
212*b3263c98SJohn Levon 		if (qualifier && qualifier != 'l')
2131f5207b7SJohn Levon 			sm_warning("qualifier '%c' ignored for %%s specifier", qualifier);
2141f5207b7SJohn Levon 
2151f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_STR;
2161f5207b7SJohn Levon 		return ++fmt - start;
2171f5207b7SJohn Levon 
2181f5207b7SJohn Levon 	case 'p':
2191f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_PTR;
2201f5207b7SJohn Levon 		return ++fmt - start;
2211f5207b7SJohn Levon 
2221f5207b7SJohn Levon 	case '%':
2231f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_PERCENT_CHAR;
2241f5207b7SJohn Levon 		return ++fmt - start;
2251f5207b7SJohn Levon 
2261f5207b7SJohn Levon 	/* integer number formats - set up the flags and "break" */
2271f5207b7SJohn Levon 	case 'o':
2281f5207b7SJohn Levon 		spec->base = 8;
2291f5207b7SJohn Levon 		break;
2301f5207b7SJohn Levon 
2311f5207b7SJohn Levon 	case 'x':
2321f5207b7SJohn Levon 		spec->flags |= SMALL;
2331f5207b7SJohn Levon 
2341f5207b7SJohn Levon 	case 'X':
2351f5207b7SJohn Levon 		spec->base = 16;
2361f5207b7SJohn Levon 		break;
2371f5207b7SJohn Levon 
2381f5207b7SJohn Levon 	case 'd':
2391f5207b7SJohn Levon 	case 'i':
2401f5207b7SJohn Levon 		spec->flags |= SIGN;
2411f5207b7SJohn Levon 	case 'u':
2421f5207b7SJohn Levon 		break;
2431f5207b7SJohn Levon 
2441f5207b7SJohn Levon 	case 'n':
2451f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_NRCHARS;
2461f5207b7SJohn Levon 		return ++fmt - start;
2471f5207b7SJohn Levon 
2481f5207b7SJohn Levon 	case 'a': case 'A':
2491f5207b7SJohn Levon 	case 'e': case 'E':
2501f5207b7SJohn Levon 	case 'f': case 'F':
2511f5207b7SJohn Levon 	case 'g': case 'G':
2521f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_FLOAT;
2531f5207b7SJohn Levon 		return ++fmt - start;
2541f5207b7SJohn Levon 
2551f5207b7SJohn Levon 	default:
2561f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_INVALID;
2571f5207b7SJohn Levon 		/* Unlike the kernel code, we 'consume' the invalid
2581f5207b7SJohn Levon 		 * character so that it can get included in the
2591f5207b7SJohn Levon 		 * report. After that, we bail out. */
2601f5207b7SJohn Levon 		return ++fmt - start;
2611f5207b7SJohn Levon 	}
2621f5207b7SJohn Levon 
2631f5207b7SJohn Levon 	if (qualifier == 'L')
2641f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_LONG_LONG;
2651f5207b7SJohn Levon 	else if (qualifier == 'l') {
2661f5207b7SJohn Levon 		if (spec->flags & SIGN)
2671f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_LONG;
2681f5207b7SJohn Levon 		else
2691f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_ULONG;
2701f5207b7SJohn Levon 	} else if (_tolower(qualifier) == 'z') {
2711f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_SIZE_T;
2721f5207b7SJohn Levon 	} else if (qualifier == 't') {
2731f5207b7SJohn Levon 		spec->type = FORMAT_TYPE_PTRDIFF;
2741f5207b7SJohn Levon 	} else if (qualifier == 'H') {
2751f5207b7SJohn Levon 		if (spec->flags & SIGN)
2761f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_BYTE;
2771f5207b7SJohn Levon 		else
2781f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_UBYTE;
2791f5207b7SJohn Levon 	} else if (qualifier == 'h') {
2801f5207b7SJohn Levon 		if (spec->flags & SIGN)
2811f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_SHORT;
2821f5207b7SJohn Levon 		else
2831f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_USHORT;
2841f5207b7SJohn Levon 	} else {
2851f5207b7SJohn Levon 		if (spec->flags & SIGN)
2861f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_INT;
2871f5207b7SJohn Levon 		else
2881f5207b7SJohn Levon 			spec->type = FORMAT_TYPE_UINT;
2891f5207b7SJohn Levon 	}
2901f5207b7SJohn Levon 
2911f5207b7SJohn Levon 	return ++fmt - start;
2921f5207b7SJohn Levon }
2931f5207b7SJohn Levon 
is_struct_tag(struct symbol * type,const char * tag)2941f5207b7SJohn Levon static int is_struct_tag(struct symbol *type, const char *tag)
2951f5207b7SJohn Levon {
2961f5207b7SJohn Levon 	return type->type == SYM_STRUCT && type->ident && !strcmp(type->ident->name, tag);
2971f5207b7SJohn Levon }
2981f5207b7SJohn Levon 
has_struct_tag(struct symbol * type,const char * tag)2991f5207b7SJohn Levon static int has_struct_tag(struct symbol *type, const char *tag)
3001f5207b7SJohn Levon {
3011f5207b7SJohn Levon 	struct symbol *tmp;
3021f5207b7SJohn Levon 
3031f5207b7SJohn Levon 	if (type->type == SYM_STRUCT)
3041f5207b7SJohn Levon 		return is_struct_tag(type, tag);
3051f5207b7SJohn Levon 	if (type->type == SYM_UNION) {
3061f5207b7SJohn Levon 		FOR_EACH_PTR(type->symbol_list, tmp) {
3071f5207b7SJohn Levon 			tmp = get_real_base_type(tmp);
3081f5207b7SJohn Levon 			if (tmp && is_struct_tag(tmp, tag))
3091f5207b7SJohn Levon 				return 1;
3101f5207b7SJohn Levon 		} END_FOR_EACH_PTR(tmp);
3111f5207b7SJohn Levon 	}
3121f5207b7SJohn Levon 	return 0;
3131f5207b7SJohn Levon }
3141f5207b7SJohn Levon 
is_char_type(struct symbol * type)3151f5207b7SJohn Levon static int is_char_type(struct symbol *type)
3161f5207b7SJohn Levon {
3171f5207b7SJohn Levon 	return type == &uchar_ctype || type == &char_ctype || type == &schar_ctype;
3181f5207b7SJohn Levon }
3191f5207b7SJohn Levon 
3201f5207b7SJohn Levon /*
3211f5207b7SJohn Levon  * I have absolutely no idea if this is how one is supposed to get the
3221f5207b7SJohn Levon  * symbol representing a typedef, but it seems to work.
3231f5207b7SJohn Levon  */
3241f5207b7SJohn Levon struct typedef_lookup {
3251f5207b7SJohn Levon 	const char *name;
3261f5207b7SJohn Levon 	struct symbol *sym;
3271f5207b7SJohn Levon 	int failed;
3281f5207b7SJohn Levon };
3291f5207b7SJohn Levon 
_typedef_lookup(const char * name)3301f5207b7SJohn Levon static struct symbol *_typedef_lookup(const char *name)
3311f5207b7SJohn Levon {
3321f5207b7SJohn Levon 	struct ident *id;
3331f5207b7SJohn Levon 	struct symbol *node;
3341f5207b7SJohn Levon 
3351f5207b7SJohn Levon 	id = built_in_ident(name);
3361f5207b7SJohn Levon 	if (!id)
3371f5207b7SJohn Levon 		return NULL;
3381f5207b7SJohn Levon 	node = lookup_symbol(id, NS_TYPEDEF);
3391f5207b7SJohn Levon 	if (!node || node->type != SYM_NODE)
3401f5207b7SJohn Levon 		return NULL;
3411f5207b7SJohn Levon 	return get_real_base_type(node);
3421f5207b7SJohn Levon }
3431f5207b7SJohn Levon 
typedef_lookup(struct typedef_lookup * tl)3441f5207b7SJohn Levon static void typedef_lookup(struct typedef_lookup *tl)
3451f5207b7SJohn Levon {
3461f5207b7SJohn Levon 	if (tl->sym || tl->failed)
3471f5207b7SJohn Levon 		return;
3481f5207b7SJohn Levon 	tl->sym = _typedef_lookup(tl->name);
3491f5207b7SJohn Levon 	if (!tl->sym) {
3501f5207b7SJohn Levon 		sm_perror(" could not find typedef '%s'", tl->name);
3511f5207b7SJohn Levon 		tl->failed = 1;
3521f5207b7SJohn Levon 	}
3531f5207b7SJohn Levon }
3541f5207b7SJohn Levon 
3551f5207b7SJohn Levon 
ip4(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)3561f5207b7SJohn Levon static void ip4(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
3571f5207b7SJohn Levon {
3581f5207b7SJohn Levon 	enum { ENDIAN_BIG, ENDIAN_LITTLE, ENDIAN_HOST } endian = ENDIAN_BIG;
3591f5207b7SJohn Levon 
3601f5207b7SJohn Levon 	assert(fmt[0] == 'i' || fmt[0] == 'I');
3611f5207b7SJohn Levon 	assert(fmt[1] == '4');
3621f5207b7SJohn Levon 
3631f5207b7SJohn Levon 	if (isalnum(fmt[2])) {
3641f5207b7SJohn Levon 		switch (fmt[2]) {
3651f5207b7SJohn Levon 		case 'h':
3661f5207b7SJohn Levon 			endian = ENDIAN_HOST;
3671f5207b7SJohn Levon 			break;
3681f5207b7SJohn Levon 		case 'l':
3691f5207b7SJohn Levon 			endian = ENDIAN_LITTLE;
3701f5207b7SJohn Levon 			break;
3711f5207b7SJohn Levon 		case 'n':
3721f5207b7SJohn Levon 		case 'b':
3731f5207b7SJohn Levon 			endian = ENDIAN_BIG;
3741f5207b7SJohn Levon 			break;
3751f5207b7SJohn Levon 		default:
3761f5207b7SJohn Levon 			sm_warning("'%%p%c4' can only be followed by one of [hnbl], not '%c'", fmt[0], fmt[2]);
3771f5207b7SJohn Levon 		}
3781f5207b7SJohn Levon 		if (isalnum(fmt[3]))
3791f5207b7SJohn Levon 			sm_warning("'%%p%c4' can only be followed by precisely one of [hnbl]", fmt[0]);
3801f5207b7SJohn Levon 	}
3811f5207b7SJohn Levon 
3821f5207b7SJohn Levon 
3831f5207b7SJohn Levon 	if (type->ctype.modifiers & MOD_NODEREF)
3841f5207b7SJohn Levon 		sm_error("passing __user pointer to '%%p%c4'", fmt[0]);
3851f5207b7SJohn Levon 
3861f5207b7SJohn Levon 	/*
3871f5207b7SJohn Levon 	 * If we have a pointer to char/u8/s8, we expect the caller to
3881f5207b7SJohn Levon 	 * handle endianness; I don't think there's anything we can
3891f5207b7SJohn Levon 	 * do. I'd like to check that if we're passed a pointer to a
3901f5207b7SJohn Levon 	 * __bitwise u32 (most likely a __be32), we should have endian
3911f5207b7SJohn Levon 	 * == ENDIAN_BIG. But I can't figure out how to get that
3921f5207b7SJohn Levon 	 * information (it also seems to require ensuring certain
3931f5207b7SJohn Levon 	 * macros are defined). But struct in_addr certainly consists
3941f5207b7SJohn Levon 	 * of only a single __be32, so in that case we can do a check.
3951f5207b7SJohn Levon 	 */
3961f5207b7SJohn Levon 	if (is_char_type(basetype))
3971f5207b7SJohn Levon 		return;
3981f5207b7SJohn Levon 
3991f5207b7SJohn Levon 	if (is_struct_tag(basetype, "in_addr") && endian != ENDIAN_BIG)
4001f5207b7SJohn Levon 		sm_warning("passing struct in_addr* to '%%p%c4%c', is the endianness ok?", fmt[0], fmt[2]);
4011f5207b7SJohn Levon 
4021f5207b7SJohn Levon 	/* ... */
4031f5207b7SJohn Levon }
4041f5207b7SJohn Levon 
ip6(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4051f5207b7SJohn Levon static void ip6(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4061f5207b7SJohn Levon {
4071f5207b7SJohn Levon 	assert(fmt[0] == 'i' || fmt[0] == 'I');
4081f5207b7SJohn Levon 	assert(fmt[1] == '6');
4091f5207b7SJohn Levon 
4101f5207b7SJohn Levon 	if (isalnum(fmt[2])) {
4111f5207b7SJohn Levon 		if (fmt[2] != 'c')
4121f5207b7SJohn Levon 			sm_warning("'%%p%c6' can only be followed by c", fmt[0]);
4131f5207b7SJohn Levon 		else if (fmt[0] == 'i')
4141f5207b7SJohn Levon 			sm_warning("'%%pi6' does not allow flag c");
4151f5207b7SJohn Levon 		if (isalnum(fmt[3]))
4161f5207b7SJohn Levon 			sm_warning("'%%p%c6%c' cannot be followed by other alphanumerics", fmt[0], fmt[2]);
4171f5207b7SJohn Levon 	}
4181f5207b7SJohn Levon 
4191f5207b7SJohn Levon 	if (type->ctype.modifiers & MOD_NODEREF)
4201f5207b7SJohn Levon 		sm_error("passing __user pointer to '%%p%c6'", fmt[0]);
4211f5207b7SJohn Levon }
4221f5207b7SJohn Levon 
ipS(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4231f5207b7SJohn Levon static void ipS(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4241f5207b7SJohn Levon {
4251f5207b7SJohn Levon 	const char *f;
4261f5207b7SJohn Levon 
4271f5207b7SJohn Levon 	assert(tolower(fmt[0]) == 'i');
4281f5207b7SJohn Levon 	assert(fmt[1] == 'S');
4291f5207b7SJohn Levon 
4301f5207b7SJohn Levon 	for (f = fmt+2; isalnum(*f); ++f) {
4311f5207b7SJohn Levon 		/* It's probably too anal checking for duplicate flags. */
4321f5207b7SJohn Levon 		if (!strchr("pfschnbl", *f))
4331f5207b7SJohn Levon 			sm_warning("'%%p%cS' cannot be followed by '%c'", fmt[0], *f);
4341f5207b7SJohn Levon 	}
4351f5207b7SJohn Levon 
4361f5207b7SJohn Levon 	/*
4371f5207b7SJohn Levon 	 * XXX: Should we also allow passing a pointer to a union, one
4381f5207b7SJohn Levon 	 * member of which is a struct sockaddr? It may be slightly
4391f5207b7SJohn Levon 	 * cleaner actually passing &u.raw instead of just &u, though
4401f5207b7SJohn Levon 	 * the generated code is of course exactly the same. For now,
4411f5207b7SJohn Levon 	 * we do accept struct sockaddr_in and struct sockaddr_in6,
4421f5207b7SJohn Levon 	 * since those are easy to handle and rather harmless.
4431f5207b7SJohn Levon 	 */
4441f5207b7SJohn Levon 	if (!has_struct_tag(basetype, "sockaddr") &&
4451f5207b7SJohn Levon 	    !has_struct_tag(basetype, "sockaddr_in") &&
4461f5207b7SJohn Levon 	    !has_struct_tag(basetype, "sockaddr_in6") &&
4471f5207b7SJohn Levon 	    !has_struct_tag(basetype, "__kernel_sockaddr_storage"))
4481f5207b7SJohn Levon 		sm_error("'%%p%cS' expects argument of type struct sockaddr *, "
4491f5207b7SJohn Levon 			"argument %d has type '%s'", fmt[0], vaidx, type_to_str(type));
4501f5207b7SJohn Levon }
4511f5207b7SJohn Levon 
hex_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4521f5207b7SJohn Levon static void hex_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4531f5207b7SJohn Levon {
4541f5207b7SJohn Levon 	assert(fmt[0] == 'h');
4551f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
4561f5207b7SJohn Levon 		if (!strchr("CDN", fmt[1]))
4571f5207b7SJohn Levon 			sm_warning("'%%ph' cannot be followed by '%c'", fmt[1]);
4581f5207b7SJohn Levon 		if (isalnum(fmt[2]))
4591f5207b7SJohn Levon 			sm_warning("'%%ph' can be followed by at most one of [CDN], and no other alphanumerics");
4601f5207b7SJohn Levon 	}
4611f5207b7SJohn Levon 	if (type->ctype.modifiers & MOD_NODEREF)
4621f5207b7SJohn Levon 		sm_error("passing __user pointer to %%ph");
4631f5207b7SJohn Levon }
4641f5207b7SJohn Levon 
escaped_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4651f5207b7SJohn Levon static void escaped_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4661f5207b7SJohn Levon {
4671f5207b7SJohn Levon 	assert(fmt[0] == 'E');
4681f5207b7SJohn Levon 	while (isalnum(*++fmt)) {
4691f5207b7SJohn Levon 		if (!strchr("achnops", *fmt))
4701f5207b7SJohn Levon 			sm_warning("%%pE can only be followed by a combination of [achnops]");
4711f5207b7SJohn Levon 	}
4721f5207b7SJohn Levon 	if (type->ctype.modifiers & MOD_NODEREF)
4731f5207b7SJohn Levon 		sm_error("passing __user pointer to %%pE");
4741f5207b7SJohn Levon }
4751f5207b7SJohn Levon 
resource_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4761f5207b7SJohn Levon static void resource_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4771f5207b7SJohn Levon {
4781f5207b7SJohn Levon 	assert(tolower(fmt[0]) == 'r');
4791f5207b7SJohn Levon 	if (!is_struct_tag(basetype, "resource")) {
4801f5207b7SJohn Levon 		sm_error("'%%p%c' expects argument of type struct resource *, "
4811f5207b7SJohn Levon 			"but argument %d has type '%s'", fmt[0], vaidx, type_to_str(type));
4821f5207b7SJohn Levon 	}
4831f5207b7SJohn Levon 	if (isalnum(fmt[1]))
4841f5207b7SJohn Levon 		sm_warning("'%%p%c' cannot be followed by '%c'", fmt[0], fmt[1]);
4851f5207b7SJohn Levon }
4861f5207b7SJohn Levon 
mac_address_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)4871f5207b7SJohn Levon static void mac_address_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
4881f5207b7SJohn Levon {
4891f5207b7SJohn Levon 	assert(tolower(fmt[0]) == 'm');
4901f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
4911f5207b7SJohn Levon 		if (!(fmt[1] == 'F' || fmt[1] == 'R'))
4921f5207b7SJohn Levon 			sm_warning("'%%p%c' cannot be followed by '%c'", fmt[0], fmt[1]);
4931f5207b7SJohn Levon 		if (fmt[0] == 'm' && fmt[1] == 'F')
4941f5207b7SJohn Levon 			sm_warning("it is pointless to pass flag F to %%pm");
4951f5207b7SJohn Levon 		if (isalnum(fmt[2]))
4961f5207b7SJohn Levon 			sm_warning("'%%p%c%c' cannot be followed by other alphanumeric", fmt[0], fmt[1]);
4971f5207b7SJohn Levon 	}
4981f5207b7SJohn Levon 	/* Technically, bdaddr_t is a typedef for an anonymous struct, but this still seems to work. */
4991f5207b7SJohn Levon 	if (!is_char_type(basetype) && !is_struct_tag(basetype, "bdaddr_t") && basetype != &void_ctype) {
5001f5207b7SJohn Levon 		sm_warning("'%%p%c' expects argument of type u8 * or bdaddr_t *, argument %d has type '%s'",
5011f5207b7SJohn Levon 			fmt[0], vaidx, type_to_str(type));
5021f5207b7SJohn Levon 	}
5031f5207b7SJohn Levon 	if (type->ctype.modifiers & MOD_NODEREF)
5041f5207b7SJohn Levon 		sm_error("passing __user pointer to '%%p%c'", fmt[0]);
5051f5207b7SJohn Levon }
5061f5207b7SJohn Levon 
dentry_file(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)5071f5207b7SJohn Levon static void dentry_file(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
5081f5207b7SJohn Levon {
5091f5207b7SJohn Levon 	const char *tag;
5101f5207b7SJohn Levon 
5111f5207b7SJohn Levon 	assert(tolower(fmt[0]) == 'd');
5121f5207b7SJohn Levon 	tag = fmt[0] == 'd' ? "dentry" : "file";
5131f5207b7SJohn Levon 
5141f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
5151f5207b7SJohn Levon 		if (!strchr("234", fmt[1]))
5161f5207b7SJohn Levon 			sm_warning("'%%p%c' can only be followed by one of [234]", fmt[0]);
5171f5207b7SJohn Levon 		if (isalnum(fmt[2]))
5181f5207b7SJohn Levon 			sm_warning("'%%p%c%c' cannot be followed by '%c'", fmt[0], fmt[1], fmt[2]);
5191f5207b7SJohn Levon 	}
5201f5207b7SJohn Levon 
5211f5207b7SJohn Levon 	if (!is_struct_tag(basetype, tag))
5221f5207b7SJohn Levon 		sm_error("'%%p%c' expects argument of type struct '%s*', argument %d has type '%s'",
5231f5207b7SJohn Levon 			fmt[0], tag, vaidx, type_to_str(type));
5241f5207b7SJohn Levon }
5251f5207b7SJohn Levon 
time_and_date(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)526efe51d0cSJohn Levon static void time_and_date(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
527efe51d0cSJohn Levon {
528efe51d0cSJohn Levon 	assert(tolower(fmt[0]) == 't');
529efe51d0cSJohn Levon 
530efe51d0cSJohn Levon 	if (fmt[1] == 'R' && !is_struct_tag(basetype, "rtc_time"))
531efe51d0cSJohn Levon 		sm_error("'%%ptR' expects argument of type struct 'rtc_time', argument %d has type '%s'",
532efe51d0cSJohn Levon 			 vaidx, type_to_str(type));
533efe51d0cSJohn Levon }
534efe51d0cSJohn Levon 
check_clock(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)5351f5207b7SJohn Levon static void check_clock(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
5361f5207b7SJohn Levon {
5371f5207b7SJohn Levon 	assert(fmt[0] == 'C');
5381f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
5391f5207b7SJohn Levon 		if (!strchr("nr", fmt[1]))
5401f5207b7SJohn Levon 			sm_warning("'%%pC' can only be followed by one of [nr]");
5411f5207b7SJohn Levon 		if (isalnum(fmt[2]))
5421f5207b7SJohn Levon 			sm_warning("'%%pC%c' cannot be followed by '%c'", fmt[1], fmt[2]);
5431f5207b7SJohn Levon 	}
5441f5207b7SJohn Levon 	if (!is_struct_tag(basetype, "clk"))
5451f5207b7SJohn Levon 		sm_error("'%%pC' expects argument of type 'struct clk*', argument %d has type '%s'",
5461f5207b7SJohn Levon 		       vaidx, type_to_str(type));
5471f5207b7SJohn Levon }
5481f5207b7SJohn Levon 
va_format(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)5491f5207b7SJohn Levon static void va_format(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
5501f5207b7SJohn Levon {
5511f5207b7SJohn Levon 	assert(fmt[0] == 'V');
5521f5207b7SJohn Levon 	if (isalnum(fmt[1]))
5531f5207b7SJohn Levon 		sm_warning("%%pV cannot be followed by any alphanumerics");
5541f5207b7SJohn Levon 	if (!is_struct_tag(basetype, "va_format"))
5551f5207b7SJohn Levon 		sm_error("%%pV expects argument of type struct va_format*, argument %d has type '%s'", vaidx, type_to_str(type));
5561f5207b7SJohn Levon }
5571f5207b7SJohn Levon 
netdev_feature(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)5581f5207b7SJohn Levon static void netdev_feature(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
5591f5207b7SJohn Levon {
5601f5207b7SJohn Levon 	static struct typedef_lookup netdev = { .name = "netdev_features_t" };
5611f5207b7SJohn Levon 
5621f5207b7SJohn Levon 	assert(fmt[0] == 'N');
5631f5207b7SJohn Levon 	if (fmt[1] != 'F') {
5641f5207b7SJohn Levon 		sm_error("%%pN must be followed by 'F'");
5651f5207b7SJohn Levon 		return;
5661f5207b7SJohn Levon 	}
5671f5207b7SJohn Levon 	if (isalnum(fmt[2]))
5681f5207b7SJohn Levon 		sm_warning("%%pNF cannot be followed by '%c'", fmt[2]);
5691f5207b7SJohn Levon 
5701f5207b7SJohn Levon 	typedef_lookup(&netdev);
5711f5207b7SJohn Levon 	if (!netdev.sym)
5721f5207b7SJohn Levon 		return;
5731f5207b7SJohn Levon 	if (basetype != netdev.sym)
5741f5207b7SJohn Levon 		sm_error("%%pNF expects argument of type netdev_features_t*, argument %d has type '%s'",
5751f5207b7SJohn Levon 			vaidx, type_to_str(type));
5761f5207b7SJohn Levon 
5771f5207b7SJohn Levon }
address_val(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)5781f5207b7SJohn Levon static void address_val(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
5791f5207b7SJohn Levon {
5801f5207b7SJohn Levon 	static struct typedef_lookup dma = { .name = "dma_addr_t" };
5811f5207b7SJohn Levon 	static struct typedef_lookup phys = { .name = "phys_addr_t" };
5821f5207b7SJohn Levon 	struct typedef_lookup *which = &phys;
5831f5207b7SJohn Levon 	const char *suf = "";
5841f5207b7SJohn Levon 	assert(fmt[0] == 'a');
5851f5207b7SJohn Levon 
5861f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
5871f5207b7SJohn Levon 		switch (fmt[1]) {
5881f5207b7SJohn Levon 		case 'd':
5891f5207b7SJohn Levon 			which = &dma;
5901f5207b7SJohn Levon 			suf = "d";
5911f5207b7SJohn Levon 			break;
5921f5207b7SJohn Levon 		case 'p':
5931f5207b7SJohn Levon 			suf = "p";
5941f5207b7SJohn Levon 			break;
5951f5207b7SJohn Levon 		default:
5961f5207b7SJohn Levon 			sm_error("'%%pa' can only be followed by one of [dp]");
5971f5207b7SJohn Levon 		}
5981f5207b7SJohn Levon 		if (isalnum(fmt[2]))
5991f5207b7SJohn Levon 			sm_error("'%%pa%c' cannot be followed by '%c'", fmt[1], fmt[2]);
6001f5207b7SJohn Levon 	}
6011f5207b7SJohn Levon 
6021f5207b7SJohn Levon 	typedef_lookup(which);
6031f5207b7SJohn Levon 	if (!which->sym)
6041f5207b7SJohn Levon 		return;
6051f5207b7SJohn Levon 	if (basetype != which->sym) {
6061f5207b7SJohn Levon 		sm_error("'%%pa%s' expects argument of type '%s*', argument %d has type '%s'",
6071f5207b7SJohn Levon 			suf, which->name, vaidx, type_to_str(type));
6081f5207b7SJohn Levon 	}
6091f5207b7SJohn Levon }
6101f5207b7SJohn Levon 
block_device(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)6111f5207b7SJohn Levon static void block_device(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
6121f5207b7SJohn Levon {
6131f5207b7SJohn Levon 	const char *tag = "block_device";
6141f5207b7SJohn Levon 
6151f5207b7SJohn Levon 	assert(fmt[0] == 'g');
6161f5207b7SJohn Levon 	if (isalnum(fmt[1])) {
6171f5207b7SJohn Levon 		sm_warning("%%pg cannot be followed by '%c'", fmt[1]);
6181f5207b7SJohn Levon 	}
6191f5207b7SJohn Levon 	if (!is_struct_tag(basetype, tag))
6201f5207b7SJohn Levon 		sm_error("'%%p%c' expects argument of type struct '%s*', argument %d has type '%s'",
6211f5207b7SJohn Levon 			fmt[0], tag, vaidx, type_to_str(type));
6221f5207b7SJohn Levon }
6231f5207b7SJohn Levon 
flag_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)6241f5207b7SJohn Levon static void flag_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
6251f5207b7SJohn Levon {
6261f5207b7SJohn Levon 	static struct typedef_lookup gfp = { .name = "gfp_t" };
6271f5207b7SJohn Levon 
6281f5207b7SJohn Levon 	assert(fmt[0] == 'G');
6291f5207b7SJohn Levon 	if (!isalnum(fmt[1])) {
6301f5207b7SJohn Levon 		sm_error("%%pG must be followed by one of [gpv]");
6311f5207b7SJohn Levon 		return;
6321f5207b7SJohn Levon 	}
6331f5207b7SJohn Levon 	switch (fmt[1]) {
6341f5207b7SJohn Levon 	case 'p':
6351f5207b7SJohn Levon 	case 'v':
6361f5207b7SJohn Levon 		if (basetype != &ulong_ctype)
6371f5207b7SJohn Levon 			sm_error("'%%pG%c' expects argument of type 'unsigned long *', argument %d has type '%s'",
6381f5207b7SJohn Levon 				fmt[1], vaidx, type_to_str(type));
6391f5207b7SJohn Levon 		break;
6401f5207b7SJohn Levon 	case 'g':
6411f5207b7SJohn Levon 		typedef_lookup(&gfp);
6421f5207b7SJohn Levon 		if (basetype != gfp.sym)
6431f5207b7SJohn Levon 			sm_error("'%%pGg' expects argument of type 'gfp_t *', argument %d has type '%s'",
6441f5207b7SJohn Levon 				vaidx, type_to_str(type));
6451f5207b7SJohn Levon 		break;
6461f5207b7SJohn Levon 	default:
6471f5207b7SJohn Levon 		sm_error("'%%pG' must be followed by one of [gpv]");
6481f5207b7SJohn Levon 	}
6491f5207b7SJohn Levon }
6501f5207b7SJohn Levon 
device_node_string(const char * fmt,struct symbol * type,struct symbol * basetype,int vaidx)6511f5207b7SJohn Levon static void device_node_string(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
6521f5207b7SJohn Levon {
6531f5207b7SJohn Levon 	if (fmt[1] != 'F') {
6541f5207b7SJohn Levon 		sm_error("%%pO can only be followed by 'F'");
6551f5207b7SJohn Levon 		return;
6561f5207b7SJohn Levon 	}
6571f5207b7SJohn Levon 	if (!is_struct_tag(basetype, "device_node"))
6581f5207b7SJohn Levon 		sm_error("'%%pOF' expects argument of type 'struct device_node*', argument %d has type '%s'",
6591f5207b7SJohn Levon 		       vaidx, type_to_str(type));
6601f5207b7SJohn Levon }
6611f5207b7SJohn Levon 
6621f5207b7SJohn Levon static void
pointer(const char * fmt,struct expression * arg,int vaidx)6631f5207b7SJohn Levon pointer(const char *fmt, struct expression *arg, int vaidx)
6641f5207b7SJohn Levon {
6651f5207b7SJohn Levon 	struct symbol *type, *basetype;
6661f5207b7SJohn Levon 
6671f5207b7SJohn Levon 	type = get_type(arg);
6681f5207b7SJohn Levon 	if (!type) {
6691f5207b7SJohn Levon 		sm_warning("could not determine type of argument %d", vaidx);
6701f5207b7SJohn Levon 		return;
6711f5207b7SJohn Levon 	}
6721f5207b7SJohn Levon 	if (!is_ptr_type(type)) {
6731f5207b7SJohn Levon 		sm_error("%%p expects pointer argument, but argument %d has type '%s'",
6741f5207b7SJohn Levon 			vaidx, type_to_str(type));
6751f5207b7SJohn Levon 		return;
6761f5207b7SJohn Levon 	}
677*b3263c98SJohn Levon 
678*b3263c98SJohn Levon 	/* error pointers */
679*b3263c98SJohn Levon 	if (*fmt == 'e')
680*b3263c98SJohn Levon 		fmt++;
681*b3263c98SJohn Levon 
682*b3263c98SJohn Levon 
6831f5207b7SJohn Levon 	/* Just plain %p, nothing to check. */
6841f5207b7SJohn Levon 	if (!isalnum(*fmt))
6851f5207b7SJohn Levon 		return;
6861f5207b7SJohn Levon 
6871f5207b7SJohn Levon 	basetype = get_real_base_type(type);
6881f5207b7SJohn Levon 	if (is_void_type(basetype))
6891f5207b7SJohn Levon 		return;
6901f5207b7SJohn Levon 	/*
6911f5207b7SJohn Levon 	 * Passing a pointer-to-array is harmless, but most likely one
6921f5207b7SJohn Levon 	 * meant to pass pointer-to-first-element. If basetype is
6931f5207b7SJohn Levon 	 * array type, we issue a notice and "dereference" the types
6941f5207b7SJohn Levon 	 * once more.
6951f5207b7SJohn Levon 	 */
6961f5207b7SJohn Levon 	if (basetype->type == SYM_ARRAY) {
6971f5207b7SJohn Levon 		spam("note: passing pointer-to-array; is the address-of redundant?");
6981f5207b7SJohn Levon 		type = basetype;
6991f5207b7SJohn Levon 		basetype = get_real_base_type(type);
7001f5207b7SJohn Levon 	}
7011f5207b7SJohn Levon 
7021f5207b7SJohn Levon 	/*
7031f5207b7SJohn Levon 	 * We pass both the type and the basetype to the helpers. If,
7041f5207b7SJohn Levon 	 * for example, the pointer is really a decayed array which is
7051f5207b7SJohn Levon 	 * passed to %pI4, we might want to check that it is in fact
7061f5207b7SJohn Levon 	 * an array of four bytes. But most are probably only
7071f5207b7SJohn Levon 	 * interested in whether the basetype makes sense. Also, the
7081f5207b7SJohn Levon 	 * pointer may carry some annotation such as __user which
7091f5207b7SJohn Levon 	 * might be worth checking in the handlers which actually
7101f5207b7SJohn Levon 	 * dereference the pointer.
7111f5207b7SJohn Levon 	 */
7121f5207b7SJohn Levon 
7131f5207b7SJohn Levon 	switch (*fmt) {
7141f5207b7SJohn Levon 	case 'b':
7151f5207b7SJohn Levon 	case 'F':
7161f5207b7SJohn Levon 	case 'f':
7171f5207b7SJohn Levon 	case 'S':
7181f5207b7SJohn Levon 	case 's':
7191f5207b7SJohn Levon 	case 'B':
7201f5207b7SJohn Levon 		/* Can we do anything sensible? Check that the arg is a function pointer, for example? */
7211f5207b7SJohn Levon 		break;
7221f5207b7SJohn Levon 
7231f5207b7SJohn Levon 	case 'R':
7241f5207b7SJohn Levon 	case 'r':
7251f5207b7SJohn Levon 		resource_string(fmt, type, basetype, vaidx);
7261f5207b7SJohn Levon 		break;
7271f5207b7SJohn Levon 	case 'M':
7281f5207b7SJohn Levon 	case 'm':
7291f5207b7SJohn Levon 		mac_address_string(fmt, type, basetype, vaidx);
7301f5207b7SJohn Levon 		break;
7311f5207b7SJohn Levon 	case 'I':
7321f5207b7SJohn Levon 	case 'i':
7331f5207b7SJohn Levon 		switch (fmt[1]) {
7341f5207b7SJohn Levon 		case '4':
7351f5207b7SJohn Levon 			ip4(fmt, type, basetype, vaidx);
7361f5207b7SJohn Levon 			break;
7371f5207b7SJohn Levon 		case '6':
7381f5207b7SJohn Levon 			ip6(fmt, type, basetype, vaidx);
7391f5207b7SJohn Levon 			break;
7401f5207b7SJohn Levon 		case 'S':
7411f5207b7SJohn Levon 			ipS(fmt, type, basetype, vaidx);
7421f5207b7SJohn Levon 			break;
7431f5207b7SJohn Levon 		default:
7441f5207b7SJohn Levon 			sm_warning("'%%p%c' must be followed by one of [46S]", fmt[0]);
7451f5207b7SJohn Levon 			break;
7461f5207b7SJohn Levon 		}
7471f5207b7SJohn Levon 		break;
7481f5207b7SJohn Levon        /*
7491f5207b7SJohn Levon 	* %pE and %ph can handle any valid pointer. We still check
7501f5207b7SJohn Levon 	* whether all the subsequent alphanumerics are valid for the
7511f5207b7SJohn Levon 	* particular %pX conversion.
7521f5207b7SJohn Levon 	*/
7531f5207b7SJohn Levon 	case 'E':
7541f5207b7SJohn Levon 		escaped_string(fmt, type, basetype, vaidx);
7551f5207b7SJohn Levon 		break;
7561f5207b7SJohn Levon 	case 'h':
7571f5207b7SJohn Levon 		hex_string(fmt, type, basetype, vaidx);
7581f5207b7SJohn Levon 		break;
7591f5207b7SJohn Levon 	case 'U': /* TODO */
7601f5207b7SJohn Levon 		break;
7611f5207b7SJohn Levon 	case 'V':
7621f5207b7SJohn Levon 		va_format(fmt, type, basetype, vaidx);
7631f5207b7SJohn Levon 		break;
7641f5207b7SJohn Levon 	case 'K': /* TODO */
7651f5207b7SJohn Levon 		break;
7661f5207b7SJohn Levon 	case 'N':
7671f5207b7SJohn Levon 		netdev_feature(fmt, type, basetype, vaidx);
7681f5207b7SJohn Levon 		break;
7691f5207b7SJohn Levon 	case 'a':
7701f5207b7SJohn Levon 		address_val(fmt, type, basetype, vaidx);
7711f5207b7SJohn Levon 		break;
7721f5207b7SJohn Levon 	case 'D':
7731f5207b7SJohn Levon 	case 'd':
7741f5207b7SJohn Levon 		dentry_file(fmt, type, basetype, vaidx);
7751f5207b7SJohn Levon 		break;
776efe51d0cSJohn Levon 	case 't':
777efe51d0cSJohn Levon 		time_and_date(fmt, type, basetype, vaidx);
778efe51d0cSJohn Levon 		break;
7791f5207b7SJohn Levon 	case 'C':
7801f5207b7SJohn Levon 		check_clock(fmt, type, basetype, vaidx);
7811f5207b7SJohn Levon 		break;
7821f5207b7SJohn Levon 	case 'g':
7831f5207b7SJohn Levon 		block_device(fmt, type, basetype, vaidx);
7841f5207b7SJohn Levon 		break;
7851f5207b7SJohn Levon 	case 'G':
7861f5207b7SJohn Levon 		flag_string(fmt, type, basetype, vaidx);
7871f5207b7SJohn Levon 		break;
7881f5207b7SJohn Levon 	case 'O':
7891f5207b7SJohn Levon 		device_node_string(fmt, type, basetype, vaidx);
7901f5207b7SJohn Levon 		break;
7911f5207b7SJohn Levon 	case 'x':
7921f5207b7SJohn Levon 		/* 'x' is for an unhashed pointer */
7931f5207b7SJohn Levon 		break;
7941f5207b7SJohn Levon 	default:
7951f5207b7SJohn Levon 		sm_error("unrecognized %%p extension '%c', treated as normal %%p", *fmt);
7961f5207b7SJohn Levon 	}
7971f5207b7SJohn Levon }
7981f5207b7SJohn Levon 
7991f5207b7SJohn Levon /*
8001f5207b7SJohn Levon  * A common error is to pass a "char" or "signed char" to %02x (or
8011f5207b7SJohn Levon  * %.2X or some other variant). This can actually be a security
8021f5207b7SJohn Levon  * problem, because a lot of code expects this to produce exactly two
8031f5207b7SJohn Levon  * characters of output. Unfortunately this also produces false
8041f5207b7SJohn Levon  * positives, since we're sometimes in arch-specific code on an arch
8051f5207b7SJohn Levon  * where char is always unsigned.
8061f5207b7SJohn Levon  */
8071f5207b7SJohn Levon static void
hexbyte(const char * fmt,int fmt_len,struct expression * arg,int vaidx,struct printf_spec spec)8081f5207b7SJohn Levon hexbyte(const char *fmt, int fmt_len, struct expression *arg, int vaidx, struct printf_spec spec)
8091f5207b7SJohn Levon {
8101f5207b7SJohn Levon 	struct symbol *type;
8111f5207b7SJohn Levon 
8121f5207b7SJohn Levon 	/*
8131f5207b7SJohn Levon 	 * For now, just check the most common and obvious, which is
8141f5207b7SJohn Levon 	 * roughly %[.0]2[xX].
8151f5207b7SJohn Levon 	 */
8161f5207b7SJohn Levon 	if (spec.field_width != 2 && spec.precision != 2)
8171f5207b7SJohn Levon 		return;
8181f5207b7SJohn Levon 	if (spec.base != 16)
8191f5207b7SJohn Levon 		return;
8201f5207b7SJohn Levon 
8211f5207b7SJohn Levon 	type = get_type(arg);
8221f5207b7SJohn Levon 	if (!type) {
8231f5207b7SJohn Levon 		sm_warning("could not determine type of argument %d", vaidx);
8241f5207b7SJohn Levon 		return;
8251f5207b7SJohn Levon 	}
8261f5207b7SJohn Levon 	if (type == &char_ctype || type == &schar_ctype)
8271f5207b7SJohn Levon 		sm_warning("argument %d to %.*s specifier has type '%s'",
8281f5207b7SJohn Levon 		       vaidx, fmt_len, fmt, type_to_str(type));
8291f5207b7SJohn Levon }
8301f5207b7SJohn Levon 
8311f5207b7SJohn Levon static int
check_format_string(const char * fmt,const char * caller)8321f5207b7SJohn Levon check_format_string(const char *fmt, const char *caller)
8331f5207b7SJohn Levon {
8341f5207b7SJohn Levon 	const char *f;
8351f5207b7SJohn Levon 
8361f5207b7SJohn Levon 	for (f = fmt; *f; ++f) {
8371f5207b7SJohn Levon 		unsigned char c = *f;
8381f5207b7SJohn Levon 		switch (c) {
8391f5207b7SJohn Levon 		case KERN_SOH_ASCII:
8401f5207b7SJohn Levon 			/*
8411f5207b7SJohn Levon 			 * This typically arises from bad conversion
8421f5207b7SJohn Levon 			 * to pr_*, e.g. pr_warn(KERN_WARNING "something").
8431f5207b7SJohn Levon 			 */
8441f5207b7SJohn Levon 			if (f != fmt)
8451f5207b7SJohn Levon 				sm_warning("KERN_* level not at start of string");
8461f5207b7SJohn Levon 			/*
8471f5207b7SJohn Levon 			 * In a very few cases, the level is actually
8481f5207b7SJohn Levon 			 * computed and passed via %c, as in KERN_SOH
8491f5207b7SJohn Levon 			 * "%c...". printk explicitly supports
8501f5207b7SJohn Levon 			 * this.
8511f5207b7SJohn Levon 			 */
8521f5207b7SJohn Levon 			if (!(('0' <= f[1] && f[1] <= '7') ||
8531f5207b7SJohn Levon 			      f[1] == 'd' || /* KERN_DEFAULT */
8541f5207b7SJohn Levon 			      f[1] == 'c' || /* KERN_CONT */
8551f5207b7SJohn Levon 			      (f[1] == '%' && f[2] == 'c')))
8561f5207b7SJohn Levon 				sm_warning("invalid KERN_* level: KERN_SOH_ASCII followed by '\\x%02x'", (unsigned char)f[1]);
8571f5207b7SJohn Levon 			break;
8581f5207b7SJohn Levon 		case '\t':
8591f5207b7SJohn Levon 		case '\n':
8601f5207b7SJohn Levon 		case '\r':
8611f5207b7SJohn Levon 		case 0x20 ... 0x7e:
8621f5207b7SJohn Levon 			break;
8631f5207b7SJohn Levon 		case 0x80 ... 0xff:
8641f5207b7SJohn Levon 			sm_warning("format string contains non-ascii character '\\x%02x'", c);
8651f5207b7SJohn Levon 			break;
8661f5207b7SJohn Levon 		case 0x08:
8671f5207b7SJohn Levon 			if (f == fmt)
8681f5207b7SJohn Levon 				break;
8691f5207b7SJohn Levon 			/* fall through */
8701f5207b7SJohn Levon 		default:
8711f5207b7SJohn Levon 			sm_warning("format string contains unusual character '\\x%02x'", c);
8721f5207b7SJohn Levon 			break;
8731f5207b7SJohn Levon 		}
8741f5207b7SJohn Levon 	}
8751f5207b7SJohn Levon 
8761f5207b7SJohn Levon 	f = strstr(fmt, caller);
8771f5207b7SJohn Levon 	if (f && strstr(f+1, caller))
8781f5207b7SJohn Levon 		sm_warning("format string contains name of enclosing function '%s' twice", caller);
8791f5207b7SJohn Levon 
8801f5207b7SJohn Levon 	return f != NULL;
8811f5207b7SJohn Levon }
8821f5207b7SJohn Levon 
arg_is___func__(struct expression * arg)8831f5207b7SJohn Levon static int arg_is___func__(struct expression *arg)
8841f5207b7SJohn Levon {
8851f5207b7SJohn Levon 	if (arg->type != EXPR_SYMBOL)
8861f5207b7SJohn Levon 		return 0;
8871f5207b7SJohn Levon 	return !strcmp(arg->symbol_name->name, "__func__") ||
8881f5207b7SJohn Levon 	       !strcmp(arg->symbol_name->name, "__FUNCTION__") ||
8891f5207b7SJohn Levon 	       !strcmp(arg->symbol_name->name, "__PRETTY_FUNCTION__");
8901f5207b7SJohn Levon }
arg_contains_caller(struct expression * arg,const char * caller)8911f5207b7SJohn Levon static int arg_contains_caller(struct expression *arg, const char *caller)
8921f5207b7SJohn Levon {
8931f5207b7SJohn Levon 	if (arg->type != EXPR_STRING)
8941f5207b7SJohn Levon 		return 0;
8951f5207b7SJohn Levon 	return strstr(arg->string->data, caller) != NULL;
8961f5207b7SJohn Levon }
8971f5207b7SJohn Levon 
is_array_of_const_char(struct symbol * sym)8981f5207b7SJohn Levon static int is_array_of_const_char(struct symbol *sym)
8991f5207b7SJohn Levon {
9001f5207b7SJohn Levon 	struct symbol *base = sym->ctype.base_type;
9011f5207b7SJohn Levon 	if (base->type != SYM_ARRAY)
9021f5207b7SJohn Levon 		return 0;
9031f5207b7SJohn Levon 	if (!(base->ctype.modifiers & MOD_CONST))
9041f5207b7SJohn Levon 		return 0;
9051f5207b7SJohn Levon 	if (!is_char_type(base->ctype.base_type)) {
9061f5207b7SJohn Levon 		spam("weird: format argument is array of const '%s'", type_to_str(base->ctype.base_type));
9071f5207b7SJohn Levon 		return 0;
9081f5207b7SJohn Levon 	}
9091f5207b7SJohn Levon 	return 1;
9101f5207b7SJohn Levon }
9111f5207b7SJohn Levon 
is_const_pointer_to_const_char(struct symbol * sym)9121f5207b7SJohn Levon static int is_const_pointer_to_const_char(struct symbol *sym)
9131f5207b7SJohn Levon {
9141f5207b7SJohn Levon 	struct symbol *base = sym->ctype.base_type;
9151f5207b7SJohn Levon 	if (!(sym->ctype.modifiers & MOD_CONST))
9161f5207b7SJohn Levon 		return 0;
9171f5207b7SJohn Levon 	if (base->type != SYM_PTR)
9181f5207b7SJohn Levon 		return 0;
9191f5207b7SJohn Levon 	if (!(base->ctype.modifiers & MOD_CONST))
9201f5207b7SJohn Levon 		return 0;
9211f5207b7SJohn Levon 	if (!is_char_type(base->ctype.base_type)) {
9221f5207b7SJohn Levon 		spam("weird: format argument is pointer to const '%s'", type_to_str(base->ctype.base_type));
9231f5207b7SJohn Levon 		return 0;
9241f5207b7SJohn Levon 	}
9251f5207b7SJohn Levon 	return 1;
9261f5207b7SJohn Levon }
9271f5207b7SJohn Levon 
unknown_format(struct expression * expr)9281f5207b7SJohn Levon static int unknown_format(struct expression *expr)
9291f5207b7SJohn Levon {
9301f5207b7SJohn Levon 	struct state_list *slist;
9311f5207b7SJohn Levon 
9321f5207b7SJohn Levon 	slist = get_strings(expr);
9331f5207b7SJohn Levon 	if (!slist)
9341f5207b7SJohn Levon 		return 1;
9351f5207b7SJohn Levon 	if (slist_has_state(slist, &undefined))
9361f5207b7SJohn Levon 		return 1;
9371f5207b7SJohn Levon 	free_slist(&slist);
9381f5207b7SJohn Levon 	return 0;
9391f5207b7SJohn Levon }
9401f5207b7SJohn Levon 
has_hex_prefix(const char * orig_fmt,const char * old_fmt)9411f5207b7SJohn Levon static bool has_hex_prefix(const char *orig_fmt, const char *old_fmt)
9421f5207b7SJohn Levon {
9431f5207b7SJohn Levon 	return old_fmt >= orig_fmt + 2 &&
9441f5207b7SJohn Levon 		old_fmt[-2] == '0' && _tolower(old_fmt[-1]) == 'x';
9451f5207b7SJohn Levon }
9461f5207b7SJohn Levon 
is_integer_specifier(int type)9471f5207b7SJohn Levon static bool is_integer_specifier(int type)
9481f5207b7SJohn Levon {
9491f5207b7SJohn Levon 	switch (type) {
9501f5207b7SJohn Levon 	case FORMAT_TYPE_LONG_LONG:
9511f5207b7SJohn Levon 	case FORMAT_TYPE_ULONG:
9521f5207b7SJohn Levon 	case FORMAT_TYPE_LONG:
9531f5207b7SJohn Levon 	case FORMAT_TYPE_UBYTE:
9541f5207b7SJohn Levon 	case FORMAT_TYPE_BYTE:
9551f5207b7SJohn Levon 	case FORMAT_TYPE_USHORT:
9561f5207b7SJohn Levon 	case FORMAT_TYPE_SHORT:
9571f5207b7SJohn Levon 	case FORMAT_TYPE_UINT:
9581f5207b7SJohn Levon 	case FORMAT_TYPE_INT:
9591f5207b7SJohn Levon 	case FORMAT_TYPE_SIZE_T:
9601f5207b7SJohn Levon 	case FORMAT_TYPE_PTRDIFF:
9611f5207b7SJohn Levon 		return true;
9621f5207b7SJohn Levon 	default:
9631f5207b7SJohn Levon 		return false;
9641f5207b7SJohn Levon 	}
9651f5207b7SJohn Levon }
9661f5207b7SJohn Levon 
9671f5207b7SJohn Levon static int
is_cast_expr(struct expression * expr)9681f5207b7SJohn Levon is_cast_expr(struct expression *expr)
9691f5207b7SJohn Levon {
9701f5207b7SJohn Levon 	if (!expr)
9711f5207b7SJohn Levon 		return 0;
9721f5207b7SJohn Levon 
9731f5207b7SJohn Levon 	switch (expr->type) {
9741f5207b7SJohn Levon 	case EXPR_CAST:
9751f5207b7SJohn Levon 	case EXPR_FORCE_CAST:
9761f5207b7SJohn Levon 		/* not EXPR_IMPLIED_CAST for our purposes */
9771f5207b7SJohn Levon 		return 1;
9781f5207b7SJohn Levon 	default:
9791f5207b7SJohn Levon 		return 0;
9801f5207b7SJohn Levon 	}
9811f5207b7SJohn Levon }
9821f5207b7SJohn Levon 
9831f5207b7SJohn Levon static void
check_cast_from_pointer(const char * fmt,int len,struct expression * arg,int va_idx)9841f5207b7SJohn Levon check_cast_from_pointer(const char *fmt, int len, struct expression *arg, int va_idx)
9851f5207b7SJohn Levon {
9861f5207b7SJohn Levon 	/*
9871f5207b7SJohn Levon 	 * This can easily be fooled by passing 0+(long)ptr or doing
9881f5207b7SJohn Levon 	 * "long local_var = (long)ptr" and passing local_var to
9891f5207b7SJohn Levon 	 * %lx. Tough.
9901f5207b7SJohn Levon 	 */
9911f5207b7SJohn Levon 	if (!is_cast_expr(arg))
9921f5207b7SJohn Levon 		return;
9931f5207b7SJohn Levon 	while (is_cast_expr(arg))
9941f5207b7SJohn Levon 		arg = arg->cast_expression;
9951f5207b7SJohn Levon 	if (is_ptr_type(get_final_type(arg)))
9961f5207b7SJohn Levon 		sm_warning("argument %d to %.*s specifier is cast from pointer",
9971f5207b7SJohn Levon 			va_idx, len, fmt);
9981f5207b7SJohn Levon }
9991f5207b7SJohn Levon 
10001f5207b7SJohn Levon static void
do_check_printf_call(const char * caller,const char * name,struct expression * callexpr,struct expression * fmtexpr,int vaidx)10011f5207b7SJohn Levon do_check_printf_call(const char *caller, const char *name, struct expression *callexpr, struct expression *fmtexpr, int vaidx)
10021f5207b7SJohn Levon {
10031f5207b7SJohn Levon 	struct printf_spec spec = {0};
10041f5207b7SJohn Levon 	const char *fmt, *orig_fmt;
10051f5207b7SJohn Levon 	int caller_in_fmt;
10061f5207b7SJohn Levon 
10071f5207b7SJohn Levon 	fmtexpr = strip_parens(fmtexpr);
10081f5207b7SJohn Levon 	if (fmtexpr->type == EXPR_CONDITIONAL) {
10091f5207b7SJohn Levon 		do_check_printf_call(caller, name, callexpr, fmtexpr->cond_true ? : fmtexpr->conditional, vaidx);
10101f5207b7SJohn Levon 		do_check_printf_call(caller, name, callexpr, fmtexpr->cond_false, vaidx);
10111f5207b7SJohn Levon 		return;
10121f5207b7SJohn Levon 	}
10131f5207b7SJohn Levon 	if (fmtexpr->type == EXPR_SYMBOL) {
10141f5207b7SJohn Levon 		/*
10151f5207b7SJohn Levon 		 * If the symbol has an initializer, we can handle
10161f5207b7SJohn Levon 		 *
10171f5207b7SJohn Levon 		 *   const char foo[] = "abc";         and
10181f5207b7SJohn Levon 		 *   const char * const foo = "abc";
10191f5207b7SJohn Levon 		 *
10201f5207b7SJohn Levon 		 * We simply replace fmtexpr with the initializer
10211f5207b7SJohn Levon 		 * expression. If foo is not one of the above, or if
10221f5207b7SJohn Levon 		 * the initializer expression is somehow not a string
10231f5207b7SJohn Levon 		 * literal, fmtexpr->type != EXPR_STRING will trigger
10241f5207b7SJohn Levon 		 * below and we'll spam+return.
10251f5207b7SJohn Levon 		 */
10261f5207b7SJohn Levon 		struct symbol *sym = fmtexpr->symbol;
10271f5207b7SJohn Levon 		if (sym && sym->initializer &&
10281f5207b7SJohn Levon 		    (is_array_of_const_char(sym) ||
10291f5207b7SJohn Levon 		     is_const_pointer_to_const_char(sym))) {
10301f5207b7SJohn Levon 			fmtexpr = strip_parens(sym->initializer);
10311f5207b7SJohn Levon 		}
10321f5207b7SJohn Levon 	}
10331f5207b7SJohn Levon 
10341f5207b7SJohn Levon 	if (fmtexpr->type != EXPR_STRING) {
10351f5207b7SJohn Levon 		if (!unknown_format(fmtexpr))
10361f5207b7SJohn Levon 			return;
10371f5207b7SJohn Levon 		/*
10381f5207b7SJohn Levon 		 * Since we're now handling both ?: and static const
10391f5207b7SJohn Levon 		 * char[] arguments, we don't get as much noise. It's
10401f5207b7SJohn Levon 		 * still spammy, though.
10411f5207b7SJohn Levon 		 */
10421f5207b7SJohn Levon 		spam("warn: call of '%s' with non-constant format argument", name);
10431f5207b7SJohn Levon 		return;
10441f5207b7SJohn Levon 	}
10451f5207b7SJohn Levon 
10461f5207b7SJohn Levon 	orig_fmt = fmt = fmtexpr->string->data;
10471f5207b7SJohn Levon 	caller_in_fmt = check_format_string(fmt, caller);
10481f5207b7SJohn Levon 
10491f5207b7SJohn Levon 	while (*fmt) {
10501f5207b7SJohn Levon 		const char *old_fmt = fmt;
10511f5207b7SJohn Levon 		int read = format_decode(fmt, &spec);
10521f5207b7SJohn Levon 		struct expression *arg;
10531f5207b7SJohn Levon 
10541f5207b7SJohn Levon 		fmt += read;
10551f5207b7SJohn Levon 		if (spec.type == FORMAT_TYPE_NONE ||
10561f5207b7SJohn Levon 		    spec.type == FORMAT_TYPE_PERCENT_CHAR)
10571f5207b7SJohn Levon 			continue;
10581f5207b7SJohn Levon 
10591f5207b7SJohn Levon 		/*
10601f5207b7SJohn Levon 		 * vaidx is currently the correct 0-based index for
10611f5207b7SJohn Levon 		 * get_argument_from_call_expr. We post-increment it
10621f5207b7SJohn Levon 		 * here so that it is the correct 1-based index for
10631f5207b7SJohn Levon 		 * all the handlers below. This of course requires
10641f5207b7SJohn Levon 		 * that we handle all FORMAT_TYPE_* things not taking
10651f5207b7SJohn Levon 		 * an argument above.
10661f5207b7SJohn Levon 		 */
10671f5207b7SJohn Levon 		arg = get_argument_from_call_expr(callexpr->args, vaidx++);
10681f5207b7SJohn Levon 
10691f5207b7SJohn Levon 		if (spec.flags & SPECIAL && has_hex_prefix(orig_fmt, old_fmt))
10701f5207b7SJohn Levon 			sm_warning("'%.2s' prefix is redundant when # flag is used", old_fmt-2);
10711f5207b7SJohn Levon 		if (is_integer_specifier(spec.type)) {
10721f5207b7SJohn Levon 			if (spec.base != 16 && has_hex_prefix(orig_fmt, old_fmt))
10731f5207b7SJohn Levon 				sm_warning("'%.2s' prefix is confusing together with '%.*s' specifier",
10741f5207b7SJohn Levon 				       old_fmt-2, (int)(fmt-old_fmt), old_fmt);
10751f5207b7SJohn Levon 
10761f5207b7SJohn Levon 			check_cast_from_pointer(old_fmt, read, arg, vaidx);
10771f5207b7SJohn Levon 		}
10781f5207b7SJohn Levon 
10791f5207b7SJohn Levon 		switch (spec.type) {
10801f5207b7SJohn Levon 		/* case FORMAT_TYPE_NONE: */
10811f5207b7SJohn Levon 		/* case FORMAT_TYPE_PERCENT_CHAR: */
10821f5207b7SJohn Levon 		/* 	break; */
10831f5207b7SJohn Levon 
10841f5207b7SJohn Levon 		case FORMAT_TYPE_INVALID:
10851f5207b7SJohn Levon 			sm_error("format specifier '%.*s' invalid", read, old_fmt);
10861f5207b7SJohn Levon 			return;
10871f5207b7SJohn Levon 
10881f5207b7SJohn Levon 		case FORMAT_TYPE_FLOAT:
10891f5207b7SJohn Levon 			sm_error("no floats in the kernel; invalid format specifier '%.*s'", read, old_fmt);
10901f5207b7SJohn Levon 			return;
10911f5207b7SJohn Levon 
10921f5207b7SJohn Levon 		case FORMAT_TYPE_NRCHARS:
10931f5207b7SJohn Levon 			sm_error("%%n not supported in kernel");
10941f5207b7SJohn Levon 			return;
10951f5207b7SJohn Levon 
10961f5207b7SJohn Levon 		case FORMAT_TYPE_WIDTH:
10971f5207b7SJohn Levon 		case FORMAT_TYPE_PRECISION:
10981f5207b7SJohn Levon 			/* check int argument */
10991f5207b7SJohn Levon 			break;
11001f5207b7SJohn Levon 
11011f5207b7SJohn Levon 		case FORMAT_TYPE_STR:
11021f5207b7SJohn Levon 			/*
11031f5207b7SJohn Levon 			 * If the format string already contains the
11041f5207b7SJohn Levon 			 * function name, it probably doesn't make
11051f5207b7SJohn Levon 			 * sense to pass __func__ as well (or rather
11061f5207b7SJohn Levon 			 * vice versa: If pr_fmt(fmt) has been defined
11071f5207b7SJohn Levon 			 * to '"%s: " fmt, __func__', it doesn't make
11081f5207b7SJohn Levon 			 * sense to use a format string containing the
11091f5207b7SJohn Levon 			 * function name).
11101f5207b7SJohn Levon 			 *
11111f5207b7SJohn Levon 			 * This produces a lot of hits. They are not
11121f5207b7SJohn Levon 			 * false positives, but it is easier to handle
11131f5207b7SJohn Levon 			 * the things which don't occur that often
11141f5207b7SJohn Levon 			 * first, so we use spam().
11151f5207b7SJohn Levon 			 */
11161f5207b7SJohn Levon 			if (caller_in_fmt) {
11171f5207b7SJohn Levon 				if (arg_is___func__(arg))
11181f5207b7SJohn Levon 					spam("warn: passing __func__ while the format string already contains the name of the function '%s'",
11191f5207b7SJohn Levon 					     caller);
11201f5207b7SJohn Levon 				else if (arg_contains_caller(arg, caller))
11211f5207b7SJohn Levon 					sm_warning("passing string constant '%s' containing '%s' which is already part of the format string",
11221f5207b7SJohn Levon 					       arg->string->data, caller);
11231f5207b7SJohn Levon 			}
11241f5207b7SJohn Levon 			break;
11251f5207b7SJohn Levon 
11261f5207b7SJohn Levon 		case FORMAT_TYPE_PTR:
11271f5207b7SJohn Levon 			/* This is the most important part: Checking %p extensions. */
11281f5207b7SJohn Levon 			pointer(fmt, arg, vaidx);
11291f5207b7SJohn Levon 			while (isalnum(*fmt))
11301f5207b7SJohn Levon 				fmt++;
11311f5207b7SJohn Levon 			break;
11321f5207b7SJohn Levon 
11331f5207b7SJohn Levon 		case FORMAT_TYPE_CHAR:
11341f5207b7SJohn Levon 
11351f5207b7SJohn Levon 		case FORMAT_TYPE_UBYTE:
11361f5207b7SJohn Levon 		case FORMAT_TYPE_BYTE:
11371f5207b7SJohn Levon 		case FORMAT_TYPE_USHORT:
11381f5207b7SJohn Levon 		case FORMAT_TYPE_SHORT:
11391f5207b7SJohn Levon 		case FORMAT_TYPE_INT:
11401f5207b7SJohn Levon 			/* argument should have integer type of width <= sizeof(int) */
11411f5207b7SJohn Levon 			break;
11421f5207b7SJohn Levon 
11431f5207b7SJohn Levon 		case FORMAT_TYPE_UINT:
11441f5207b7SJohn Levon 			hexbyte(old_fmt, fmt-old_fmt, arg, vaidx, spec);
11451f5207b7SJohn Levon 		case FORMAT_TYPE_LONG:
11461f5207b7SJohn Levon 		case FORMAT_TYPE_ULONG:
11471f5207b7SJohn Levon 		case FORMAT_TYPE_LONG_LONG:
11481f5207b7SJohn Levon 		case FORMAT_TYPE_PTRDIFF:
11491f5207b7SJohn Levon 		case FORMAT_TYPE_SIZE_T:
11501f5207b7SJohn Levon 			break;
11511f5207b7SJohn Levon 		}
11521f5207b7SJohn Levon 
11531f5207b7SJohn Levon 
11541f5207b7SJohn Levon 	}
11551f5207b7SJohn Levon 
11561f5207b7SJohn Levon 	if (get_argument_from_call_expr(callexpr->args, vaidx))
11571f5207b7SJohn Levon 		sm_warning("excess argument passed to '%s'", name);
11581f5207b7SJohn Levon 
11591f5207b7SJohn Levon 
11601f5207b7SJohn Levon }
11611f5207b7SJohn Levon 
11621f5207b7SJohn Levon static void
check_printf_call(const char * name,struct expression * callexpr,void * _info)11631f5207b7SJohn Levon check_printf_call(const char *name, struct expression *callexpr, void *_info)
11641f5207b7SJohn Levon {
11651f5207b7SJohn Levon 	/*
11661f5207b7SJohn Levon 	 * Note: attribute(printf) uses 1-based indexing, but
11671f5207b7SJohn Levon 	 * get_argument_from_call_expr() uses 0-based indexing.
11681f5207b7SJohn Levon 	 */
11691f5207b7SJohn Levon 	int info = PTR_INT(_info);
11701f5207b7SJohn Levon 	int fmtidx = (info & 0xff) - 1;
11711f5207b7SJohn Levon 	int vaidx = ((info >> 8) & 0xff) - 1;
11721f5207b7SJohn Levon 	struct expression *fmtexpr;
11731f5207b7SJohn Levon 	const char *caller = get_function();
11741f5207b7SJohn Levon 
11751f5207b7SJohn Levon 	if (!caller)
11761f5207b7SJohn Levon 		return;
11771f5207b7SJohn Levon 
11781f5207b7SJohn Levon 	/*
11791f5207b7SJohn Levon 	 * Calling a v*printf function with a literal format arg is
11801f5207b7SJohn Levon 	 * extremely rare, so we don't bother doing the only checking
11811f5207b7SJohn Levon 	 * we could do, namely checking that the format string is
11821f5207b7SJohn Levon 	 * valid.
11831f5207b7SJohn Levon 	 */
11841f5207b7SJohn Levon 	if (vaidx < 0)
11851f5207b7SJohn Levon 		return;
11861f5207b7SJohn Levon 
11871f5207b7SJohn Levon 	/*
11881f5207b7SJohn Levon 	 * For the things we use the name of the calling function for,
11891f5207b7SJohn Levon 	 * it is more appropriate to skip a potential SyS_ prefix; the
11901f5207b7SJohn Levon 	 * same goes for leading underscores.
11911f5207b7SJohn Levon 	 */
11921f5207b7SJohn Levon 	if (!strncmp(caller, "SyS_", 4))
11931f5207b7SJohn Levon 		caller += 4;
11941f5207b7SJohn Levon 	while (*caller == '_')
11951f5207b7SJohn Levon 		++caller;
11961f5207b7SJohn Levon 
11971f5207b7SJohn Levon 	/* Lack of format argument is a bug. */
11981f5207b7SJohn Levon 	fmtexpr = get_argument_from_call_expr(callexpr->args, fmtidx);
11991f5207b7SJohn Levon 	if (!fmtexpr) {
12001f5207b7SJohn Levon 		sm_error("call of '%s' with no format argument", name);
12011f5207b7SJohn Levon 		return;
12021f5207b7SJohn Levon 	}
12031f5207b7SJohn Levon 
12041f5207b7SJohn Levon 	do_check_printf_call(caller, name, callexpr, fmtexpr, vaidx);
12051f5207b7SJohn Levon }
12061f5207b7SJohn Levon 
12071f5207b7SJohn Levon 
check_kernel_printf(int id)12081f5207b7SJohn Levon void check_kernel_printf(int id)
12091f5207b7SJohn Levon {
12101f5207b7SJohn Levon 	if (option_project != PROJ_KERNEL)
12111f5207b7SJohn Levon 		return;
12121f5207b7SJohn Levon 
12131f5207b7SJohn Levon 	my_id = id;
12141f5207b7SJohn Levon 
12151f5207b7SJohn Levon #define printf_hook(func, fmt, first_to_check)	\
12161f5207b7SJohn Levon 	add_function_hook(#func, check_printf_call, INT_PTR(fmt + (first_to_check << 8)))
12171f5207b7SJohn Levon 
12181f5207b7SJohn Levon 	/* Extracted using stupid perl script. */
12191f5207b7SJohn Levon 
12201f5207b7SJohn Levon #if 0
12211f5207b7SJohn Levon 	printf_hook(srm_printk, 1, 2);                    /* arch/alpha/include/asm/console.h */
12221f5207b7SJohn Levon 	printf_hook(die_if_kernel, 1, 2);                 /* arch/frv/include/asm/bug.h */
12231f5207b7SJohn Levon 	printf_hook(ia64_mca_printk, 1, 2);               /* arch/ia64/include/asm/mca.h */
12241f5207b7SJohn Levon 	printf_hook(nfprint, 1, 2);                       /* arch/m68k/include/asm/natfeat.h */
12251f5207b7SJohn Levon 	printf_hook(gdbstub_printk, 1, 2);                /* arch/mn10300/include/asm/gdb-stub.h */
12261f5207b7SJohn Levon 	printf_hook(DBG, 1, 2);                           /* arch/powerpc/boot/ps3.c */
12271f5207b7SJohn Levon 	printf_hook(printf, 1, 2);                        /* arch/powerpc/boot/stdio.h */
12281f5207b7SJohn Levon 	printf_hook(udbg_printf, 1, 2);                   /* arch/powerpc/include/asm/udbg.h */
12291f5207b7SJohn Levon 	printf_hook(__debug_sprintf_event, 3, 4);         /* arch/s390/include/asm/debug.h */
12301f5207b7SJohn Levon 	printf_hook(__debug_sprintf_exception, 3, 4);     /* arch/s390/include/asm/debug.h */
12311f5207b7SJohn Levon 	printf_hook(prom_printf, 1, 2);                   /* arch/sparc/include/asm/oplib_32.h */
12321f5207b7SJohn Levon 
12331f5207b7SJohn Levon 	printf_hook(fail, 1, 2);                          /* arch/x86/vdso/vdso2c.c */
12341f5207b7SJohn Levon #endif
12351f5207b7SJohn Levon 
12361f5207b7SJohn Levon 	printf_hook(_ldm_printk, 3, 4);                   /* block/partitions/ldm.c */
12371f5207b7SJohn Levon 	printf_hook(rbd_warn, 2, 3);                      /* drivers/block/rbd.c */
12381f5207b7SJohn Levon 	printf_hook(fw_err, 2, 3);                        /* drivers/firewire/core.h */
12391f5207b7SJohn Levon 	printf_hook(fw_notice, 2, 3);                     /* drivers/firewire/core.h */
12401f5207b7SJohn Levon 	printf_hook(i915_error_printf, 2, 3);             /* drivers/gpu/drm/i915/i915_drv.h */
12411f5207b7SJohn Levon 	printf_hook(i915_handle_error, 3, 4);             /* drivers/gpu/drm/i915/i915_drv.h */
12421f5207b7SJohn Levon 	printf_hook(nv_printk_, 3, 4);                    /* drivers/gpu/drm/nouveau/core/include/core/printk.h */
12431f5207b7SJohn Levon 	printf_hook(host1x_debug_output, 2, 3);           /* drivers/gpu/host1x/debug.h */
12441f5207b7SJohn Levon 	printf_hook(callc_debug, 2, 3);                   /* drivers/isdn/hisax/callc.c */
12451f5207b7SJohn Levon 	printf_hook(link_debug, 3, 4);                    /* drivers/isdn/hisax/callc.c */
12461f5207b7SJohn Levon 	printf_hook(HiSax_putstatus, 3, 4);               /* drivers/isdn/hisax/hisax.h */
12471f5207b7SJohn Levon 	printf_hook(VHiSax_putstatus, 3, 0);              /* drivers/isdn/hisax/hisax.h */
12481f5207b7SJohn Levon 	printf_hook(debugl1, 2, 3);                       /* drivers/isdn/hisax/isdnl1.h */
12491f5207b7SJohn Levon 	printf_hook(l3m_debug, 2, 3);                     /* drivers/isdn/hisax/isdnl3.c */
12501f5207b7SJohn Levon 	printf_hook(dout_debug, 2, 3);                    /* drivers/isdn/hisax/st5481_d.c */
12511f5207b7SJohn Levon 	printf_hook(l1m_debug, 2, 3);                     /* drivers/isdn/hisax/st5481_d.c */
12521f5207b7SJohn Levon 	printf_hook(bch_cache_set_error, 2, 3);           /* drivers/md/bcache/bcache.h */
12531f5207b7SJohn Levon 	printf_hook(_tda_printk, 4, 5);                   /* drivers/media/tuners/tda18271-priv.h */
12541f5207b7SJohn Levon 	printf_hook(i40evf_debug_d, 3, 4);                /* drivers/net/ethernet/intel/i40evf/i40e_osdep.h */
12551f5207b7SJohn Levon 	printf_hook(en_print, 3, 4);                      /* drivers/net/ethernet/mellanox/mlx4/mlx4_en.h */
12561f5207b7SJohn Levon 	printf_hook(_ath_dbg, 3, 4);                      /* drivers/net/wireless/ath/ath.h */
12571f5207b7SJohn Levon 	printf_hook(ath_printk, 3, 4);                    /* drivers/net/wireless/ath/ath.h */
12581f5207b7SJohn Levon 	printf_hook(ath10k_dbg, 3, 4);                    /* drivers/net/wireless/ath/ath10k/debug.h */
12591f5207b7SJohn Levon 	printf_hook(ath10k_err, 2, 3);                    /* drivers/net/wireless/ath/ath10k/debug.h */
12601f5207b7SJohn Levon 	printf_hook(ath10k_info, 2, 3);                   /* drivers/net/wireless/ath/ath10k/debug.h */
12611f5207b7SJohn Levon 	printf_hook(ath10k_warn, 2, 3);                   /* drivers/net/wireless/ath/ath10k/debug.h */
12621f5207b7SJohn Levon 	printf_hook(_ath5k_printk, 3, 4);                 /* drivers/net/wireless/ath/ath5k/ath5k.h */
12631f5207b7SJohn Levon 	printf_hook(ATH5K_DBG, 3, 4);                     /* drivers/net/wireless/ath/ath5k/debug.h */
12641f5207b7SJohn Levon 	printf_hook(ATH5K_DBG_UNLIMIT, 3, 4);             /* drivers/net/wireless/ath/ath5k/debug.h */
12651f5207b7SJohn Levon 	printf_hook(ath6kl_printk, 2, 3);                 /* drivers/net/wireless/ath/ath6kl/common.h */
12661f5207b7SJohn Levon 	printf_hook(ath6kl_err, 1, 2);                    /* drivers/net/wireless/ath/ath6kl/debug.h */
12671f5207b7SJohn Levon 	printf_hook(ath6kl_info, 1, 2);                   /* drivers/net/wireless/ath/ath6kl/debug.h */
12681f5207b7SJohn Levon 	printf_hook(ath6kl_warn, 1, 2);                   /* drivers/net/wireless/ath/ath6kl/debug.h */
12691f5207b7SJohn Levon 	printf_hook(wil_dbg_trace, 2, 3);                 /* drivers/net/wireless/ath/wil6210/wil6210.h */
12701f5207b7SJohn Levon 	printf_hook(wil_err, 2, 3);                       /* drivers/net/wireless/ath/wil6210/wil6210.h */
12711f5207b7SJohn Levon 	printf_hook(wil_err_ratelimited, 2, 3);           /* drivers/net/wireless/ath/wil6210/wil6210.h */
12721f5207b7SJohn Levon 	printf_hook(wil_info, 2, 3);                      /* drivers/net/wireless/ath/wil6210/wil6210.h */
12731f5207b7SJohn Levon 	printf_hook(b43dbg, 2, 3);                        /* drivers/net/wireless/b43/b43.h */
12741f5207b7SJohn Levon 	printf_hook(b43err, 2, 3);                        /* drivers/net/wireless/b43/b43.h */
12751f5207b7SJohn Levon 	printf_hook(b43info, 2, 3);                       /* drivers/net/wireless/b43/b43.h */
12761f5207b7SJohn Levon 	printf_hook(b43warn, 2, 3);                       /* drivers/net/wireless/b43/b43.h */
12771f5207b7SJohn Levon 	printf_hook(b43legacydbg, 2, 3);                  /* drivers/net/wireless/b43legacy/b43legacy.h */
12781f5207b7SJohn Levon 	printf_hook(b43legacyerr, 2, 3);                  /* drivers/net/wireless/b43legacy/b43legacy.h */
12791f5207b7SJohn Levon 	printf_hook(b43legacyinfo, 2, 3);                 /* drivers/net/wireless/b43legacy/b43legacy.h */
12801f5207b7SJohn Levon 	printf_hook(b43legacywarn, 2, 3);                 /* drivers/net/wireless/b43legacy/b43legacy.h */
12811f5207b7SJohn Levon 	printf_hook(__brcmf_dbg, 3, 4);                   /* drivers/net/wireless/brcm80211/brcmfmac/debug.h */
12821f5207b7SJohn Levon 	printf_hook(__brcmf_err, 2, 3);                   /* drivers/net/wireless/brcm80211/brcmfmac/debug.h */
12831f5207b7SJohn Levon 	printf_hook(__brcms_crit, 2, 3);                  /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */
12841f5207b7SJohn Levon 	printf_hook(__brcms_dbg, 4, 5);                   /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */
12851f5207b7SJohn Levon 	printf_hook(__brcms_err, 2, 3);                   /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */
12861f5207b7SJohn Levon 	printf_hook(__brcms_info, 2, 3);                  /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */
12871f5207b7SJohn Levon 	printf_hook(__brcms_warn, 2, 3);                  /* drivers/net/wireless/brcm80211/brcmsmac/debug.h */
12881f5207b7SJohn Levon 	printf_hook(brcmu_dbg_hex_dump, 3, 4);            /* drivers/net/wireless/brcm80211/include/brcmu_utils.h */
12891f5207b7SJohn Levon 	printf_hook(__iwl_crit, 2, 3);                    /* drivers/net/wireless/iwlwifi/iwl-debug.h */
12901f5207b7SJohn Levon 	printf_hook(__iwl_dbg, 5, 6);                     /* drivers/net/wireless/iwlwifi/iwl-debug.h */
12911f5207b7SJohn Levon 	printf_hook(__iwl_err, 4, 5);                     /* drivers/net/wireless/iwlwifi/iwl-debug.h */
12921f5207b7SJohn Levon 	printf_hook(__iwl_info, 2, 3);                    /* drivers/net/wireless/iwlwifi/iwl-debug.h */
12931f5207b7SJohn Levon 	printf_hook(__iwl_warn, 2, 3);                    /* drivers/net/wireless/iwlwifi/iwl-debug.h */
12941f5207b7SJohn Levon 	printf_hook(rsi_dbg, 2, 3);                       /* drivers/net/wireless/rsi/rsi_main.h */
12951f5207b7SJohn Levon 	printf_hook(RTPRINT, 4, 5);                       /* drivers/net/wireless/rtlwifi/debug.h */
12961f5207b7SJohn Levon 	printf_hook(RT_ASSERT, 2, 3);                     /* drivers/net/wireless/rtlwifi/debug.h */
12971f5207b7SJohn Levon 	printf_hook(RT_TRACE, 4, 5);                      /* drivers/net/wireless/rtlwifi/debug.h */
12981f5207b7SJohn Levon 	printf_hook(__of_node_dup, 2, 3);                 /* drivers/of/of_private.h */
12991f5207b7SJohn Levon 	printf_hook(BNX2FC_HBA_DBG, 2, 3);                /* drivers/scsi/bnx2fc/bnx2fc_debug.h */
13001f5207b7SJohn Levon 	printf_hook(BNX2FC_IO_DBG, 2, 3);                 /* drivers/scsi/bnx2fc/bnx2fc_debug.h */
13011f5207b7SJohn Levon 	printf_hook(BNX2FC_TGT_DBG, 2, 3);                /* drivers/scsi/bnx2fc/bnx2fc_debug.h */
13021f5207b7SJohn Levon 	printf_hook(ql_dbg, 4, 5);                        /* drivers/scsi/qla2xxx/qla_dbg.h */
13031f5207b7SJohn Levon 	printf_hook(ql_dbg_pci, 4, 5);                    /* drivers/scsi/qla2xxx/qla_dbg.h */
13041f5207b7SJohn Levon 	printf_hook(ql_log, 4, 5);                        /* drivers/scsi/qla2xxx/qla_dbg.h */
13051f5207b7SJohn Levon 	printf_hook(ql_log_pci, 4, 5);                    /* drivers/scsi/qla2xxx/qla_dbg.h */
13061f5207b7SJohn Levon 	printf_hook(libcfs_debug_msg, 2, 3);              /* drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h */
13071f5207b7SJohn Levon 	printf_hook(libcfs_debug_vmsg2, 4, 5);            /* drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h */
13081f5207b7SJohn Levon 	printf_hook(_ldlm_lock_debug, 3, 4);              /* drivers/staging/lustre/lustre/include/lustre_dlm.h */
13091f5207b7SJohn Levon 	printf_hook(_debug_req, 3, 4);                    /* drivers/staging/lustre/lustre/include/lustre_net.h */
13101f5207b7SJohn Levon 	printf_hook(iscsi_change_param_sprintf, 2, 3);    /* drivers/target/iscsi/iscsi_target_login.c */
13111f5207b7SJohn Levon 	printf_hook(dbg, 1, 2);                           /* drivers/tty/serial/samsung.c */
13121f5207b7SJohn Levon 	printf_hook(_usb_stor_dbg, 2, 3);                 /* drivers/usb/storage/debug.h */
13131f5207b7SJohn Levon 	printf_hook(usb_stor_dbg, 2, 3);                  /* drivers/usb/storage/debug.h */
13141f5207b7SJohn Levon 	printf_hook(vringh_bad, 1, 2);                    /* drivers/vhost/vringh.c */
13151f5207b7SJohn Levon 	printf_hook(__adfs_error, 3, 4);                  /* fs/adfs/adfs.h */
13161f5207b7SJohn Levon 	printf_hook(affs_error, 3, 4);                    /* fs/affs/affs.h */
13171f5207b7SJohn Levon 	printf_hook(affs_warning, 3, 4);                  /* fs/affs/affs.h */
13181f5207b7SJohn Levon 	printf_hook(befs_debug, 2, 3);                    /* fs/befs/befs.h */
13191f5207b7SJohn Levon 	printf_hook(befs_error, 2, 3);                    /* fs/befs/befs.h */
13201f5207b7SJohn Levon 	printf_hook(befs_warning, 2, 3);                  /* fs/befs/befs.h */
13211f5207b7SJohn Levon 	printf_hook(__btrfs_panic, 5, 6);                 /* fs/btrfs/ctree.h */
13221f5207b7SJohn Levon 	printf_hook(__btrfs_std_error, 5, 6);             /* fs/btrfs/ctree.h */
13231f5207b7SJohn Levon 	printf_hook(btrfs_printk, 2, 3);                  /* fs/btrfs/ctree.h */
13241f5207b7SJohn Levon 	printf_hook(cifs_vfs_err, 1, 2);                  /* fs/cifs/cifs_debug.h */
13251f5207b7SJohn Levon 	printf_hook(__ecryptfs_printk, 1, 2);             /* fs/ecryptfs/ecryptfs_kernel.h */
13261f5207b7SJohn Levon 	printf_hook(ext2_error, 3, 4);                    /* fs/ext2/ext2.h */
13271f5207b7SJohn Levon 	printf_hook(ext2_msg, 3, 4);                      /* fs/ext2/ext2.h */
13281f5207b7SJohn Levon 	printf_hook(ext3_abort, 3, 4);                    /* fs/ext3/ext3.h */
13291f5207b7SJohn Levon 	printf_hook(ext3_error, 3, 4);                    /* fs/ext3/ext3.h */
13301f5207b7SJohn Levon 	printf_hook(ext3_msg, 3, 4);                      /* fs/ext3/ext3.h */
13311f5207b7SJohn Levon 	printf_hook(ext3_warning, 3, 4);                  /* fs/ext3/ext3.h */
13321f5207b7SJohn Levon 	printf_hook(__ext4_abort, 4, 5);                  /* fs/ext4/ext4.h */
13331f5207b7SJohn Levon 	printf_hook(__ext4_error, 4, 5);                  /* fs/ext4/ext4.h */
13341f5207b7SJohn Levon 	printf_hook(__ext4_error_file, 5, 6);             /* fs/ext4/ext4.h */
13351f5207b7SJohn Levon 	printf_hook(__ext4_error_inode, 5, 6);            /* fs/ext4/ext4.h */
13361f5207b7SJohn Levon 	printf_hook(__ext4_grp_locked_error, 7, 8);       /* fs/ext4/ext4.h */
13371f5207b7SJohn Levon 	printf_hook(__ext4_msg, 3, 4);                    /* fs/ext4/ext4.h */
13381f5207b7SJohn Levon 	printf_hook(__ext4_warning, 4, 5);                /* fs/ext4/ext4.h */
13391f5207b7SJohn Levon 	printf_hook(f2fs_msg, 3, 4);                      /* fs/f2fs/f2fs.h */
13401f5207b7SJohn Levon 	printf_hook(__fat_fs_error, 3, 4);                /* fs/fat/fat.h */
13411f5207b7SJohn Levon 	printf_hook(fat_msg, 3, 4);                       /* fs/fat/fat.h */
13421f5207b7SJohn Levon 	printf_hook(gfs2_print_dbg, 2, 3);                /* fs/gfs2/glock.h */
13431f5207b7SJohn Levon 	printf_hook(gfs2_lm_withdraw, 2, 3);              /* fs/gfs2/util.h */
13441f5207b7SJohn Levon 	printf_hook(hpfs_error, 2, 3);                    /* fs/hpfs/hpfs_fn.h */
13451f5207b7SJohn Levon 	printf_hook(jfs_error, 2, 3);                     /* fs/jfs/jfs_superblock.h */
13461f5207b7SJohn Levon 	printf_hook(nilfs_error, 3, 4);                   /* fs/nilfs2/nilfs.h */
13471f5207b7SJohn Levon 	printf_hook(nilfs_warning, 3, 4);                 /* fs/nilfs2/nilfs.h */
13481f5207b7SJohn Levon 	printf_hook(__ntfs_debug, 4, 5);                  /* fs/ntfs/debug.h */
13491f5207b7SJohn Levon 	printf_hook(__ntfs_error, 3, 4);                  /* fs/ntfs/debug.h */
13501f5207b7SJohn Levon 	printf_hook(__ntfs_warning, 3, 4);                /* fs/ntfs/debug.h */
13511f5207b7SJohn Levon 	printf_hook(__ocfs2_abort, 3, 4);                 /* fs/ocfs2/super.h */
13521f5207b7SJohn Levon 	printf_hook(__ocfs2_error, 3, 4);                 /* fs/ocfs2/super.h */
13531f5207b7SJohn Levon 	printf_hook(_udf_err, 3, 4);                      /* fs/udf/udfdecl.h */
13541f5207b7SJohn Levon 	printf_hook(_udf_warn, 3, 4);                     /* fs/udf/udfdecl.h */
13551f5207b7SJohn Levon 	printf_hook(ufs_error, 3, 4);                     /* fs/ufs/ufs.h */
13561f5207b7SJohn Levon 	printf_hook(ufs_panic, 3, 4);                     /* fs/ufs/ufs.h */
13571f5207b7SJohn Levon 	printf_hook(ufs_warning, 3, 4);                   /* fs/ufs/ufs.h */
13581f5207b7SJohn Levon 	printf_hook(xfs_alert, 2, 3);                     /* fs/xfs/xfs_message.h */
13591f5207b7SJohn Levon 	printf_hook(xfs_alert_tag, 3, 4);                 /* fs/xfs/xfs_message.h */
13601f5207b7SJohn Levon 	printf_hook(xfs_crit, 2, 3);                      /* fs/xfs/xfs_message.h */
13611f5207b7SJohn Levon 	printf_hook(xfs_debug, 2, 3);                     /* fs/xfs/xfs_message.h */
13621f5207b7SJohn Levon 	printf_hook(xfs_emerg, 2, 3);                     /* fs/xfs/xfs_message.h */
13631f5207b7SJohn Levon 	printf_hook(xfs_err, 2, 3);                       /* fs/xfs/xfs_message.h */
13641f5207b7SJohn Levon 	printf_hook(xfs_info, 2, 3);                      /* fs/xfs/xfs_message.h */
13651f5207b7SJohn Levon 	printf_hook(xfs_notice, 2, 3);                    /* fs/xfs/xfs_message.h */
13661f5207b7SJohn Levon 	printf_hook(xfs_warn, 2, 3);                      /* fs/xfs/xfs_message.h */
13671f5207b7SJohn Levon 	printf_hook(warn_slowpath_fmt, 3, 4);             /* include/asm-generic/bug.h */
13681f5207b7SJohn Levon 	printf_hook(warn_slowpath_fmt_taint, 4, 5);       /* include/asm-generic/bug.h */
13691f5207b7SJohn Levon 	printf_hook(drm_err, 1, 2);                       /* include/drm/drmP.h */
13701f5207b7SJohn Levon 	printf_hook(drm_ut_debug_printk, 2, 3);           /* include/drm/drmP.h */
13711f5207b7SJohn Levon 	printf_hook(__acpi_handle_debug, 3, 4);           /* include/linux/acpi.h */
13721f5207b7SJohn Levon 	printf_hook(acpi_handle_printk, 3, 4);            /* include/linux/acpi.h */
13731f5207b7SJohn Levon 	printf_hook(audit_log, 4, 5);                     /* include/linux/audit.h */
13741f5207b7SJohn Levon 	printf_hook(audit_log_format, 2, 3);              /* include/linux/audit.h */
13751f5207b7SJohn Levon 	printf_hook(bdi_register, 3, 4);                  /* include/linux/backing-dev.h */
13761f5207b7SJohn Levon 	printf_hook(__trace_note_message, 2, 3);          /* include/linux/blktrace_api.h */
13771f5207b7SJohn Levon 	printf_hook(_dev_info, 2, 3);                     /* include/linux/device.h */
13781f5207b7SJohn Levon 	printf_hook(dev_alert, 2, 3);                     /* include/linux/device.h */
13791f5207b7SJohn Levon 	printf_hook(dev_crit, 2, 3);                      /* include/linux/device.h */
13801f5207b7SJohn Levon 	printf_hook(dev_emerg, 2, 3);                     /* include/linux/device.h */
13811f5207b7SJohn Levon 	printf_hook(dev_err, 2, 3);                       /* include/linux/device.h */
13821f5207b7SJohn Levon 	printf_hook(dev_notice, 2, 3);                    /* include/linux/device.h */
13831f5207b7SJohn Levon 	printf_hook(dev_printk, 3, 4);                    /* include/linux/device.h */
13841f5207b7SJohn Levon 	printf_hook(dev_printk_emit, 3, 4);               /* include/linux/device.h */
13851f5207b7SJohn Levon 	printf_hook(dev_set_name, 2, 3);                  /* include/linux/device.h */
13861f5207b7SJohn Levon 	printf_hook(dev_vprintk_emit, 3, 0);              /* include/linux/device.h */
13871f5207b7SJohn Levon 	printf_hook(dev_warn, 2, 3);                      /* include/linux/device.h */
13881f5207b7SJohn Levon 	printf_hook(device_create, 5, 6);                 /* include/linux/device.h */
13891f5207b7SJohn Levon 	printf_hook(device_create_with_groups, 6, 7);     /* include/linux/device.h */
13901f5207b7SJohn Levon 	printf_hook(devm_kasprintf, 3, 4);                /* include/linux/device.h */
13911f5207b7SJohn Levon 	printf_hook(__dynamic_dev_dbg, 3, 4);             /* include/linux/dynamic_debug.h */
13921f5207b7SJohn Levon 	printf_hook(__dynamic_netdev_dbg, 3, 4);          /* include/linux/dynamic_debug.h */
13931f5207b7SJohn Levon 	printf_hook(__dynamic_pr_debug, 2, 3);            /* include/linux/dynamic_debug.h */
13941f5207b7SJohn Levon 	printf_hook(__simple_attr_check_format, 1, 2);    /* include/linux/fs.h */
13951f5207b7SJohn Levon 	printf_hook(fscache_init_cache, 3, 4);            /* include/linux/fscache-cache.h */
13961f5207b7SJohn Levon 	printf_hook(gameport_set_phys, 2, 3);             /* include/linux/gameport.h */
13971f5207b7SJohn Levon 	printf_hook(iio_trigger_alloc, 1, 2);             /* include/linux/iio/trigger.h */
13981f5207b7SJohn Levon 	printf_hook(__check_printsym_format, 1, 2);       /* include/linux/kallsyms.h */
13991f5207b7SJohn Levon 	printf_hook(kdb_printf, 1, 2);                    /* include/linux/kdb.h */
14001f5207b7SJohn Levon 	printf_hook(vkdb_printf, 1, 0);                   /* include/linux/kdb.h */
14011f5207b7SJohn Levon 	printf_hook(____trace_printk_check_format, 1, 2);  /* include/linux/kernel.h */
14021f5207b7SJohn Levon 	printf_hook(__trace_bprintk, 2, 3);               /* include/linux/kernel.h */
14031f5207b7SJohn Levon 	printf_hook(__trace_printk, 2, 3);                /* include/linux/kernel.h */
14041f5207b7SJohn Levon 	printf_hook(kasprintf, 2, 3);                     /* include/linux/kernel.h */
14051f5207b7SJohn Levon 	printf_hook(panic, 1, 2);                         /* include/linux/kernel.h */
14061f5207b7SJohn Levon 	printf_hook(scnprintf, 3, 4);                     /* include/linux/kernel.h */
14071f5207b7SJohn Levon 	printf_hook(snprintf, 3, 4);                      /* include/linux/kernel.h */
14081f5207b7SJohn Levon 	printf_hook(sprintf, 2, 3);                       /* include/linux/kernel.h */
14091f5207b7SJohn Levon 	printf_hook(trace_printk, 1, 2);                  /* include/linux/kernel.h */
14101f5207b7SJohn Levon 	printf_hook(vscnprintf, 3, 0);                    /* include/linux/kernel.h */
14111f5207b7SJohn Levon 	printf_hook(vsnprintf, 3, 0);                     /* include/linux/kernel.h */
14121f5207b7SJohn Levon 	printf_hook(vsprintf, 2, 0);                      /* include/linux/kernel.h */
14131f5207b7SJohn Levon 	printf_hook(vmcoreinfo_append_str, 1, 2);         /* include/linux/kexec.h */
14141f5207b7SJohn Levon 	printf_hook(__request_module, 2, 3);              /* include/linux/kmod.h */
14151f5207b7SJohn Levon 	printf_hook(add_uevent_var, 2, 3);                /* include/linux/kobject.h */
14161f5207b7SJohn Levon 	printf_hook(kobject_add, 3, 4);                   /* include/linux/kobject.h */
14171f5207b7SJohn Levon 	printf_hook(kobject_init_and_add, 4, 5);          /* include/linux/kobject.h */
14181f5207b7SJohn Levon 	printf_hook(kobject_set_name, 2, 3);              /* include/linux/kobject.h */
14191f5207b7SJohn Levon 	printf_hook(kthread_create_on_node, 4, 5);        /* include/linux/kthread.h */
14201f5207b7SJohn Levon 	printf_hook(__ata_ehi_push_desc, 2, 3);           /* include/linux/libata.h */
14211f5207b7SJohn Levon 	printf_hook(ata_dev_printk, 3, 4);                /* include/linux/libata.h */
14221f5207b7SJohn Levon 	printf_hook(ata_ehi_push_desc, 2, 3);             /* include/linux/libata.h */
14231f5207b7SJohn Levon 	printf_hook(ata_link_printk, 3, 4);               /* include/linux/libata.h */
14241f5207b7SJohn Levon 	printf_hook(ata_port_desc, 2, 3);                 /* include/linux/libata.h */
14251f5207b7SJohn Levon 	printf_hook(ata_port_printk, 3, 4);               /* include/linux/libata.h */
14261f5207b7SJohn Levon 	printf_hook(warn_alloc_failed, 3, 4);             /* include/linux/mm.h */
14271f5207b7SJohn Levon 	printf_hook(mmiotrace_printk, 1, 2);              /* include/linux/mmiotrace.h */
14281f5207b7SJohn Levon 	printf_hook(netdev_alert, 2, 3);                  /* include/linux/netdevice.h */
14291f5207b7SJohn Levon 	printf_hook(netdev_crit, 2, 3);                   /* include/linux/netdevice.h */
14301f5207b7SJohn Levon 	printf_hook(netdev_emerg, 2, 3);                  /* include/linux/netdevice.h */
14311f5207b7SJohn Levon 	printf_hook(netdev_err, 2, 3);                    /* include/linux/netdevice.h */
14321f5207b7SJohn Levon 	printf_hook(netdev_info, 2, 3);                   /* include/linux/netdevice.h */
14331f5207b7SJohn Levon 	printf_hook(netdev_notice, 2, 3);                 /* include/linux/netdevice.h */
14341f5207b7SJohn Levon 	printf_hook(netdev_printk, 3, 4);                 /* include/linux/netdevice.h */
14351f5207b7SJohn Levon 	printf_hook(netdev_warn, 2, 3);                   /* include/linux/netdevice.h */
14361f5207b7SJohn Levon 	printf_hook(early_printk, 1, 2);                  /* include/linux/printk.h */
14371f5207b7SJohn Levon 	printf_hook(no_printk, 1, 2);                     /* include/linux/printk.h */
14381f5207b7SJohn Levon 	printf_hook(printk, 1, 2);                        /* include/linux/printk.h */
14391f5207b7SJohn Levon 	printf_hook(printk_deferred, 1, 2);               /* include/linux/printk.h */
14401f5207b7SJohn Levon 	printf_hook(printk_emit, 5, 6);                   /* include/linux/printk.h */
14411f5207b7SJohn Levon 	printf_hook(vprintk, 1, 0);                       /* include/linux/printk.h */
14421f5207b7SJohn Levon 	printf_hook(vprintk_emit, 5, 0);                  /* include/linux/printk.h */
14431f5207b7SJohn Levon 	printf_hook(__quota_error, 3, 4);                 /* include/linux/quotaops.h */
14441f5207b7SJohn Levon 	printf_hook(seq_buf_printf, 2, 3);                /* include/linux/seq_buf.h */
14451f5207b7SJohn Levon 	printf_hook(seq_buf_vprintf, 2, 0);               /* include/linux/seq_buf.h */
14461f5207b7SJohn Levon 	printf_hook(seq_printf, 2, 3);                    /* include/linux/seq_file.h */
14471f5207b7SJohn Levon 	printf_hook(seq_vprintf, 2, 0);                   /* include/linux/seq_file.h */
14481f5207b7SJohn Levon 	printf_hook(bprintf, 3, 4);                       /* include/linux/string.h */
14491f5207b7SJohn Levon 	printf_hook(trace_seq_printf, 2, 3);              /* include/linux/trace_seq.h */
14501f5207b7SJohn Levon 	printf_hook(trace_seq_vprintf, 2, 0);             /* include/linux/trace_seq.h */
14511f5207b7SJohn Levon 	printf_hook(__alloc_workqueue_key, 1, 6);         /* include/linux/workqueue.h */
14521f5207b7SJohn Levon 	printf_hook(set_worker_desc, 1, 2);               /* include/linux/workqueue.h */
14531f5207b7SJohn Levon 	printf_hook(_p9_debug, 3, 4);                     /* include/net/9p/9p.h */
14541f5207b7SJohn Levon 	printf_hook(bt_err, 1, 2);                        /* include/net/bluetooth/bluetooth.h */
14551f5207b7SJohn Levon 	printf_hook(bt_info, 1, 2);                       /* include/net/bluetooth/bluetooth.h */
14561f5207b7SJohn Levon 	printf_hook(nf_ct_helper_log, 3, 4);              /* include/net/netfilter/nf_conntrack_helper.h */
14571f5207b7SJohn Levon 	printf_hook(nf_log_buf_add, 2, 3);                /* include/net/netfilter/nf_log.h */
14581f5207b7SJohn Levon 	printf_hook(nf_log_packet, 8, 9);                 /* include/net/netfilter/nf_log.h */
14591f5207b7SJohn Levon 	printf_hook(SOCK_DEBUG, 2, 3);                    /* include/net/sock.h */
14601f5207b7SJohn Levon 	printf_hook(__snd_printk, 4, 5);                  /* include/sound/core.h */
14611f5207b7SJohn Levon 	printf_hook(_snd_printd, 2, 3);                   /* include/sound/core.h */
14621f5207b7SJohn Levon 	printf_hook(snd_printd, 1, 2);                    /* include/sound/core.h */
14631f5207b7SJohn Levon 	printf_hook(snd_printdd, 1, 2);                   /* include/sound/core.h */
14641f5207b7SJohn Levon 	printf_hook(snd_iprintf, 2, 3);                   /* include/sound/info.h */
14651f5207b7SJohn Levon 	printf_hook(snd_seq_create_kernel_client, 3, 4);  /* include/sound/seq_kernel.h */
14661f5207b7SJohn Levon 	printf_hook(xen_raw_printk, 1, 2);                /* include/xen/hvc-console.h */
14671f5207b7SJohn Levon 	printf_hook(xenbus_dev_error, 3, 4);              /* include/xen/xenbus.h */
14681f5207b7SJohn Levon 	printf_hook(xenbus_dev_fatal, 3, 4);              /* include/xen/xenbus.h */
14691f5207b7SJohn Levon 	printf_hook(xenbus_printf, 4, 5);                 /* include/xen/xenbus.h */
14701f5207b7SJohn Levon 	printf_hook(xenbus_watch_pathfmt, 4, 5);          /* include/xen/xenbus.h */
14711f5207b7SJohn Levon 	printf_hook(batadv_fdebug_log, 2, 3);             /* net/batman-adv/debugfs.c */
14721f5207b7SJohn Levon 	printf_hook(_batadv_dbg, 4, 5);                   /* net/batman-adv/main.h */
14731f5207b7SJohn Levon 	printf_hook(batadv_debug_log, 2, 3);              /* net/batman-adv/main.h */
14741f5207b7SJohn Levon 	printf_hook(__sdata_dbg, 2, 3);                   /* net/mac80211/debug.h */
14751f5207b7SJohn Levon 	printf_hook(__sdata_err, 1, 2);                   /* net/mac80211/debug.h */
14761f5207b7SJohn Levon 	printf_hook(__sdata_info, 1, 2);                  /* net/mac80211/debug.h */
14771f5207b7SJohn Levon 	printf_hook(__wiphy_dbg, 3, 4);                   /* net/mac80211/debug.h */
14781f5207b7SJohn Levon 	printf_hook(mac80211_format_buffer, 4, 5);        /* net/mac80211/debugfs.h */
14791f5207b7SJohn Levon 	printf_hook(__rds_conn_error, 2, 3);              /* net/rds/rds.h */
14801f5207b7SJohn Levon 	printf_hook(rdsdebug, 1, 2);                      /* net/rds/rds.h */
14811f5207b7SJohn Levon 	printf_hook(printl, 1, 2);                        /* net/sctp/probe.c */
14821f5207b7SJohn Levon 	printf_hook(svc_printk, 2, 3);                    /* net/sunrpc/svc.c */
14831f5207b7SJohn Levon 	printf_hook(tomoyo_io_printf, 2, 3);              /* security/tomoyo/common.c */
14841f5207b7SJohn Levon 	printf_hook(tomoyo_supervisor, 2, 3);             /* security/tomoyo/common.h */
14851f5207b7SJohn Levon 	printf_hook(tomoyo_write_log, 2, 3);              /* security/tomoyo/common.h */
14861f5207b7SJohn Levon 	printf_hook(cmp_error, 2, 3);                     /* sound/firewire/cmp.c */
14871f5207b7SJohn Levon }
1488