815 lines · 30.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Test cases for sscanf facility. */ #include <kunit/test.h>#include <linux/bitops.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/overflow.h>#include <linux/prandom.h>#include <linux/slab.h>#include <linux/string.h> #define BUF_SIZE 1024 static char *test_buffer;static char *fmt_buffer;static struct rnd_state rnd_state; typedef void (*check_fn)(struct kunit *test, const char *file, const int line,			 const void *check_data, const char *string, const char *fmt, int n_args,			 va_list ap); static void __scanf(7, 9)_test(struct kunit *test, const char *file, const int line, check_fn fn, const void *check_data,	const char *string, const char *fmt, int n_args, ...){	va_list ap, ap_copy;	int ret; 	va_start(ap, n_args);	va_copy(ap_copy, ap);	ret = vsscanf(string, fmt, ap_copy);	va_end(ap_copy); 	if (ret != n_args) {		KUNIT_FAIL(test, "%s:%d: vsscanf(\"%s\", \"%s\", ...) returned %d expected %d",			   file, line, string, fmt, ret, n_args);	} else {		(*fn)(test, file, line, check_data, string, fmt, n_args, ap);	} 	va_end(ap);} #define _check_numbers_template(arg_fmt, expect, str, fmt, n_args, ap)		\do {										\	for (; n_args > 0; n_args--, expect++) {				\		typeof(*expect) got = *va_arg(ap, typeof(expect));		\		if (got != *expect) {						\			KUNIT_FAIL(test,					\				   "%s:%d: vsscanf(\"%s\", \"%s\", ...) expected " arg_fmt " got " arg_fmt, \				   file, line, str, fmt, *expect, got);		\			return;							\		}								\	}									\} while (0) static void check_ull(struct kunit *test, const char *file, const int line, const void *check_data,		      const char *string, const char *fmt, int n_args, va_list ap){	const unsigned long long *pval = check_data; 	_check_numbers_template("%llu", pval, string, fmt, n_args, ap);} static void check_ll(struct kunit *test, const char *file, const int line, const void *check_data,		     const char *string, const char *fmt, int n_args, va_list ap){	const long long *pval = check_data; 	_check_numbers_template("%lld", pval, string, fmt, n_args, ap);} static void check_ulong(struct kunit *test, const char *file, const int line,			const void *check_data, const char *string, const char *fmt, int n_args,			va_list ap){	const unsigned long *pval = check_data; 	_check_numbers_template("%lu", pval, string, fmt, n_args, ap);} static void check_long(struct kunit *test, const char *file, const int line, const void *check_data,		       const char *string, const char *fmt, int n_args, va_list ap){	const long *pval = check_data; 	_check_numbers_template("%ld", pval, string, fmt, n_args, ap);} static void check_uint(struct kunit *test, const char *file, const int line, const void *check_data,		       const char *string, const char *fmt, int n_args, va_list ap){	const unsigned int *pval = check_data; 	_check_numbers_template("%u", pval, string, fmt, n_args, ap);} static void check_int(struct kunit *test, const char *file, const int line, const void *check_data,		      const char *string, const char *fmt, int n_args, va_list ap){	const int *pval = check_data; 	_check_numbers_template("%d", pval, string, fmt, n_args, ap);} static void check_ushort(struct kunit *test, const char *file, const int line,			 const void *check_data, const char *string, const char *fmt, int n_args,			 va_list ap){	const unsigned short *pval = check_data; 	_check_numbers_template("%hu", pval, string, fmt, n_args, ap);} static void check_short(struct kunit *test, const char *file, const int line,			const void *check_data, const char *string, const char *fmt, int n_args,			va_list ap){	const short *pval = check_data; 	_check_numbers_template("%hd", pval, string, fmt, n_args, ap);} static void check_uchar(struct kunit *test, const char *file, const int line,			const void *check_data, const char *string, const char *fmt, int n_args,			va_list ap){	const unsigned char *pval = check_data; 	_check_numbers_template("%hhu", pval, string, fmt, n_args, ap);} static void check_char(struct kunit *test, const char *file, const int line, const void *check_data,		       const char *string, const char *fmt, int n_args, va_list ap){	const signed char *pval = check_data; 	_check_numbers_template("%hhd", pval, string, fmt, n_args, ap);} /* Selection of interesting numbers to test, copied from test-kstrtox.c */static const unsigned long long numbers[] = {	0x0ULL,	0x1ULL,	0x7fULL,	0x80ULL,	0x81ULL,	0xffULL,	0x100ULL,	0x101ULL,	0x7fffULL,	0x8000ULL,	0x8001ULL,	0xffffULL,	0x10000ULL,	0x10001ULL,	0x7fffffffULL,	0x80000000ULL,	0x80000001ULL,	0xffffffffULL,	0x100000000ULL,	0x100000001ULL,	0x7fffffffffffffffULL,	0x8000000000000000ULL,	0x8000000000000001ULL,	0xfffffffffffffffeULL,	0xffffffffffffffffULL,}; #define value_representable_in_type(T, val)					 \(is_signed_type(T)								 \	? ((long long)(val) >= type_min(T)) && ((long long)(val) <= type_max(T)) \	: ((unsigned long long)(val) <= type_max(T)))  #define test_one_number(T, gen_fmt, scan_fmt, val, fn)			\do {									\	const T expect_val = (T)(val);					\	T result = ~expect_val; /* should be overwritten */		\									\	snprintf(test_buffer, BUF_SIZE, gen_fmt, expect_val);		\	_test(test, __FILE__, __LINE__, fn, &expect_val, test_buffer, "%" scan_fmt, 1, &result);\} while (0) #define simple_numbers_loop(T, gen_fmt, scan_fmt, fn)			\do {									\	int i;								\									\	for (i = 0; i < ARRAY_SIZE(numbers); i++) {			\		if (value_representable_in_type(T, numbers[i]))		\			test_one_number(T, gen_fmt, scan_fmt,		\					numbers[i], fn);		\									\		if (value_representable_in_type(T, -numbers[i]))	\			test_one_number(T, gen_fmt, scan_fmt,		\					-numbers[i], fn);		\	}								\} while (0) static void numbers_simple(struct kunit *test){	simple_numbers_loop(unsigned long long,	"%llu",	  "llu", check_ull);	simple_numbers_loop(long long,		"%lld",	  "lld", check_ll);	simple_numbers_loop(long long,		"%lld",	  "lli", check_ll);	simple_numbers_loop(unsigned long long,	"%llx",	  "llx", check_ull);	simple_numbers_loop(long long,		"%llx",	  "llx", check_ll);	simple_numbers_loop(long long,		"0x%llx", "lli", check_ll);	simple_numbers_loop(unsigned long long, "0x%llx", "llx", check_ull);	simple_numbers_loop(long long,		"0x%llx", "llx", check_ll); 	simple_numbers_loop(unsigned long,	"%lu",	  "lu", check_ulong);	simple_numbers_loop(long,		"%ld",	  "ld", check_long);	simple_numbers_loop(long,		"%ld",	  "li", check_long);	simple_numbers_loop(unsigned long,	"%lx",	  "lx", check_ulong);	simple_numbers_loop(long,		"%lx",	  "lx", check_long);	simple_numbers_loop(long,		"0x%lx",  "li", check_long);	simple_numbers_loop(unsigned long,	"0x%lx",  "lx", check_ulong);	simple_numbers_loop(long,		"0x%lx",  "lx", check_long); 	simple_numbers_loop(unsigned int,	"%u",	  "u", check_uint);	simple_numbers_loop(int,		"%d",	  "d", check_int);	simple_numbers_loop(int,		"%d",	  "i", check_int);	simple_numbers_loop(unsigned int,	"%x",	  "x", check_uint);	simple_numbers_loop(int,		"%x",	  "x", check_int);	simple_numbers_loop(int,		"0x%x",   "i", check_int);	simple_numbers_loop(unsigned int,	"0x%x",   "x", check_uint);	simple_numbers_loop(int,		"0x%x",   "x", check_int); 	simple_numbers_loop(unsigned short,	"%hu",	  "hu", check_ushort);	simple_numbers_loop(short,		"%hd",	  "hd", check_short);	simple_numbers_loop(short,		"%hd",	  "hi", check_short);	simple_numbers_loop(unsigned short,	"%hx",	  "hx", check_ushort);	simple_numbers_loop(short,		"%hx",	  "hx", check_short);	simple_numbers_loop(short,		"0x%hx",  "hi", check_short);	simple_numbers_loop(unsigned short,	"0x%hx",  "hx", check_ushort);	simple_numbers_loop(short,		"0x%hx",  "hx", check_short); 	simple_numbers_loop(unsigned char,	"%hhu",	  "hhu", check_uchar);	simple_numbers_loop(signed char,	"%hhd",	  "hhd", check_char);	simple_numbers_loop(signed char,	"%hhd",	  "hhi", check_char);	simple_numbers_loop(unsigned char,	"%hhx",	  "hhx", check_uchar);	simple_numbers_loop(signed char,	"%hhx",	  "hhx", check_char);	simple_numbers_loop(signed char,	"0x%hhx", "hhi", check_char);	simple_numbers_loop(unsigned char,	"0x%hhx", "hhx", check_uchar);	simple_numbers_loop(signed char,	"0x%hhx", "hhx", check_char);} /* * This gives a better variety of number "lengths" in a small sample than * the raw prandom*() functions (Not mathematically rigorous!!). * Variabilty of length and value is more important than perfect randomness. */static u32 next_test_random(u32 max_bits){	u32 n_bits = hweight32(prandom_u32_state(&rnd_state)) % (max_bits + 1); 	return prandom_u32_state(&rnd_state) & GENMASK(n_bits, 0);} static unsigned long long next_test_random_ull(void){	u32 rand1 = prandom_u32_state(&rnd_state);	u32 n_bits = (hweight32(rand1) * 3) % 64;	u64 val = (u64)prandom_u32_state(&rnd_state) * rand1; 	return val & GENMASK_ULL(n_bits, 0);} #define random_for_type(T)				\	((T)(sizeof(T) <= sizeof(u32)			\		? next_test_random(BITS_PER_TYPE(T))	\		: next_test_random_ull())) /* * Define a pattern of negative and positive numbers to ensure we get * some of both within the small number of samples in a test string. */#define NEGATIVES_PATTERN 0x3246	/* 00110010 01000110 */ #define fill_random_array(arr)							\do {										\	unsigned int neg_pattern = NEGATIVES_PATTERN;				\	int i;									\										\	for (i = 0; i < ARRAY_SIZE(arr); i++, neg_pattern >>= 1) {		\		(arr)[i] = random_for_type(typeof((arr)[0]));			\		if (is_signed_type(typeof((arr)[0])) && (neg_pattern & 1))	\			(arr)[i] = -(arr)[i];					\	}									\} while (0) /* * Convenience wrapper around snprintf() to append at buf_pos in buf, * updating buf_pos and returning the number of characters appended. * On error buf_pos is not changed and return value is 0. */static int __printf(4, 5)append_fmt(char *buf, int *buf_pos, int buf_len, const char *val_fmt, ...){	va_list ap;	int field_len; 	va_start(ap, val_fmt);	field_len = vsnprintf(buf + *buf_pos, buf_len - *buf_pos, val_fmt, ap);	va_end(ap); 	if (field_len < 0)		field_len = 0; 	*buf_pos += field_len; 	return field_len;} /* * Convenience function to append the field delimiter string * to both the value string and format string buffers. */static void append_delim(char *str_buf, int *str_buf_pos, int str_buf_len,				char *fmt_buf, int *fmt_buf_pos, int fmt_buf_len,				const char *delim_str){	append_fmt(str_buf, str_buf_pos, str_buf_len, delim_str);	append_fmt(fmt_buf, fmt_buf_pos, fmt_buf_len, delim_str);} #define test_array_8(fn, check_data, string, fmt, arr)				\do {										\	BUILD_BUG_ON(ARRAY_SIZE(arr) != 8);					\	_test(test, __FILE__, __LINE__, fn, check_data, string, fmt, 8,		\		&(arr)[0], &(arr)[1], &(arr)[2], &(arr)[3],			\		&(arr)[4], &(arr)[5], &(arr)[6], &(arr)[7]);			\} while (0) #define numbers_list_8(T, gen_fmt, field_sep, scan_fmt, fn)			\do {										\	int i, pos = 0, fmt_pos = 0;						\	T expect[8], result[8];							\										\	fill_random_array(expect);						\										\	for (i = 0; i < ARRAY_SIZE(expect); i++) {				\		if (i != 0)							\			append_delim(test_buffer, &pos, BUF_SIZE,		\				     fmt_buffer, &fmt_pos, BUF_SIZE,		\				     field_sep);				\										\		append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt, expect[i]);	\		append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE, "%%%s", scan_fmt);	\	}									\										\	test_array_8(fn, expect, test_buffer, fmt_buffer, result);		\} while (0) #define numbers_list_fix_width(T, gen_fmt, field_sep, width, scan_fmt, fn)	\do {										\	char full_fmt[16];							\										\	snprintf(full_fmt, sizeof(full_fmt), "%u%s", width, scan_fmt);		\	numbers_list_8(T, gen_fmt, field_sep, full_fmt, fn);			\} while (0) #define numbers_list_val_width(T, gen_fmt, field_sep, scan_fmt, fn)		\do {										\	int i, val_len, pos = 0, fmt_pos = 0;					\	T expect[8], result[8];							\										\	fill_random_array(expect);						\										\	for (i = 0; i < ARRAY_SIZE(expect); i++) {				\		if (i != 0)							\			append_delim(test_buffer, &pos, BUF_SIZE,		\				     fmt_buffer, &fmt_pos, BUF_SIZE, field_sep);\										\		val_len = append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt,	\				     expect[i]);				\		append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE,			\			   "%%%u%s", val_len, scan_fmt);			\	}									\										\	test_array_8(fn, expect, test_buffer, fmt_buffer, result);		\} while (0) static void numbers_list_ll(struct kunit *test, const char *delim){	numbers_list_8(unsigned long long, "%llu",   delim, "llu", check_ull);	numbers_list_8(long long,	   "%lld",   delim, "lld", check_ll);	numbers_list_8(long long,	   "%lld",   delim, "lli", check_ll);	numbers_list_8(unsigned long long, "%llx",   delim, "llx", check_ull);	numbers_list_8(unsigned long long, "0x%llx", delim, "llx", check_ull);	numbers_list_8(long long,	   "0x%llx", delim, "lli", check_ll);} static void numbers_list_l(struct kunit *test, const char *delim){	numbers_list_8(unsigned long,	   "%lu",    delim, "lu", check_ulong);	numbers_list_8(long,		   "%ld",    delim, "ld", check_long);	numbers_list_8(long,		   "%ld",    delim, "li", check_long);	numbers_list_8(unsigned long,	   "%lx",    delim, "lx", check_ulong);	numbers_list_8(unsigned long,	   "0x%lx",  delim, "lx", check_ulong);	numbers_list_8(long,		   "0x%lx",  delim, "li", check_long);} static void numbers_list_d(struct kunit *test, const char *delim){	numbers_list_8(unsigned int,	   "%u",     delim, "u", check_uint);	numbers_list_8(int,		   "%d",     delim, "d", check_int);	numbers_list_8(int,		   "%d",     delim, "i", check_int);	numbers_list_8(unsigned int,	   "%x",     delim, "x", check_uint);	numbers_list_8(unsigned int,	   "0x%x",   delim, "x", check_uint);	numbers_list_8(int,		   "0x%x",   delim, "i", check_int);} static void numbers_list_h(struct kunit *test, const char *delim){	numbers_list_8(unsigned short,	   "%hu",    delim, "hu", check_ushort);	numbers_list_8(short,		   "%hd",    delim, "hd", check_short);	numbers_list_8(short,		   "%hd",    delim, "hi", check_short);	numbers_list_8(unsigned short,	   "%hx",    delim, "hx", check_ushort);	numbers_list_8(unsigned short,	   "0x%hx",  delim, "hx", check_ushort);	numbers_list_8(short,		   "0x%hx",  delim, "hi", check_short);} static void numbers_list_hh(struct kunit *test, const char *delim){	numbers_list_8(unsigned char,	   "%hhu",   delim, "hhu", check_uchar);	numbers_list_8(signed char,	   "%hhd",   delim, "hhd", check_char);	numbers_list_8(signed char,	   "%hhd",   delim, "hhi", check_char);	numbers_list_8(unsigned char,	   "%hhx",   delim, "hhx", check_uchar);	numbers_list_8(unsigned char,	   "0x%hhx", delim, "hhx", check_uchar);	numbers_list_8(signed char,	   "0x%hhx", delim, "hhi", check_char);} static void numbers_list(struct kunit *test){	const char * const *param = test->param_value;	const char *delim = *param; 	numbers_list_ll(test, delim);	numbers_list_l(test, delim);	numbers_list_d(test, delim);	numbers_list_h(test, delim);	numbers_list_hh(test, delim);} static void numbers_list_field_width_ll(struct kunit *test, const char *delim){	numbers_list_fix_width(unsigned long long, "%llu",   delim, 20, "llu", check_ull);	numbers_list_fix_width(long long,	   "%lld",   delim, 20, "lld", check_ll);	numbers_list_fix_width(long long,	   "%lld",   delim, 20, "lli", check_ll);	numbers_list_fix_width(unsigned long long, "%llx",   delim, 16, "llx", check_ull);	numbers_list_fix_width(unsigned long long, "0x%llx", delim, 18, "llx", check_ull);	numbers_list_fix_width(long long,	   "0x%llx", delim, 18, "lli", check_ll);} static void numbers_list_field_width_l(struct kunit *test, const char *delim){#if BITS_PER_LONG == 64	numbers_list_fix_width(unsigned long,	"%lu",	     delim, 20, "lu", check_ulong);	numbers_list_fix_width(long,		"%ld",	     delim, 20, "ld", check_long);	numbers_list_fix_width(long,		"%ld",	     delim, 20, "li", check_long);	numbers_list_fix_width(unsigned long,	"%lx",	     delim, 16, "lx", check_ulong);	numbers_list_fix_width(unsigned long,	"0x%lx",     delim, 18, "lx", check_ulong);	numbers_list_fix_width(long,		"0x%lx",     delim, 18, "li", check_long);#else	numbers_list_fix_width(unsigned long,	"%lu",	     delim, 10, "lu", check_ulong);	numbers_list_fix_width(long,		"%ld",	     delim, 11, "ld", check_long);	numbers_list_fix_width(long,		"%ld",	     delim, 11, "li", check_long);	numbers_list_fix_width(unsigned long,	"%lx",	     delim, 8,  "lx", check_ulong);	numbers_list_fix_width(unsigned long,	"0x%lx",     delim, 10, "lx", check_ulong);	numbers_list_fix_width(long,		"0x%lx",     delim, 10, "li", check_long);#endif} static void numbers_list_field_width_d(struct kunit *test, const char *delim){	numbers_list_fix_width(unsigned int,	"%u",	     delim, 10, "u", check_uint);	numbers_list_fix_width(int,		"%d",	     delim, 11, "d", check_int);	numbers_list_fix_width(int,		"%d",	     delim, 11, "i", check_int);	numbers_list_fix_width(unsigned int,	"%x",	     delim, 8,  "x", check_uint);	numbers_list_fix_width(unsigned int,	"0x%x",	     delim, 10, "x", check_uint);	numbers_list_fix_width(int,		"0x%x",	     delim, 10, "i", check_int);} static void numbers_list_field_width_h(struct kunit *test, const char *delim){	numbers_list_fix_width(unsigned short,	"%hu",	     delim, 5, "hu", check_ushort);	numbers_list_fix_width(short,		"%hd",	     delim, 6, "hd", check_short);	numbers_list_fix_width(short,		"%hd",	     delim, 6, "hi", check_short);	numbers_list_fix_width(unsigned short,	"%hx",	     delim, 4, "hx", check_ushort);	numbers_list_fix_width(unsigned short,	"0x%hx",     delim, 6, "hx", check_ushort);	numbers_list_fix_width(short,		"0x%hx",     delim, 6, "hi", check_short);} static void numbers_list_field_width_hh(struct kunit *test, const char *delim){	numbers_list_fix_width(unsigned char,	"%hhu",	     delim, 3, "hhu", check_uchar);

Lines 1–500 of 815.