From 5c04761b0b2023c8205f80148c47b62dea303d49 Mon Sep 17 00:00:00 2001 From: Ethan Graham Date: Wed, 2 Jul 2025 11:57:14 +0000 Subject: [PATCH 1/4] kftf: add some examples of fuzz targets with domain constraints --- lib/oid_registry.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/oid_registry.c b/lib/oid_registry.c index 9b757a117f09f7..2c93d07d269ca2 100644 --- a/lib/oid_registry.c +++ b/lib/oid_registry.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -85,8 +86,7 @@ enum OID look_up_OID(const void *data, size_t datasize) } } return oid; - next: - ; +next:; } return OID__NR; @@ -172,4 +172,40 @@ int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize) snprintf(buffer, bufsize, "(bad)"); return -EBADMSG; } + +struct sprint_oid_arg { + const char *data; + size_t datasize; + size_t bufsize; +}; + +FUZZ_TEST(test_sprint_oid, struct sprint_oid_arg) +{ + // ignore null pointers + KFTF_EXPECT_NOT_NULL(sprint_oid_arg, data); + KFTF_EXPECT_IN_RANGE(sprint_oid_arg, bufsize, 16, PAGE_SIZE); + + char *kernel_data = kmalloc(arg.datasize, GFP_KERNEL); + if (!kernel_data) { + pr_warn("bug: unable to kmalloc a buffer for kernel data\n"); + return; + } + if (copy_from_user(kernel_data, arg.data, arg.datasize)) { + pr_warn("bug: unable to copy data from user to kernel\n"); + kfree(kernel_data); + return; + } + char *buffer = kmalloc(arg.bufsize, GFP_KERNEL); + if (!buffer) { + pr_warn("bug: unable to kmalloc a buffer\n"); + kfree(kernel_data); + return; + } + + sprint_oid((const char *)kernel_data, arg.datasize, buffer, + arg.bufsize); + kfree(buffer); + kfree(kernel_data); +} + EXPORT_SYMBOL_GPL(sprint_oid); From 26f204a914a8fa12e66e81f3990cbe0d76892a69 Mon Sep 17 00:00:00 2001 From: Ethan Graham Date: Thu, 3 Jul 2025 18:46:15 +0000 Subject: [PATCH 2/4] kftf: add more test cases --- lib/bitmap-str.c | 32 ++++++++++++++++++++++++++++++++ net/atm/mpoa_proc.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/lib/bitmap-str.c b/lib/bitmap-str.c index be745209507a40..fd27c93f6e6732 100644 --- a/lib/bitmap-str.c +++ b/lib/bitmap-str.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -399,6 +400,37 @@ int bitmap_parselist(const char *buf, unsigned long *maskp, int nmaskbits) } EXPORT_SYMBOL(bitmap_parselist); +struct bitmap_parselist_arg { + const char *buf; + int nmaskbits; +}; + +FUZZ_TEST(test_bitmap_parselist, struct bitmap_parselist_arg) +{ + unsigned long maskp_out; + size_t buflen; + char *kernel_buf; + size_t count = 2 * PAGE_SIZE; + + if (!arg.buf) + return; + + buflen = strnlen_user(arg.buf, count); + if (buflen > count) + return; + + kernel_buf = strndup_user(arg.buf, count); + if (!kernel_buf) { + pr_warn("test_bitmap_parselist: unable to allocate kernel buffer"); + return; + } + + pr_info("test_bitmap_parselist: buflen = %zu, buf = %s, nmaskbits = %d\n", + buflen, kernel_buf, arg.nmaskbits); + bitmap_parselist(kernel_buf, &maskp_out, arg.nmaskbits); + + kfree(kernel_buf); +} /** * bitmap_parselist_user() - convert user buffer's list format ASCII diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index aaf64b95391507..b4556af0ae34be 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -281,6 +282,37 @@ static int parse_qos(const char *buff) return 1; } +struct parse_qos_arg { + const char *buff; +}; + +FUZZ_TEST(test_parse_qos, struct parse_qos_arg) +{ + size_t bufflen; + char *kernel_buff; + int ret; + size_t count = 2 * PAGE_SIZE; + + if (!arg.buff) + return; + + bufflen = strnlen_user(arg.buff, count); + if (bufflen > count) + return; + + kernel_buff = strndup_user(arg.buff, bufflen); + if (!kernel_buff || + kernel_buff == ZERO_SIZE_PTR /* be careful here */) { + pr_warn("failed to allocate kernel buffer\n"); + return; + } + + pr_info("test_parse_qos: bufflen = %zu\n", bufflen); + ret = parse_qos(kernel_buff); + + kfree(kernel_buff); +} + /* * INITIALIZATION function - called when module is initialized/loaded. */ From 6e7678a3f2dd0c29d24efc511311bf29aadd0f6e Mon Sep 17 00:00:00 2001 From: Ethan Graham Date: Tue, 8 Jul 2025 06:43:03 +0000 Subject: [PATCH 3/4] kftf: remove some useless tests and update FUZZ_TESTSs to use the new domain constraint functionality. Remove useless int_sqrt test that is polluting syz-manager during fuzzing --- lib/bitmap-str.c | 3 +-- net/atm/mpoa_proc.c | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/bitmap-str.c b/lib/bitmap-str.c index fd27c93f6e6732..d2c4fb4fd57c9c 100644 --- a/lib/bitmap-str.c +++ b/lib/bitmap-str.c @@ -412,8 +412,7 @@ FUZZ_TEST(test_bitmap_parselist, struct bitmap_parselist_arg) char *kernel_buf; size_t count = 2 * PAGE_SIZE; - if (!arg.buf) - return; + KFTF_EXPECT_NOT_NULL(bitmap_parselist_arg, buf); buflen = strnlen_user(arg.buf, count); if (buflen > count) diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index b4556af0ae34be..d1a11e3f5856e2 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -292,9 +292,7 @@ FUZZ_TEST(test_parse_qos, struct parse_qos_arg) char *kernel_buff; int ret; size_t count = 2 * PAGE_SIZE; - - if (!arg.buff) - return; + KFTF_EXPECT_NOT_NULL(parse_qos_arg, buff); bufflen = strnlen_user(arg.buff, count); if (bufflen > count) From ba54dcc906e6436af927a28a7f431c5174530f4a Mon Sep 17 00:00:00 2001 From: Ethan Graham Date: Fri, 25 Jul 2025 10:16:05 +0000 Subject: [PATCH 4/4] kfuzztest: add test cases that use current KFuzzTest interface --- arch/x86/lib/cmdline.c | 27 +++++++++++++++++++++ crypto/asymmetric_keys/pkcs7_parser.c | 15 ++++++++++++ crypto/asymmetric_keys/pkcs8_parser.c | 17 +++++++++++++ crypto/rsa_helper.c | 33 +++++++++++++++++++++++++ lib/bitmap-str.c | 35 ++++++++++++--------------- lib/oid_registry.c | 30 +++++++++++------------ lib/parser.c | 32 +++++++++++++++++++++++- net/atm/mpoa_proc.c | 23 +++--------------- 8 files changed, 156 insertions(+), 56 deletions(-) diff --git a/arch/x86/lib/cmdline.c b/arch/x86/lib/cmdline.c index c65cd55504549a..f4caff8f8890c9 100644 --- a/arch/x86/lib/cmdline.c +++ b/arch/x86/lib/cmdline.c @@ -11,6 +11,8 @@ #include #include +#include + static inline int myisspace(u8 c) { return c <= ' '; /* Close enough approximation */ @@ -233,3 +235,28 @@ int cmdline_find_option(const char *cmdline, const char *option, char *buffer, return ret; } + +struct cmdline_find_option_arg { + const char *cmdline; + const char *option; +}; + +FUZZ_TEST(test_cmdline_find_option, struct cmdline_find_option_arg) +{ + char *buffer; + + KFUZZTEST_EXPECT_NOT_NULL(cmlind_find_option_arg, cmdline); + KFUZZTEST_EXPECT_NOT_NULL(cmlind_find_option_arg, option); + KFUZZTEST_ANNOTATE_STRING(cmdline_find_option_arg, cmdline); + KFUZZTEST_ANNOTATE_STRING(cmdline_find_option_arg, option); + + int bufsize = 1024; + buffer = kmalloc(bufsize, GFP_KERNEL); + if (!buffer || IS_ERR(buffer)) { + pr_warn("failed to allocate a kernel buffer"); + return; + } + + cmdline_find_option(arg->cmdline, arg->option, buffer, bufsize); + kfree(buffer); +} diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 423d13c475452c..e8477f8b0eafbf 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "pkcs7_parser.h" #include "pkcs7.asn1.h" @@ -169,6 +170,20 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) } EXPORT_SYMBOL_GPL(pkcs7_parse_message); +struct pkcs7_parse_message_arg { + const void *data; + size_t datalen; +}; + +FUZZ_TEST(test_pkcs7_parse_message, struct pkcs7_parse_message_arg) +{ + KFUZZTEST_EXPECT_NOT_NULL(pkcs7_parse_message_arg, data); + KFUZZTEST_ANNOTATE_LEN(pkcs7_parse_message_arg, datalen, data); + KFUZZTEST_EXPECT_LE(pkcs7_parse_message_arg, datalen, 16 * PAGE_SIZE); + + pkcs7_parse_message(arg->data, arg->datalen); +} + /** * pkcs7_get_content_data - Get access to the PKCS#7 content * @pkcs7: The preparsed PKCS#7 message to access diff --git a/crypto/asymmetric_keys/pkcs8_parser.c b/crypto/asymmetric_keys/pkcs8_parser.c index 105dcce27f711a..d72e538c405d27 100644 --- a/crypto/asymmetric_keys/pkcs8_parser.c +++ b/crypto/asymmetric_keys/pkcs8_parser.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "pkcs8.asn1.h" struct pkcs8_parse_context { @@ -130,6 +131,22 @@ static struct public_key *pkcs8_parse(const void *data, size_t datalen) return ERR_PTR(ret); } +struct pkcs8_parse_arg { + const void *data; + size_t datalen; +}; + +FUZZ_TEST(test_pkcs8_parse, struct pkcs8_parse_arg) +{ + const size_t max_size = 16 * 1024; + + KFUZZTEST_EXPECT_NOT_NULL(pkcs8_parse_arg, data); + if (arg->datalen > max_size) + return; + + pkcs8_parse(arg->data, arg->datalen); +} + /* * Attempt to parse a data blob for a key as a PKCS#8 private key. */ diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c index 94266f29049c92..7e66de1ee23f47 100644 --- a/crypto/rsa_helper.c +++ b/crypto/rsa_helper.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "rsapubkey.asn1.h" #include "rsaprivkey.asn1.h" @@ -166,6 +167,22 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, } EXPORT_SYMBOL_GPL(rsa_parse_pub_key); +struct rsa_parse_pub_key_arg { + const void *key; + size_t key_len; +}; + +FUZZ_TEST(test_rsa_parse_pub_key, struct rsa_parse_pub_key_arg) +{ + KFUZZTEST_EXPECT_NOT_NULL(rsa_parse_pub_key_arg, key); + // Bound the maximum size of the key that we accept. + if (arg->key_len > 16 * PAGE_SIZE) + return; + + struct rsa_key out; + rsa_parse_pub_key(&out, arg->key, arg->key_len); +} + /** * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key @@ -184,3 +201,19 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len); } EXPORT_SYMBOL_GPL(rsa_parse_priv_key); + +struct rsa_parse_priv_key_arg { + const void *key; + size_t key_len; +}; + +FUZZ_TEST(test_rsa_parse_priv_key, struct rsa_parse_priv_key_arg) +{ + KFUZZTEST_EXPECT_NOT_NULL(rsa_parse_priv_key_arg, key); + // bound the maximum size of the key that we accept + if (arg->key_len > 16 * PAGE_SIZE) + return; + + struct rsa_key out; + rsa_parse_priv_key(&out, arg->key, arg->key_len); +} diff --git a/lib/bitmap-str.c b/lib/bitmap-str.c index d2c4fb4fd57c9c..1da0261efb5399 100644 --- a/lib/bitmap-str.c +++ b/lib/bitmap-str.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -407,28 +407,25 @@ struct bitmap_parselist_arg { FUZZ_TEST(test_bitmap_parselist, struct bitmap_parselist_arg) { - unsigned long maskp_out; - size_t buflen; - char *kernel_buf; - size_t count = 2 * PAGE_SIZE; - - KFTF_EXPECT_NOT_NULL(bitmap_parselist_arg, buf); - - buflen = strnlen_user(arg.buf, count); - if (buflen > count) - return; - - kernel_buf = strndup_user(arg.buf, count); - if (!kernel_buf) { - pr_warn("test_bitmap_parselist: unable to allocate kernel buffer"); + unsigned long *maskp; + size_t maskp_size; + + KFUZZTEST_EXPECT_NOT_NULL(bitmap_parselist_arg, buf); + KFUZZTEST_EXPECT_IN_RANGE(bitmap_parselist_arg, nmaskbits, 16, + 1024 * 10); + KFUZZTEST_ANNOTATE_STRING(bitmap_parselist_arg, buf); + + // number of longs that we need to allocate + maskp_size = BITS_TO_LONGS(arg->nmaskbits) * sizeof(unsigned long); + maskp = kmalloc(maskp_size, GFP_KERNEL); + if (!maskp) { + pr_warn("bug: failed to allocate kernel buffer for maskp\n"); return; } - pr_info("test_bitmap_parselist: buflen = %zu, buf = %s, nmaskbits = %d\n", - buflen, kernel_buf, arg.nmaskbits); - bitmap_parselist(kernel_buf, &maskp_out, arg.nmaskbits); + bitmap_parselist(arg->buf, maskp, arg->nmaskbits); - kfree(kernel_buf); + kfree(maskp); } /** diff --git a/lib/oid_registry.c b/lib/oid_registry.c index 2c93d07d269ca2..a7140395cd2341 100644 --- a/lib/oid_registry.c +++ b/lib/oid_registry.c @@ -9,10 +9,10 @@ #include #include #include -#include #include #include #include +#include #include "oid_registry_data.c" MODULE_DESCRIPTION("OID Registry"); @@ -181,31 +181,29 @@ struct sprint_oid_arg { FUZZ_TEST(test_sprint_oid, struct sprint_oid_arg) { - // ignore null pointers - KFTF_EXPECT_NOT_NULL(sprint_oid_arg, data); - KFTF_EXPECT_IN_RANGE(sprint_oid_arg, bufsize, 16, PAGE_SIZE); + KFUZZTEST_EXPECT_NOT_NULL(sprint_oid_arg, data); + KFUZZTEST_EXPECT_IN_RANGE(sprint_oid_arg, bufsize, 16, PAGE_SIZE); + KFUZZTEST_ANNOTATE_LEN(sprint_oid_arg, datasize, data); - char *kernel_data = kmalloc(arg.datasize, GFP_KERNEL); - if (!kernel_data) { - pr_warn("bug: unable to kmalloc a buffer for kernel data\n"); + /* limit to a reasonable bufsize */ + if (arg->bufsize > 4 * PAGE_SIZE) return; - } - if (copy_from_user(kernel_data, arg.data, arg.datasize)) { - pr_warn("bug: unable to copy data from user to kernel\n"); - kfree(kernel_data); + + /* This actually works - maybe a macro for this?? */ + if (ksize(arg) < sizeof(arg->data) + sizeof(arg->datasize) + + sizeof(arg->bufsize) + arg->datasize) { + pr_warn("invalid buffer size!\n"); return; } - char *buffer = kmalloc(arg.bufsize, GFP_KERNEL); + + char *buffer = kmalloc(arg->bufsize, GFP_KERNEL); if (!buffer) { pr_warn("bug: unable to kmalloc a buffer\n"); - kfree(kernel_data); return; } - sprint_oid((const char *)kernel_data, arg.datasize, buffer, - arg.bufsize); + sprint_oid(arg->data, arg->datasize, buffer, arg->bufsize); kfree(buffer); - kfree(kernel_data); } EXPORT_SYMBOL_GPL(sprint_oid); diff --git a/lib/parser.c b/lib/parser.c index 73e8f8e5be73ff..b9e8c5b1eb88eb 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -10,6 +10,7 @@ #include #include #include +#include /* * max size needed by different bases to express U64 @@ -151,10 +152,39 @@ static int match_number(substring_t *s, int *result, int base) else if (val < (long)INT_MIN || val > (long)INT_MAX) ret = -ERANGE; else - *result = (int) val; + *result = (int)val; return ret; } +struct match_number_arg { + char *str; + size_t from; + size_t to; + int base; +}; + +FUZZ_TEST(test_match_number, struct match_number_arg) +{ + size_t str_len; + + KFUZZTEST_EXPECT_NOT_NULL(match_number_arg, str); + KFUZZTEST_ANNOTATE_STRING(match_number_arg, str); + KFUZZTEST_ANNOTATE_LEN(match_number_arg, str_len, str); + + str_len = strlen(arg->str); + + /* quite verbose - ideally should encode with some annotation system */ + if (arg->from >= str_len || arg->to >= str_len || arg->from > arg->to) { + return; + } + + substring_t s = { .from = arg->str + arg->from, + .to = arg->str + arg->to }; + + int result; + match_number(&s, &result, arg->base); +} + /** * match_u64int - scan a number in the given base from a substring_t * @s: substring to be scanned diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index d1a11e3f5856e2..0349d857b118a2 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -288,27 +288,10 @@ struct parse_qos_arg { FUZZ_TEST(test_parse_qos, struct parse_qos_arg) { - size_t bufflen; - char *kernel_buff; int ret; - size_t count = 2 * PAGE_SIZE; - KFTF_EXPECT_NOT_NULL(parse_qos_arg, buff); - - bufflen = strnlen_user(arg.buff, count); - if (bufflen > count) - return; - - kernel_buff = strndup_user(arg.buff, bufflen); - if (!kernel_buff || - kernel_buff == ZERO_SIZE_PTR /* be careful here */) { - pr_warn("failed to allocate kernel buffer\n"); - return; - } - - pr_info("test_parse_qos: bufflen = %zu\n", bufflen); - ret = parse_qos(kernel_buff); + KFUZZTEST_EXPECT_NOT_NULL(parse_qos_arg, buff); - kfree(kernel_buff); + ret = parse_qos(arg->buff); } /*