From cd4eaccc00d79ab97d9a96f7922558558b13f220 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Fri, 10 Oct 2025 10:21:38 +0200 Subject: treewide: drop outdated compiler version remarks in Kconfig help texts As of writing, Documentation/Changes states the minimal versions of GNU C being 8.1, Clang being 15.0.0 and binutils being 2.30. A few Kconfig help texts are pointing out that specific GCC and Clang versions are needed, but by now, those pointers to versions, such later than 4.0, later than 4.4, or clang later than 5.0, are obsolete and unlikely to be found by users configuring their kernel builds anyway. Drop these outdated remarks in Kconfig help texts referring to older compiler and binutils versions. No functional change. Link: https://lkml.kernel.org/r/20251010082138.185752-1-lukas.bulwahn@redhat.com Signed-off-by: Lukas Bulwahn Cc: Bill Wendling Cc: Justin Stitt Cc: Nathan Chancellor Cc: Russel King Signed-off-by: Andrew Morton --- lib/Kconfig.debug | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/Kconfig.debug') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 3034e294d50d..e89c024dcbdf 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -332,8 +332,7 @@ config DEBUG_INFO_COMPRESSED_ZLIB depends on $(cc-option,-gz=zlib) depends on $(ld-option,--compress-debug-sections=zlib) help - Compress the debug information using zlib. Requires GCC 5.0+ or Clang - 5.0+, binutils 2.26+, and zlib. + Compress the debug information using zlib. Users of dpkg-deb via debian/rules may find an increase in size of their debug .deb packages with this config set, due to the -- cgit v1.2.3 From 9544f9e6947f6508d29f0d0cc2dacaa749fc1613 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Wed, 15 Oct 2025 14:36:15 +0800 Subject: hung_task: panic when there are more than N hung tasks at the same time The hung_task_panic sysctl is currently a blunt instrument: it's all or nothing. Panicking on a single hung task can be an overreaction to a transient glitch. A more reliable indicator of a systemic problem is when multiple tasks hang simultaneously. Extend hung_task_panic to accept an integer threshold, allowing the kernel to panic only when N hung tasks are detected in a single scan. This provides finer control to distinguish between isolated incidents and system-wide failures. The accepted values are: - 0: Don't panic (unchanged) - 1: Panic on the first hung task (unchanged) - N > 1: Panic after N hung tasks are detected in a single scan The original behavior is preserved for values 0 and 1, maintaining full backward compatibility. [lance.yang@linux.dev: new changelog] Link: https://lkml.kernel.org/r/20251015063615.2632-1-lirongqing@baidu.com Signed-off-by: Li RongQing Reviewed-by: Masami Hiramatsu (Google) Reviewed-by: Lance Yang Tested-by: Lance Yang Acked-by: Andrew Jeffery [aspeed_g5_defconfig] Cc: Anshuman Khandual Cc: Arnd Bergmann Cc: David Hildenbrand Cc: Florian Wesphal Cc: Jakub Kacinski Cc: Jason A. Donenfeld Cc: Joel Granados Cc: Joel Stanley Cc: Jonathan Corbet Cc: Kees Cook Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: "Paul E . McKenney" Cc: Pawan Gupta Cc: Petr Mladek Cc: Phil Auld Cc: Randy Dunlap Cc: Russell King Cc: Shuah Khan Cc: Simon Horman Cc: Stanislav Fomichev Cc: Steven Rostedt Signed-off-by: Andrew Morton --- Documentation/admin-guide/kernel-parameters.txt | 20 +++++++++++++------- Documentation/admin-guide/sysctl/kernel.rst | 9 +++++---- arch/arm/configs/aspeed_g5_defconfig | 2 +- kernel/configs/debug.config | 2 +- kernel/hung_task.c | 15 ++++++++++----- lib/Kconfig.debug | 9 +++++---- tools/testing/selftests/wireguard/qemu/kernel.config | 2 +- 7 files changed, 36 insertions(+), 23 deletions(-) (limited to 'lib/Kconfig.debug') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 6c42061ca20e..b8f8f5d74093 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2010,14 +2010,20 @@ the added memory block itself do not be affected. hung_task_panic= - [KNL] Should the hung task detector generate panics. - Format: 0 | 1 + [KNL] Number of hung tasks to trigger kernel panic. + Format: + + When set to a non-zero value, a kernel panic will be triggered if + the number of detected hung tasks reaches this value. + + 0: don't panic + 1: panic immediately on first hung task + N: panic after N hung tasks are detected in a single scan - A value of 1 instructs the kernel to panic when a - hung task is detected. The default value is controlled - by the CONFIG_BOOTPARAM_HUNG_TASK_PANIC build-time - option. The value selected by this boot parameter can - be changed later by the kernel.hung_task_panic sysctl. + The default value is controlled by the + CONFIG_BOOTPARAM_HUNG_TASK_PANIC build-time option. The value + selected by this boot parameter can be changed later by the + kernel.hung_task_panic sysctl. hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC) terminal devices. Valid values: 0..8 diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index f3ee807b5d8b..0065a55bc09e 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -397,13 +397,14 @@ a hung task is detected. hung_task_panic =============== -Controls the kernel's behavior when a hung task is detected. +When set to a non-zero value, a kernel panic will be triggered if the +number of hung tasks found during a single scan reaches this value. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -= ================================================= += ======================================================= 0 Continue operation. This is the default behavior. -1 Panic immediately. -= ================================================= +N Panic when N hung tasks are found during a single scan. += ======================================================= hung_task_check_count diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig index 61cee1e7ebea..c3b0d5f06889 100644 --- a/arch/arm/configs/aspeed_g5_defconfig +++ b/arch/arm/configs/aspeed_g5_defconfig @@ -308,7 +308,7 @@ CONFIG_PANIC_ON_OOPS=y CONFIG_PANIC_TIMEOUT=-1 CONFIG_SOFTLOCKUP_DETECTOR=y CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y -CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=1 CONFIG_WQ_WATCHDOG=y # CONFIG_SCHED_DEBUG is not set CONFIG_FUNCTION_TRACER=y diff --git a/kernel/configs/debug.config b/kernel/configs/debug.config index e81327d2cd63..9f6ab7dabf67 100644 --- a/kernel/configs/debug.config +++ b/kernel/configs/debug.config @@ -83,7 +83,7 @@ CONFIG_SLUB_DEBUG_ON=y # # Debug Oops, Lockups and Hangs # -# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=0 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_DETECT_HUNG_TASK=y diff --git a/kernel/hung_task.c b/kernel/hung_task.c index b2c1f14b8129..84b4b049faa5 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -81,7 +81,7 @@ static unsigned int __read_mostly sysctl_hung_task_all_cpu_backtrace; * hung task is detected: */ static unsigned int __read_mostly sysctl_hung_task_panic = - IS_ENABLED(CONFIG_BOOTPARAM_HUNG_TASK_PANIC); + CONFIG_BOOTPARAM_HUNG_TASK_PANIC; static int hung_task_panic(struct notifier_block *this, unsigned long event, void *ptr) @@ -218,8 +218,11 @@ static inline void debug_show_blocker(struct task_struct *task, unsigned long ti } #endif -static void check_hung_task(struct task_struct *t, unsigned long timeout) +static void check_hung_task(struct task_struct *t, unsigned long timeout, + unsigned long prev_detect_count) { + unsigned long total_hung_task; + if (!task_is_hung(t, timeout)) return; @@ -229,9 +232,10 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) */ sysctl_hung_task_detect_count++; + total_hung_task = sysctl_hung_task_detect_count - prev_detect_count; trace_sched_process_hang(t); - if (sysctl_hung_task_panic) { + if (sysctl_hung_task_panic && total_hung_task >= sysctl_hung_task_panic) { console_verbose(); hung_task_show_lock = true; hung_task_call_panic = true; @@ -300,6 +304,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) int max_count = sysctl_hung_task_check_count; unsigned long last_break = jiffies; struct task_struct *g, *t; + unsigned long prev_detect_count = sysctl_hung_task_detect_count; /* * If the system crashed already then all bets are off, @@ -320,7 +325,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) last_break = jiffies; } - check_hung_task(t, timeout); + check_hung_task(t, timeout, prev_detect_count); } unlock: rcu_read_unlock(); @@ -389,7 +394,7 @@ static const struct ctl_table hung_task_sysctls[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = SYSCTL_ZERO, - .extra2 = SYSCTL_ONE, + .extra2 = SYSCTL_INT_MAX, }, { .procname = "hung_task_check_count", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e89c024dcbdf..19592a57e1ed 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1257,12 +1257,13 @@ config DEFAULT_HUNG_TASK_TIMEOUT Keeping the default should be fine in most cases. config BOOTPARAM_HUNG_TASK_PANIC - bool "Panic (Reboot) On Hung Tasks" + int "Number of hung tasks to trigger kernel panic" depends on DETECT_HUNG_TASK + default 0 help - Say Y here to enable the kernel to panic on "hung tasks", - which are bugs that cause the kernel to leave a task stuck - in uninterruptible "D" state. + When set to a non-zero value, a kernel panic will be triggered + if the number of hung tasks found during a single scan reaches + this value. The panic can be used in combination with panic_timeout, to cause the system to reboot automatically after a diff --git a/tools/testing/selftests/wireguard/qemu/kernel.config b/tools/testing/selftests/wireguard/qemu/kernel.config index 936b18be07cf..0504c11c2de6 100644 --- a/tools/testing/selftests/wireguard/qemu/kernel.config +++ b/tools/testing/selftests/wireguard/qemu/kernel.config @@ -81,7 +81,7 @@ CONFIG_WQ_WATCHDOG=y CONFIG_DETECT_HUNG_TASK=y CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y -CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=1 CONFIG_PANIC_TIMEOUT=-1 CONFIG_STACKTRACE=y CONFIG_EARLY_PRINTK=y -- cgit v1.2.3 From 7f37d88f5cb32fff454f12cd99444686482ca23b Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Wed, 29 Oct 2025 13:27:43 +0100 Subject: lib/Kconfig.debug: cleanup CONFIG_DEBUG_SECTION_MISMATCH help text Simplify formulations, correct flow, split it into proper paragraphs and update structure. No functional changes. Link: https://lkml.kernel.org/r/20251029122743.1110-1-bp@kernel.org Signed-off-by: Borislav Petkov (AMD) Signed-off-by: Andrew Morton --- lib/Kconfig.debug | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'lib/Kconfig.debug') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 19592a57e1ed..9a087826498a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -483,23 +483,23 @@ config DEBUG_SECTION_MISMATCH bool "Enable full Section mismatch analysis" depends on CC_IS_GCC help - The section mismatch analysis checks if there are illegal - references from one section to another section. - During linktime or runtime, some sections are dropped; - any use of code/data previously in these sections would - most likely result in an oops. - In the code, functions and variables are annotated with - __init,, etc. (see the full list in include/linux/init.h), - which results in the code/data being placed in specific sections. + The section mismatch analysis checks if there are illegal references + from one section to another. During linktime or runtime, some + sections are dropped; any use of code/data previously in these + sections would most likely result in an oops. + + In the code, functions and variables are annotated with __init, + __initdata, and so on (see the full list in include/linux/init.h). + This directs the toolchain to place code/data in specific sections. + The section mismatch analysis is always performed after a full - kernel build, and enabling this option causes the following - additional step to occur: - - Add the option -fno-inline-functions-called-once to gcc commands. - When inlining a function annotated with __init in a non-init - function, we would lose the section information and thus - the analysis would not catch the illegal reference. - This option tells gcc to inline less (but it does result in - a larger kernel). + kernel build, and enabling this option causes the option + -fno-inline-functions-called-once to be added to gcc commands. + + However, when inlining a function annotated with __init in + a non-init function, we would lose the section information and thus + the analysis would not catch the illegal reference. This option + tells gcc to inline less (but it does result in a larger kernel). config SECTION_MISMATCH_WARN_ONLY bool "Make section mismatch errors non-fatal" -- cgit v1.2.3 From 8b365c4f5be9e979bb991a52a0cb4b1e4680c8bd Mon Sep 17 00:00:00 2001 From: Guan-Chun Wu <409411716@gms.tku.edu.tw> Date: Fri, 14 Nov 2025 14:01:57 +0800 Subject: lib: add KUnit tests for base64 encoding/decoding Add a KUnit test suite to validate the base64 helpers. The tests cover both encoding and decoding, including padded and unpadded forms as defined by RFC 4648 (standard base64), and add negative cases for malformed inputs and padding errors. The test suite also validates other variants (URLSAFE, IMAP) to ensure their correctness. In addition to functional checks, the suite includes simple microbenchmarks which report average encode/decode latency for small (64B) and larger (1KB) inputs. These numbers are informational only and do not gate the tests. Kconfig (BASE64_KUNIT) and lib/tests/Makefile are updated accordingly. Sample KUnit output: KTAP version 1 # Subtest: base64 # module: base64_kunit 1..4 # base64_performance_tests: [64B] encode run : 32ns # base64_performance_tests: [64B] decode run : 35ns # base64_performance_tests: [1KB] encode run : 510ns # base64_performance_tests: [1KB] decode run : 530ns ok 1 base64_performance_tests ok 2 base64_std_encode_tests ok 3 base64_std_decode_tests ok 4 base64_variant_tests # base64: pass:4 fail:0 skip:0 total:4 # Totals: pass:4 fail:0 skip:0 total:4 Link: https://lkml.kernel.org/r/20251114060157.89507-1-409411716@gms.tku.edu.tw Signed-off-by: Guan-Chun Wu <409411716@gms.tku.edu.tw> Reviewed-by: Kuan-Wei Chiu Cc: Christoph Hellwig Cc: David Laight Cc: Eric Biggers Cc: Ilya Dryomov Cc: Jaegeuk Kim Cc: Jens Axboe Cc: Keith Busch Cc: Sagi Grimberg Cc: "Theodore Y. Ts'o" Cc: Viacheslav Dubeyko Cc: Xiubo Li Cc: Yu-Sheng Huang Signed-off-by: Andrew Morton --- lib/Kconfig.debug | 19 ++- lib/tests/Makefile | 1 + lib/tests/base64_kunit.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 lib/tests/base64_kunit.c (limited to 'lib/Kconfig.debug') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 9a087826498a..bd3bb7a0c801 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2815,8 +2815,25 @@ config CMDLINE_KUNIT_TEST If unsure, say N. +config BASE64_KUNIT + tristate "KUnit test for base64 decoding and encoding" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This builds the base64 unit tests. + + The tests cover the encoding and decoding logic of Base64 functions + in the kernel. + In addition to correctness checks, simple performance benchmarks + for both encoding and decoding are also included. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config BITS_TEST - tristate "KUnit test for bits.h" if !KUNIT_ALL_TESTS + tristate "KUnit test for bit functions and macros" if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS help diff --git a/lib/tests/Makefile b/lib/tests/Makefile index f7460831cfdd..601dba4b7d96 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -4,6 +4,7 @@ # KUnit tests CFLAGS_bitfield_kunit.o := $(DISABLE_STRUCTLEAK_PLUGIN) +obj-$(CONFIG_BASE64_KUNIT) += base64_kunit.o obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_BITS_TEST) += test_bits.o obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o diff --git a/lib/tests/base64_kunit.c b/lib/tests/base64_kunit.c new file mode 100644 index 000000000000..f7252070c359 --- /dev/null +++ b/lib/tests/base64_kunit.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * base64_kunit_test.c - KUnit tests for base64 encoding and decoding functions + * + * Copyright (c) 2025, Guan-Chun Wu <409411716@gms.tku.edu.tw> + */ + +#include +#include + +/* ---------- Benchmark helpers ---------- */ +static u64 bench_encode_ns(const u8 *data, int len, char *dst, int reps, + enum base64_variant variant) +{ + u64 t0, t1; + + t0 = ktime_get_ns(); + for (int i = 0; i < reps; i++) + base64_encode(data, len, dst, true, variant); + t1 = ktime_get_ns(); + + return div64_u64(t1 - t0, (u64)reps); +} + +static u64 bench_decode_ns(const char *data, int len, u8 *dst, int reps, + enum base64_variant variant) +{ + u64 t0, t1; + + t0 = ktime_get_ns(); + for (int i = 0; i < reps; i++) + base64_decode(data, len, dst, true, variant); + t1 = ktime_get_ns(); + + return div64_u64(t1 - t0, (u64)reps); +} + +static void run_perf_and_check(struct kunit *test, const char *label, int size, + enum base64_variant variant) +{ + const int reps = 1000; + size_t outlen = DIV_ROUND_UP(size, 3) * 4; + u8 *in = kmalloc(size, GFP_KERNEL); + char *enc = kmalloc(outlen, GFP_KERNEL); + u8 *decoded = kmalloc(size, GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, enc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, decoded); + + get_random_bytes(in, size); + int enc_len = base64_encode(in, size, enc, true, variant); + int dec_len = base64_decode(enc, enc_len, decoded, true, variant); + + /* correctness sanity check */ + KUNIT_EXPECT_EQ(test, dec_len, size); + KUNIT_EXPECT_MEMEQ(test, decoded, in, size); + + /* benchmark encode */ + + u64 t1 = bench_encode_ns(in, size, enc, reps, variant); + + kunit_info(test, "[%s] encode run : %lluns", label, t1); + + u64 t2 = bench_decode_ns(enc, enc_len, decoded, reps, variant); + + kunit_info(test, "[%s] decode run : %lluns", label, t2); + + kfree(in); + kfree(enc); + kfree(decoded); +} + +static void base64_performance_tests(struct kunit *test) +{ + /* run on STD variant only */ + run_perf_and_check(test, "64B", 64, BASE64_STD); + run_perf_and_check(test, "1KB", 1024, BASE64_STD); +} + +/* ---------- Helpers for encode ---------- */ +static void expect_encode_ok(struct kunit *test, const u8 *src, int srclen, + const char *expected, bool padding, + enum base64_variant variant) +{ + char buf[128]; + int encoded_len = base64_encode(src, srclen, buf, padding, variant); + + buf[encoded_len] = '\0'; + + KUNIT_EXPECT_EQ(test, encoded_len, strlen(expected)); + KUNIT_EXPECT_STREQ(test, buf, expected); +} + +/* ---------- Helpers for decode ---------- */ +static void expect_decode_ok(struct kunit *test, const char *src, + const u8 *expected, int expected_len, bool padding, + enum base64_variant variant) +{ + u8 buf[128]; + int decoded_len = base64_decode(src, strlen(src), buf, padding, variant); + + KUNIT_EXPECT_EQ(test, decoded_len, expected_len); + KUNIT_EXPECT_MEMEQ(test, buf, expected, expected_len); +} + +static void expect_decode_err(struct kunit *test, const char *src, + int srclen, bool padding, + enum base64_variant variant) +{ + u8 buf[64]; + int decoded_len = base64_decode(src, srclen, buf, padding, variant); + + KUNIT_EXPECT_EQ(test, decoded_len, -1); +} + +/* ---------- Encode Tests ---------- */ +static void base64_std_encode_tests(struct kunit *test) +{ + /* With padding */ + expect_encode_ok(test, (const u8 *)"", 0, "", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"f", 1, "Zg==", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"fo", 2, "Zm8=", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foo", 3, "Zm9v", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foob", 4, "Zm9vYg==", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"fooba", 5, "Zm9vYmE=", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foobar", 6, "Zm9vYmFy", true, BASE64_STD); + + /* Extra cases with padding */ + expect_encode_ok(test, (const u8 *)"Hello, world!", 13, "SGVsbG8sIHdvcmxkIQ==", + true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"abcdefghijklmnopqrstuvwxyz", 26, + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", true, BASE64_STD); + expect_encode_ok(test, (const u8 *)"0123456789+/", 12, "MDEyMzQ1Njc4OSsv", + true, BASE64_STD); + + /* Without padding */ + expect_encode_ok(test, (const u8 *)"", 0, "", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"f", 1, "Zg", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"fo", 2, "Zm8", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foo", 3, "Zm9v", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foob", 4, "Zm9vYg", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"fooba", 5, "Zm9vYmE", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"foobar", 6, "Zm9vYmFy", false, BASE64_STD); + + /* Extra cases without padding */ + expect_encode_ok(test, (const u8 *)"Hello, world!", 13, "SGVsbG8sIHdvcmxkIQ", + false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"abcdefghijklmnopqrstuvwxyz", 26, + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo", false, BASE64_STD); + expect_encode_ok(test, (const u8 *)"0123456789+/", 12, "MDEyMzQ1Njc4OSsv", + false, BASE64_STD); +} + +/* ---------- Decode Tests ---------- */ +static void base64_std_decode_tests(struct kunit *test) +{ + /* -------- With padding --------*/ + expect_decode_ok(test, "", (const u8 *)"", 0, true, BASE64_STD); + expect_decode_ok(test, "Zg==", (const u8 *)"f", 1, true, BASE64_STD); + expect_decode_ok(test, "Zm8=", (const u8 *)"fo", 2, true, BASE64_STD); + expect_decode_ok(test, "Zm9v", (const u8 *)"foo", 3, true, BASE64_STD); + expect_decode_ok(test, "Zm9vYg==", (const u8 *)"foob", 4, true, BASE64_STD); + expect_decode_ok(test, "Zm9vYmE=", (const u8 *)"fooba", 5, true, BASE64_STD); + expect_decode_ok(test, "Zm9vYmFy", (const u8 *)"foobar", 6, true, BASE64_STD); + expect_decode_ok(test, "SGVsbG8sIHdvcmxkIQ==", (const u8 *)"Hello, world!", 13, + true, BASE64_STD); + expect_decode_ok(test, "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", + (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, true, BASE64_STD); + expect_decode_ok(test, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", + (const u8 *)"abcdefghijklmnopqrstuvwxyz", 26, true, BASE64_STD); + + /* Error cases */ + expect_decode_err(test, "Zg=!", 4, true, BASE64_STD); + expect_decode_err(test, "Zm$=", 4, true, BASE64_STD); + expect_decode_err(test, "Z===", 4, true, BASE64_STD); + expect_decode_err(test, "Zg", 2, true, BASE64_STD); + expect_decode_err(test, "Zm9v====", 8, true, BASE64_STD); + expect_decode_err(test, "Zm==A", 5, true, BASE64_STD); + + { + char with_nul[4] = { 'Z', 'g', '\0', '=' }; + + expect_decode_err(test, with_nul, 4, true, BASE64_STD); + } + + /* -------- Without padding --------*/ + expect_decode_ok(test, "", (const u8 *)"", 0, false, BASE64_STD); + expect_decode_ok(test, "Zg", (const u8 *)"f", 1, false, BASE64_STD); + expect_decode_ok(test, "Zm8", (const u8 *)"fo", 2, false, BASE64_STD); + expect_decode_ok(test, "Zm9v", (const u8 *)"foo", 3, false, BASE64_STD); + expect_decode_ok(test, "Zm9vYg", (const u8 *)"foob", 4, false, BASE64_STD); + expect_decode_ok(test, "Zm9vYmE", (const u8 *)"fooba", 5, false, BASE64_STD); + expect_decode_ok(test, "Zm9vYmFy", (const u8 *)"foobar", 6, false, BASE64_STD); + expect_decode_ok(test, "TWFu", (const u8 *)"Man", 3, false, BASE64_STD); + expect_decode_ok(test, "SGVsbG8sIHdvcmxkIQ", (const u8 *)"Hello, world!", 13, + false, BASE64_STD); + expect_decode_ok(test, "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", + (const u8 *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, false, BASE64_STD); + expect_decode_ok(test, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo", + (const u8 *)"abcdefghijklmnopqrstuvwxyz", 26, false, BASE64_STD); + expect_decode_ok(test, "MDEyMzQ1Njc4OSsv", (const u8 *)"0123456789+/", 12, + false, BASE64_STD); + + /* Error cases */ + expect_decode_err(test, "Zg=!", 4, false, BASE64_STD); + expect_decode_err(test, "Zm$=", 4, false, BASE64_STD); + expect_decode_err(test, "Z===", 4, false, BASE64_STD); + expect_decode_err(test, "Zg=", 3, false, BASE64_STD); + expect_decode_err(test, "Zm9v====", 8, false, BASE64_STD); + expect_decode_err(test, "Zm==v", 4, false, BASE64_STD); + + { + char with_nul[4] = { 'Z', 'g', '\0', '=' }; + + expect_decode_err(test, with_nul, 4, false, BASE64_STD); + } +} + +/* ---------- Variant tests (URLSAFE / IMAP) ---------- */ +static void base64_variant_tests(struct kunit *test) +{ + const u8 sample1[] = { 0x00, 0xfb, 0xff, 0x7f, 0x80 }; + char std_buf[128], url_buf[128], imap_buf[128]; + u8 back[128]; + int n_std, n_url, n_imap, m; + int i; + + n_std = base64_encode(sample1, sizeof(sample1), std_buf, false, BASE64_STD); + n_url = base64_encode(sample1, sizeof(sample1), url_buf, false, BASE64_URLSAFE); + std_buf[n_std] = '\0'; + url_buf[n_url] = '\0'; + + for (i = 0; i < n_std; i++) { + if (std_buf[i] == '+') + std_buf[i] = '-'; + else if (std_buf[i] == '/') + std_buf[i] = '_'; + } + KUNIT_EXPECT_STREQ(test, std_buf, url_buf); + + m = base64_decode(url_buf, n_url, back, false, BASE64_URLSAFE); + KUNIT_EXPECT_EQ(test, m, (int)sizeof(sample1)); + KUNIT_EXPECT_MEMEQ(test, back, sample1, sizeof(sample1)); + + n_std = base64_encode(sample1, sizeof(sample1), std_buf, false, BASE64_STD); + n_imap = base64_encode(sample1, sizeof(sample1), imap_buf, false, BASE64_IMAP); + std_buf[n_std] = '\0'; + imap_buf[n_imap] = '\0'; + + for (i = 0; i < n_std; i++) + if (std_buf[i] == '/') + std_buf[i] = ','; + KUNIT_EXPECT_STREQ(test, std_buf, imap_buf); + + m = base64_decode(imap_buf, n_imap, back, false, BASE64_IMAP); + KUNIT_EXPECT_EQ(test, m, (int)sizeof(sample1)); + KUNIT_EXPECT_MEMEQ(test, back, sample1, sizeof(sample1)); + + { + const char *bad = "Zg=="; + u8 tmp[8]; + + m = base64_decode(bad, strlen(bad), tmp, false, BASE64_URLSAFE); + KUNIT_EXPECT_EQ(test, m, -1); + + m = base64_decode(bad, strlen(bad), tmp, false, BASE64_IMAP); + KUNIT_EXPECT_EQ(test, m, -1); + } +} + +/* ---------- Test registration ---------- */ +static struct kunit_case base64_test_cases[] = { + KUNIT_CASE(base64_performance_tests), + KUNIT_CASE(base64_std_encode_tests), + KUNIT_CASE(base64_std_decode_tests), + KUNIT_CASE(base64_variant_tests), + {} +}; + +static struct kunit_suite base64_test_suite = { + .name = "base64", + .test_cases = base64_test_cases, +}; + +kunit_test_suite(base64_test_suite); + +MODULE_AUTHOR("Guan-Chun Wu <409411716@gms.tku.edu.tw>"); +MODULE_DESCRIPTION("KUnit tests for Base64 encoding/decoding, including performance checks"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3