123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Test cases for sscanf facility.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/bitops.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/overflow.h>
- #include <linux/printk.h>
- #include <linux/random.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include "../tools/testing/selftests/kselftest_module.h"
- #define BUF_SIZE 1024
- KSTM_MODULE_GLOBALS();
- static char *test_buffer __initdata;
- static char *fmt_buffer __initdata;
- static struct rnd_state rnd_state __initdata;
- typedef int (*check_fn)(const void *check_data, const char *string,
- const char *fmt, int n_args, va_list ap);
- static void __scanf(4, 6) __init
- _test(check_fn fn, const void *check_data, const char *string, const char *fmt,
- int n_args, ...)
- {
- va_list ap, ap_copy;
- int ret;
- total_tests++;
- va_start(ap, n_args);
- va_copy(ap_copy, ap);
- ret = vsscanf(string, fmt, ap_copy);
- va_end(ap_copy);
- if (ret != n_args) {
- pr_warn("vsscanf(\"%s\", \"%s\", ...) returned %d expected %d\n",
- string, fmt, ret, n_args);
- goto fail;
- }
- ret = (*fn)(check_data, string, fmt, n_args, ap);
- if (ret)
- goto fail;
- va_end(ap);
- return;
- fail:
- failed_tests++;
- va_end(ap);
- }
- #define _check_numbers_template(arg_fmt, expect, str, fmt, n_args, ap) \
- do { \
- pr_debug("\"%s\", \"%s\" ->\n", str, fmt); \
- for (; n_args > 0; n_args--, expect++) { \
- typeof(*expect) got = *va_arg(ap, typeof(expect)); \
- pr_debug("\t" arg_fmt "\n", got); \
- if (got != *expect) { \
- pr_warn("vsscanf(\"%s\", \"%s\", ...) expected " arg_fmt " got " arg_fmt "\n", \
- str, fmt, *expect, got); \
- return 1; \
- } \
- } \
- return 0; \
- } while (0)
- static int __init check_ull(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 int __init check_ll(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 int __init check_ulong(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 int __init check_long(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 int __init check_uint(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 int __init check_int(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 int __init check_ushort(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 int __init check_short(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 int __init check_uchar(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 int __init check_char(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[] __initconst = {
- 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(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 __init numbers_simple(void)
- {
- 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 __init 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 __init 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 __init __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 __init 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(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 __init numbers_list_ll(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 __init numbers_list_l(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 __init numbers_list_d(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 __init numbers_list_h(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 __init numbers_list_hh(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 __init numbers_list(const char *delim)
- {
- numbers_list_ll(delim);
- numbers_list_l(delim);
- numbers_list_d(delim);
- numbers_list_h(delim);
- numbers_list_hh(delim);
- }
- static void __init numbers_list_field_width_ll(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 __init numbers_list_field_width_l(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 __init numbers_list_field_width_d(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 __init numbers_list_field_width_h(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 __init numbers_list_field_width_hh(const char *delim)
- {
- numbers_list_fix_width(unsigned char, "%hhu", delim, 3, "hhu", check_uchar);
- numbers_list_fix_width(signed char, "%hhd", delim, 4, "hhd", check_char);
- numbers_list_fix_width(signed char, "%hhd", delim, 4, "hhi", check_char);
- numbers_list_fix_width(unsigned char, "%hhx", delim, 2, "hhx", check_uchar);
- numbers_list_fix_width(unsigned char, "0x%hhx", delim, 4, "hhx", check_uchar);
- numbers_list_fix_width(signed char, "0x%hhx", delim, 4, "hhi", check_char);
- }
- /*
- * List of numbers separated by delim. Each field width specifier is the
- * maximum possible digits for the given type and base.
- */
- static void __init numbers_list_field_width_typemax(const char *delim)
- {
- numbers_list_field_width_ll(delim);
- numbers_list_field_width_l(delim);
- numbers_list_field_width_d(delim);
- numbers_list_field_width_h(delim);
- numbers_list_field_width_hh(delim);
- }
- static void __init numbers_list_field_width_val_ll(const char *delim)
- {
- numbers_list_val_width(unsigned long long, "%llu", delim, "llu", check_ull);
- numbers_list_val_width(long long, "%lld", delim, "lld", check_ll);
- numbers_list_val_width(long long, "%lld", delim, "lli", check_ll);
- numbers_list_val_width(unsigned long long, "%llx", delim, "llx", check_ull);
- numbers_list_val_width(unsigned long long, "0x%llx", delim, "llx", check_ull);
- numbers_list_val_width(long long, "0x%llx", delim, "lli", check_ll);
- }
- static void __init numbers_list_field_width_val_l(const char *delim)
- {
- numbers_list_val_width(unsigned long, "%lu", delim, "lu", check_ulong);
- numbers_list_val_width(long, "%ld", delim, "ld", check_long);
- numbers_list_val_width(long, "%ld", delim, "li", check_long);
- numbers_list_val_width(unsigned long, "%lx", delim, "lx", check_ulong);
- numbers_list_val_width(unsigned long, "0x%lx", delim, "lx", check_ulong);
- numbers_list_val_width(long, "0x%lx", delim, "li", check_long);
- }
- static void __init numbers_list_field_width_val_d(const char *delim)
- {
- numbers_list_val_width(unsigned int, "%u", delim, "u", check_uint);
- numbers_list_val_width(int, "%d", delim, "d", check_int);
- numbers_list_val_width(int, "%d", delim, "i", check_int);
- numbers_list_val_width(unsigned int, "%x", delim, "x", check_uint);
- numbers_list_val_width(unsigned int, "0x%x", delim, "x", check_uint);
- numbers_list_val_width(int, "0x%x", delim, "i", check_int);
- }
- static void __init numbers_list_field_width_val_h(const char *delim)
- {
- numbers_list_val_width(unsigned short, "%hu", delim, "hu", check_ushort);
- numbers_list_val_width(short, "%hd", delim, "hd", check_short);
- numbers_list_val_width(short, "%hd", delim, "hi", check_short);
- numbers_list_val_width(unsigned short, "%hx", delim, "hx", check_ushort);
- numbers_list_val_width(unsigned short, "0x%hx", delim, "hx", check_ushort);
- numbers_list_val_width(short, "0x%hx", delim, "hi", check_short);
- }
- static void __init numbers_list_field_width_val_hh(const char *delim)
- {
- numbers_list_val_width(unsigned char, "%hhu", delim, "hhu", check_uchar);
- numbers_list_val_width(signed char, "%hhd", delim, "hhd", check_char);
- numbers_list_val_width(signed char, "%hhd", delim, "hhi", check_char);
- numbers_list_val_width(unsigned char, "%hhx", delim, "hhx", check_uchar);
- numbers_list_val_width(unsigned char, "0x%hhx", delim, "hhx", check_uchar);
- numbers_list_val_width(signed char, "0x%hhx", delim, "hhi", check_char);
- }
- /*
- * List of numbers separated by delim. Each field width specifier is the
- * exact length of the corresponding value digits in the string being scanned.
- */
- static void __init numbers_list_field_width_val_width(const char *delim)
- {
- numbers_list_field_width_val_ll(delim);
- numbers_list_field_width_val_l(delim);
- numbers_list_field_width_val_d(delim);
- numbers_list_field_width_val_h(delim);
- numbers_list_field_width_val_hh(delim);
- }
- /*
- * Slice a continuous string of digits without field delimiters, containing
- * numbers of varying length, using the field width to extract each group
- * of digits. For example the hex values c0,3,bf01,303 would have a
- * string representation of "c03bf01303" and extracted with "%2x%1x%4x%3x".
- */
- static void __init numbers_slice(void)
- {
- numbers_list_field_width_val_width("");
- }
- #define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn) \
- do { \
- const T expect[2] = { expect0, expect1 }; \
- T result[2] = { (T)~expect[0], (T)~expect[1] }; \
- \
- _test(fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]); \
- } while (0)
- /*
- * Number prefix is >= field width.
- * Expected behaviour is derived from testing userland sscanf.
- */
- static void __init numbers_prefix_overflow(void)
- {
- /*
- * Negative decimal with a field of width 1, should quit scanning
- * and return 0.
- */
- test_number_prefix(long long, "-1 1", "%1lld %lld", 0, 0, 0, check_ll);
- test_number_prefix(long, "-1 1", "%1ld %ld", 0, 0, 0, check_long);
- test_number_prefix(int, "-1 1", "%1d %d", 0, 0, 0, check_int);
- test_number_prefix(short, "-1 1", "%1hd %hd", 0, 0, 0, check_short);
- test_number_prefix(signed char, "-1 1", "%1hhd %hhd", 0, 0, 0, check_char);
- test_number_prefix(long long, "-1 1", "%1lli %lli", 0, 0, 0, check_ll);
- test_number_prefix(long, "-1 1", "%1li %li", 0, 0, 0, check_long);
- test_number_prefix(int, "-1 1", "%1i %i", 0, 0, 0, check_int);
- test_number_prefix(short, "-1 1", "%1hi %hi", 0, 0, 0, check_short);
- test_number_prefix(signed char, "-1 1", "%1hhi %hhi", 0, 0, 0, check_char);
- /*
- * 0x prefix in a field of width 1: 0 is a valid digit so should
- * convert. Next field scan starts at the 'x' which isn't a digit so
- * scan quits with one field converted.
- */
- test_number_prefix(unsigned long long, "0xA7", "%1llx%llx", 0, 0, 1, check_ull);
- test_number_prefix(unsigned long, "0xA7", "%1lx%lx", 0, 0, 1, check_ulong);
- test_number_prefix(unsigned int, "0xA7", "%1x%x", 0, 0, 1, check_uint);
- test_number_prefix(unsigned short, "0xA7", "%1hx%hx", 0, 0, 1, check_ushort);
- test_number_prefix(unsigned char, "0xA7", "%1hhx%hhx", 0, 0, 1, check_uchar);
- test_number_prefix(long long, "0xA7", "%1lli%llx", 0, 0, 1, check_ll);
- test_number_prefix(long, "0xA7", "%1li%lx", 0, 0, 1, check_long);
- test_number_prefix(int, "0xA7", "%1i%x", 0, 0, 1, check_int);
- test_number_prefix(short, "0xA7", "%1hi%hx", 0, 0, 1, check_short);
- test_number_prefix(char, "0xA7", "%1hhi%hhx", 0, 0, 1, check_char);
- /*
- * 0x prefix in a field of width 2 using %x conversion: first field
- * converts to 0. Next field scan starts at the character after "0x".
- * Both fields will convert.
- */
- test_number_prefix(unsigned long long, "0xA7", "%2llx%llx", 0, 0xa7, 2, check_ull);
- test_number_prefix(unsigned long, "0xA7", "%2lx%lx", 0, 0xa7, 2, check_ulong);
- test_number_prefix(unsigned int, "0xA7", "%2x%x", 0, 0xa7, 2, check_uint);
- test_number_prefix(unsigned short, "0xA7", "%2hx%hx", 0, 0xa7, 2, check_ushort);
- test_number_prefix(unsigned char, "0xA7", "%2hhx%hhx", 0, 0xa7, 2, check_uchar);
- /*
- * 0x prefix in a field of width 2 using %i conversion: first field
- * converts to 0. Next field scan starts at the character after "0x",
- * which will convert if can be interpreted as decimal but will fail
- * if it contains any hex digits (since no 0x prefix).
- */
- test_number_prefix(long long, "0x67", "%2lli%lli", 0, 67, 2, check_ll);
- test_number_prefix(long, "0x67", "%2li%li", 0, 67, 2, check_long);
- test_number_prefix(int, "0x67", "%2i%i", 0, 67, 2, check_int);
- test_number_prefix(short, "0x67", "%2hi%hi", 0, 67, 2, check_short);
- test_number_prefix(char, "0x67", "%2hhi%hhi", 0, 67, 2, check_char);
- test_number_prefix(long long, "0xA7", "%2lli%lli", 0, 0, 1, check_ll);
- test_number_prefix(long, "0xA7", "%2li%li", 0, 0, 1, check_long);
- test_number_prefix(int, "0xA7", "%2i%i", 0, 0, 1, check_int);
- test_number_prefix(short, "0xA7", "%2hi%hi", 0, 0, 1, check_short);
- test_number_prefix(char, "0xA7", "%2hhi%hhi", 0, 0, 1, check_char);
- }
- #define _test_simple_strtoxx(T, fn, gen_fmt, expect, base) \
- do { \
- T got; \
- char *endp; \
- int len; \
- bool fail = false; \
- \
- total_tests++; \
- len = snprintf(test_buffer, BUF_SIZE, gen_fmt, expect); \
- got = (fn)(test_buffer, &endp, base); \
- pr_debug(#fn "(\"%s\", %d) -> " gen_fmt "\n", test_buffer, base, got); \
- if (got != (expect)) { \
- fail = true; \
- pr_warn(#fn "(\"%s\", %d): got " gen_fmt " expected " gen_fmt "\n", \
- test_buffer, base, got, expect); \
- } else if (endp != test_buffer + len) { \
- fail = true; \
- pr_warn(#fn "(\"%s\", %d) startp=0x%px got endp=0x%px expected 0x%px\n", \
- test_buffer, base, test_buffer, \
- test_buffer + len, endp); \
- } \
- \
- if (fail) \
- failed_tests++; \
- } while (0)
- #define test_simple_strtoxx(T, fn, gen_fmt, base) \
- do { \
- int i; \
- \
- for (i = 0; i < ARRAY_SIZE(numbers); i++) { \
- _test_simple_strtoxx(T, fn, gen_fmt, (T)numbers[i], base); \
- \
- if (is_signed_type(T)) \
- _test_simple_strtoxx(T, fn, gen_fmt, \
- -(T)numbers[i], base); \
- } \
- } while (0)
- static void __init test_simple_strtoull(void)
- {
- test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu", 10);
- test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu", 0);
- test_simple_strtoxx(unsigned long long, simple_strtoull, "%llx", 16);
- test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 16);
- test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 0);
- }
- static void __init test_simple_strtoll(void)
- {
- test_simple_strtoxx(long long, simple_strtoll, "%lld", 10);
- test_simple_strtoxx(long long, simple_strtoll, "%lld", 0);
- test_simple_strtoxx(long long, simple_strtoll, "%llx", 16);
- test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 16);
- test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 0);
- }
- static void __init test_simple_strtoul(void)
- {
- test_simple_strtoxx(unsigned long, simple_strtoul, "%lu", 10);
- test_simple_strtoxx(unsigned long, simple_strtoul, "%lu", 0);
- test_simple_strtoxx(unsigned long, simple_strtoul, "%lx", 16);
- test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 16);
- test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 0);
- }
- static void __init test_simple_strtol(void)
- {
- test_simple_strtoxx(long, simple_strtol, "%ld", 10);
- test_simple_strtoxx(long, simple_strtol, "%ld", 0);
- test_simple_strtoxx(long, simple_strtol, "%lx", 16);
- test_simple_strtoxx(long, simple_strtol, "0x%lx", 16);
- test_simple_strtoxx(long, simple_strtol, "0x%lx", 0);
- }
- /* Selection of common delimiters/separators between numbers in a string. */
- static const char * const number_delimiters[] __initconst = {
- " ", ":", ",", "-", "/",
- };
- static void __init test_numbers(void)
- {
- int i;
- /* String containing only one number. */
- numbers_simple();
- /* String with multiple numbers separated by delimiter. */
- for (i = 0; i < ARRAY_SIZE(number_delimiters); i++) {
- numbers_list(number_delimiters[i]);
- /* Field width may be longer than actual field digits. */
- numbers_list_field_width_typemax(number_delimiters[i]);
- /* Each field width exactly length of actual field digits. */
- numbers_list_field_width_val_width(number_delimiters[i]);
- }
- /* Slice continuous sequence of digits using field widths. */
- numbers_slice();
- numbers_prefix_overflow();
- }
- static void __init selftest(void)
- {
- test_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
- if (!test_buffer)
- return;
- fmt_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
- if (!fmt_buffer) {
- kfree(test_buffer);
- return;
- }
- prandom_seed_state(&rnd_state, 3141592653589793238ULL);
- test_numbers();
- test_simple_strtoull();
- test_simple_strtoll();
- test_simple_strtoul();
- test_simple_strtol();
- kfree(fmt_buffer);
- kfree(test_buffer);
- }
- KSTM_MODULE_LOADERS(test_scanf);
- MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");
- MODULE_LICENSE("GPL v2");
|