mirror of
https://github.com/torvalds/linux.git
synced 2026-05-05 23:05:25 -04:00
Merge tag 'v6.11-rc1' into for-6.12
Linux 6.11-rc1
This commit is contained in:
@@ -539,13 +539,7 @@ config CPUMASK_OFFSTACK
|
||||
stack overflow.
|
||||
|
||||
config FORCE_NR_CPUS
|
||||
bool "Set number of CPUs at compile time"
|
||||
depends on SMP && EXPERT && !COMPILE_TEST
|
||||
help
|
||||
Say Yes if you have NR_CPUS set to an actual number of possible
|
||||
CPUs in your system, not to a default value. This forces the core
|
||||
code to rely on compile-time value and optimize kernel routines
|
||||
better.
|
||||
def_bool !SMP
|
||||
|
||||
config CPU_RMAP
|
||||
bool
|
||||
@@ -629,6 +623,7 @@ config SIGNATURE
|
||||
|
||||
config DIMLIB
|
||||
tristate
|
||||
depends on NET
|
||||
help
|
||||
Dynamic Interrupt Moderation library.
|
||||
Implements an algorithm for dynamically changing CQ moderation values
|
||||
|
||||
@@ -1043,7 +1043,9 @@ config PANIC_TIMEOUT
|
||||
Set the timeout value (in seconds) until a reboot occurs when
|
||||
the kernel panics. If n = 0, then we wait forever. A timeout
|
||||
value n > 0 will wait n seconds before rebooting, while a timeout
|
||||
value n < 0 will reboot immediately.
|
||||
value n < 0 will reboot immediately. This setting can be overridden
|
||||
with the kernel command line option panic=, and from userspace via
|
||||
/proc/sys/kernel/panic.
|
||||
|
||||
config LOCKUP_DETECTOR
|
||||
bool
|
||||
@@ -2505,18 +2507,6 @@ config TEST_VMALLOC
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_USER_COPY
|
||||
tristate "Test user/kernel boundary protections"
|
||||
depends on m
|
||||
help
|
||||
This builds the "test_user_copy" module that runs sanity checks
|
||||
on the copy_to/from_user infrastructure, making sure basic
|
||||
user/kernel boundary testing is working. If it fails to load,
|
||||
a regression has been detected in the user/kernel memory boundary
|
||||
protections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_BPF
|
||||
tristate "Test BPF filter functionality"
|
||||
depends on m && NET
|
||||
@@ -2814,6 +2804,15 @@ config SIPHASH_KUNIT_TEST
|
||||
This is intended to help people writing architecture-specific
|
||||
optimized versions. If unsure, say N.
|
||||
|
||||
config USERCOPY_KUNIT_TEST
|
||||
tristate "KUnit Test for user/kernel boundary protections"
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds the "usercopy_kunit" module that runs sanity checks
|
||||
on the copy_to/from_user infrastructure, making sure basic
|
||||
user/kernel boundary testing is working.
|
||||
|
||||
config TEST_UDELAY
|
||||
tristate "udelay test driver"
|
||||
help
|
||||
|
||||
@@ -78,7 +78,6 @@ obj-$(CONFIG_TEST_LKM) += test_module.o
|
||||
obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
|
||||
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
|
||||
obj-$(CONFIG_TEST_SORT) += test_sort.o
|
||||
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
|
||||
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
|
||||
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
|
||||
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
|
||||
@@ -388,6 +387,7 @@ CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-truncation)
|
||||
CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
|
||||
obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
|
||||
obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
|
||||
obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
|
||||
|
||||
@@ -426,3 +426,7 @@ $(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
|
||||
ifeq ($(CONFIG_FORTIFY_SOURCE),y)
|
||||
$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
|
||||
endif
|
||||
|
||||
# Some architectures define __NO_FORTIFY if __SANITIZE_ADDRESS__ is undefined.
|
||||
# Pass CFLAGS_KASAN to avoid warnings.
|
||||
$(foreach x, $(patsubst %.log,%.o,$(TEST_FORTIFY_LOGS)), $(eval KASAN_SANITIZE_$(x) := y))
|
||||
|
||||
@@ -227,6 +227,7 @@ struct page_ext_operations page_alloc_tagging_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL(page_alloc_tagging_ops);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static struct ctl_table memory_allocation_profiling_sysctls[] = {
|
||||
{
|
||||
.procname = "mem_profiling",
|
||||
@@ -238,9 +239,19 @@ static struct ctl_table memory_allocation_profiling_sysctls[] = {
|
||||
#endif
|
||||
.proc_handler = proc_do_static_key,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static void __init sysctl_init(void)
|
||||
{
|
||||
if (!mem_profiling_support)
|
||||
memory_allocation_profiling_sysctls[0].mode = 0444;
|
||||
|
||||
register_sysctl_init("vm", memory_allocation_profiling_sysctls);
|
||||
}
|
||||
#else /* CONFIG_SYSCTL */
|
||||
static inline void sysctl_init(void) {}
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static int __init alloc_tag_init(void)
|
||||
{
|
||||
const struct codetag_type_desc desc = {
|
||||
@@ -253,9 +264,7 @@ static int __init alloc_tag_init(void)
|
||||
if (IS_ERR(alloc_tag_cttype))
|
||||
return PTR_ERR(alloc_tag_cttype);
|
||||
|
||||
if (!mem_profiling_support)
|
||||
memory_allocation_profiling_sysctls[0].mode = 0444;
|
||||
register_sysctl_init("vm", memory_allocation_profiling_sysctls);
|
||||
sysctl_init();
|
||||
procfs_init();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -449,4 +449,5 @@ asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_boolean);
|
||||
|
||||
MODULE_DESCRIPTION("Simple encoder primitives for ASN.1 BER/DER/CER");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -273,4 +273,5 @@ static __exit void test_atomics_exit(void) {}
|
||||
module_init(test_atomics_init);
|
||||
module_exit(test_atomics_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Testsuite for atomic64_t functions");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
20
lib/bch.c
20
lib/bch.c
@@ -479,11 +479,8 @@ static int solve_linear_system(struct bch_control *bch, unsigned int *rows,
|
||||
/* find suitable row for elimination */
|
||||
for (r = p; r < m; r++) {
|
||||
if (rows[r] & mask) {
|
||||
if (r != p) {
|
||||
tmp = rows[r];
|
||||
rows[r] = rows[p];
|
||||
rows[p] = tmp;
|
||||
}
|
||||
if (r != p)
|
||||
swap(rows[r], rows[p]);
|
||||
rem = r+1;
|
||||
break;
|
||||
}
|
||||
@@ -799,21 +796,14 @@ static void gf_poly_div(struct bch_control *bch, struct gf_poly *a,
|
||||
static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a,
|
||||
struct gf_poly *b)
|
||||
{
|
||||
struct gf_poly *tmp;
|
||||
|
||||
dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b));
|
||||
|
||||
if (a->deg < b->deg) {
|
||||
tmp = b;
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
if (a->deg < b->deg)
|
||||
swap(a, b);
|
||||
|
||||
while (b->deg > 0) {
|
||||
gf_poly_mod(bch, a, b, NULL);
|
||||
tmp = b;
|
||||
b = a;
|
||||
a = tmp;
|
||||
swap(a, b);
|
||||
}
|
||||
|
||||
dbg("%s\n", gf_poly_str(a));
|
||||
|
||||
@@ -151,4 +151,5 @@ static struct kunit_suite bitfields_test_suite = {
|
||||
kunit_test_suites(&bitfields_test_suite);
|
||||
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
||||
MODULE_DESCRIPTION("Test cases for bitfield helpers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
* Masami Hiramatsu <mhiramat@kernel.org>
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* NOTE: This is only for tools/bootconfig, because tools/bootconfig will
|
||||
* run the parser sanity test.
|
||||
* This does NOT mean lib/bootconfig.c is available in the user space.
|
||||
* However, if you change this file, please make sure the tools/bootconfig
|
||||
* has no issue on building and running.
|
||||
*/
|
||||
#include <linux/bootconfig.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/bug.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
@@ -24,16 +32,6 @@ const char * __init xbc_get_embedded_bootconfig(size_t *size)
|
||||
return (*size) ? embedded_bootconfig_data : NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* !__KERNEL__ */
|
||||
/*
|
||||
* NOTE: This is only for tools/bootconfig, because tools/bootconfig will
|
||||
* run the parser sanity test.
|
||||
* This does NOT mean lib/bootconfig.c is available in the user space.
|
||||
* However, if you change this file, please make sure the tools/bootconfig
|
||||
* has no issue on building and running.
|
||||
*/
|
||||
#include <linux/bootconfig.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
@@ -38,7 +38,9 @@ close IN_FILE || die;
|
||||
#
|
||||
open C_FILE, ">$ARGV[1]" or die;
|
||||
print C_FILE "/*\n";
|
||||
print C_FILE " * Automatically generated by ", $0 =~ s#^\Q$abs_srctree/\E##r, ". Do not edit\n";
|
||||
my $scriptname = $0;
|
||||
$scriptname =~ s#^\Q$abs_srctree/\E##;
|
||||
print C_FILE " * Automatically generated by ", $scriptname, ". Do not edit\n";
|
||||
print C_FILE " */\n";
|
||||
|
||||
#
|
||||
|
||||
@@ -73,6 +73,13 @@ static int get_build_id_32(const void *page_addr, unsigned char *build_id,
|
||||
Elf32_Phdr *phdr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* Neither ELF spec nor ELF loader require that program headers
|
||||
* start immediately after ELF header.
|
||||
*/
|
||||
if (ehdr->e_phoff != sizeof(Elf32_Ehdr))
|
||||
return -EINVAL;
|
||||
/* only supports phdr that fits in one page */
|
||||
if (ehdr->e_phnum >
|
||||
(PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
|
||||
@@ -98,6 +105,13 @@ static int get_build_id_64(const void *page_addr, unsigned char *build_id,
|
||||
Elf64_Phdr *phdr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* Neither ELF spec nor ELF loader require that program headers
|
||||
* start immediately after ELF header.
|
||||
*/
|
||||
if (ehdr->e_phoff != sizeof(Elf64_Ehdr))
|
||||
return -EINVAL;
|
||||
/* only supports phdr that fits in one page */
|
||||
if (ehdr->e_phnum >
|
||||
(PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
|
||||
|
||||
@@ -639,4 +639,5 @@ static struct kunit_suite checksum_test_suite = {
|
||||
kunit_test_suites(&checksum_test_suite);
|
||||
|
||||
MODULE_AUTHOR("Noah Goldstein <goldstein.w.n@gmail.com>");
|
||||
MODULE_DESCRIPTION("Test cases csum_* APIs");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -13,14 +13,25 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/debug.h>
|
||||
|
||||
static inline void closure_put_after_sub(struct closure *cl, int flags)
|
||||
static inline void closure_put_after_sub_checks(int flags)
|
||||
{
|
||||
int r = flags & CLOSURE_REMAINING_MASK;
|
||||
|
||||
BUG_ON(flags & CLOSURE_GUARD_MASK);
|
||||
BUG_ON(!r && (flags & ~CLOSURE_DESTRUCTOR));
|
||||
if (WARN(flags & CLOSURE_GUARD_MASK,
|
||||
"closure has guard bits set: %x (%u)",
|
||||
flags & CLOSURE_GUARD_MASK, (unsigned) __fls(r)))
|
||||
r &= ~CLOSURE_GUARD_MASK;
|
||||
|
||||
if (!r) {
|
||||
WARN(!r && (flags & ~CLOSURE_DESTRUCTOR),
|
||||
"closure ref hit 0 with incorrect flags set: %x (%u)",
|
||||
flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags));
|
||||
}
|
||||
|
||||
static inline void closure_put_after_sub(struct closure *cl, int flags)
|
||||
{
|
||||
closure_put_after_sub_checks(flags);
|
||||
|
||||
if (!(flags & CLOSURE_REMAINING_MASK)) {
|
||||
smp_acquire__after_ctrl_dep();
|
||||
|
||||
cl->closure_get_happened = false;
|
||||
@@ -139,6 +150,41 @@ void __sched __closure_sync(struct closure *cl)
|
||||
}
|
||||
EXPORT_SYMBOL(__closure_sync);
|
||||
|
||||
/*
|
||||
* closure_return_sync - finish running a closure, synchronously (i.e. waiting
|
||||
* for outstanding get()s to finish) and returning once closure refcount is 0.
|
||||
*
|
||||
* Unlike closure_sync() this doesn't reinit the ref to 1; subsequent
|
||||
* closure_get_not_zero() calls waill fail.
|
||||
*/
|
||||
void __sched closure_return_sync(struct closure *cl)
|
||||
{
|
||||
struct closure_syncer s = { .task = current };
|
||||
|
||||
cl->s = &s;
|
||||
set_closure_fn(cl, closure_sync_fn, NULL);
|
||||
|
||||
unsigned flags = atomic_sub_return_release(1 + CLOSURE_RUNNING - CLOSURE_DESTRUCTOR,
|
||||
&cl->remaining);
|
||||
|
||||
closure_put_after_sub_checks(flags);
|
||||
|
||||
if (unlikely(flags & CLOSURE_REMAINING_MASK)) {
|
||||
while (1) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
if (s.done)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
if (cl->parent)
|
||||
closure_put(cl->parent);
|
||||
}
|
||||
EXPORT_SYMBOL(closure_return_sync);
|
||||
|
||||
int __sched __closure_sync_timeout(struct closure *cl, unsigned long timeout)
|
||||
{
|
||||
struct closure_syncer s = { .task = current };
|
||||
@@ -198,6 +244,9 @@ void closure_debug_destroy(struct closure *cl)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (cl->magic == CLOSURE_MAGIC_STACK)
|
||||
return;
|
||||
|
||||
BUG_ON(cl->magic != CLOSURE_MAGIC_ALIVE);
|
||||
cl->magic = CLOSURE_MAGIC_DEAD;
|
||||
|
||||
|
||||
@@ -153,4 +153,5 @@ static struct kunit_suite cmdline_test_suite = {
|
||||
};
|
||||
kunit_test_suite(cmdline_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for API provided by cmdline.c");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -152,4 +152,5 @@ static struct kunit_suite test_cpumask_suite = {
|
||||
};
|
||||
kunit_test_suite(test_cpumask_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for cpumask");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -71,4 +71,5 @@ void arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len)
|
||||
}
|
||||
EXPORT_SYMBOL(arc4_crypt);
|
||||
|
||||
MODULE_DESCRIPTION("ARC4 Cipher Algorithm");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -899,4 +899,5 @@ void des3_ede_decrypt(const struct des3_ede_ctx *dctx, u8 *dst, const u8 *src)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(des3_ede_decrypt);
|
||||
|
||||
MODULE_DESCRIPTION("DES & Triple DES EDE Cipher Algorithms");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -32,4 +32,5 @@ void chacha_crypt_generic(u32 *state, u8 *dst, const u8 *src,
|
||||
}
|
||||
EXPORT_SYMBOL(chacha_crypt_generic);
|
||||
|
||||
MODULE_DESCRIPTION("ChaCha stream cipher (RFC7539)");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1285,14 +1285,12 @@ void mpi_ec_mul_point(MPI_POINT result,
|
||||
sum = &p2_;
|
||||
|
||||
for (j = nbits-1; j >= 0; j--) {
|
||||
MPI_POINT t;
|
||||
|
||||
sw = mpi_test_bit(scalar, j);
|
||||
point_swap_cond(q1, q2, sw, ctx);
|
||||
montgomery_ladder(prd, sum, q1, q2, point->x, ctx);
|
||||
point_swap_cond(prd, sum, sw, ctx);
|
||||
t = q1; q1 = prd; prd = t;
|
||||
t = q2; q2 = sum; sum = t;
|
||||
swap(q1, prd);
|
||||
swap(q2, sum);
|
||||
}
|
||||
|
||||
mpi_clear(result->y);
|
||||
|
||||
@@ -212,12 +212,10 @@ void mpi_rshift(MPI x, MPI a, unsigned int n)
|
||||
return;
|
||||
}
|
||||
|
||||
if (nlimbs) {
|
||||
for (i = 0; i < x->nlimbs - nlimbs; i++)
|
||||
x->d[i] = x->d[i+nlimbs];
|
||||
x->d[i] = 0;
|
||||
x->nlimbs -= nlimbs;
|
||||
}
|
||||
for (i = 0; i < x->nlimbs - nlimbs; i++)
|
||||
x->d[i] = x->d[i+nlimbs];
|
||||
x->d[i] = 0;
|
||||
x->nlimbs -= nlimbs;
|
||||
|
||||
if (x->nlimbs && nbits)
|
||||
mpihelp_rshift(x->d, x->d, x->nlimbs, nbits);
|
||||
|
||||
@@ -176,7 +176,6 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
|
||||
|
||||
for (;;) {
|
||||
while (c) {
|
||||
mpi_ptr_t tp;
|
||||
mpi_size_t xsize;
|
||||
|
||||
/*if (mpihelp_mul_n(xp, rp, rp, rsize) < 0) goto enomem */
|
||||
@@ -207,9 +206,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
|
||||
xsize = msize;
|
||||
}
|
||||
|
||||
tp = rp;
|
||||
rp = xp;
|
||||
xp = tp;
|
||||
swap(rp, xp);
|
||||
rsize = xsize;
|
||||
|
||||
if ((mpi_limb_signed_t) e < 0) {
|
||||
@@ -235,9 +232,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
|
||||
xsize = msize;
|
||||
}
|
||||
|
||||
tp = rp;
|
||||
rp = xp;
|
||||
xp = tp;
|
||||
swap(rp, xp);
|
||||
rsize = xsize;
|
||||
}
|
||||
e <<= 1;
|
||||
|
||||
@@ -76,3 +76,4 @@ EXPORT_SYMBOL_GPL(poly1305_final_generic);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
|
||||
MODULE_DESCRIPTION("Poly1305 authenticator algorithm, RFC7539");
|
||||
|
||||
@@ -137,4 +137,5 @@ void sha1_init(__u32 *buf)
|
||||
}
|
||||
EXPORT_SYMBOL(sha1_init);
|
||||
|
||||
MODULE_DESCRIPTION("SHA-1 Algorithm");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -165,4 +165,5 @@ void sha256(const u8 *data, unsigned int len, u8 *out)
|
||||
}
|
||||
EXPORT_SYMBOL(sha256);
|
||||
|
||||
MODULE_DESCRIPTION("SHA-256 Algorithm");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -85,4 +85,5 @@ void __crypto_xor(u8 *dst, const u8 *src1, const u8 *src2, unsigned int len)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__crypto_xor);
|
||||
|
||||
MODULE_DESCRIPTION("Crypto library utility functions");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -78,16 +78,17 @@ static bool obj_freeing;
|
||||
/* The number of objs on the global free list */
|
||||
static int obj_nr_tofree;
|
||||
|
||||
static int debug_objects_maxchain __read_mostly;
|
||||
static int __maybe_unused debug_objects_maxchecked __read_mostly;
|
||||
static int debug_objects_fixups __read_mostly;
|
||||
static int debug_objects_warnings __read_mostly;
|
||||
static int debug_objects_enabled __read_mostly
|
||||
= CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
|
||||
static int debug_objects_pool_size __read_mostly
|
||||
= ODEBUG_POOL_SIZE;
|
||||
static int debug_objects_pool_min_level __read_mostly
|
||||
= ODEBUG_POOL_MIN_LEVEL;
|
||||
static int __data_racy debug_objects_maxchain __read_mostly;
|
||||
static int __data_racy __maybe_unused debug_objects_maxchecked __read_mostly;
|
||||
static int __data_racy debug_objects_fixups __read_mostly;
|
||||
static int __data_racy debug_objects_warnings __read_mostly;
|
||||
static int __data_racy debug_objects_enabled __read_mostly
|
||||
= CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
|
||||
static int __data_racy debug_objects_pool_size __read_mostly
|
||||
= ODEBUG_POOL_SIZE;
|
||||
static int __data_racy debug_objects_pool_min_level __read_mostly
|
||||
= ODEBUG_POOL_MIN_LEVEL;
|
||||
|
||||
static const struct debug_obj_descr *descr_test __read_mostly;
|
||||
static struct kmem_cache *obj_cache __ro_after_init;
|
||||
|
||||
|
||||
@@ -232,7 +232,8 @@ static int INIT get_next_block(struct bunzip_data *bd)
|
||||
RUNB) */
|
||||
symCount = symTotal+2;
|
||||
for (j = 0; j < groupCount; j++) {
|
||||
unsigned char length[MAX_SYMBOLS], temp[MAX_HUFCODE_BITS+1];
|
||||
unsigned char length[MAX_SYMBOLS];
|
||||
unsigned short temp[MAX_HUFCODE_BITS+1];
|
||||
int minLen, maxLen, pp;
|
||||
/* Read Huffman code lengths for each symbol. They're
|
||||
stored in a way similar to mtf; record a starting
|
||||
|
||||
@@ -83,4 +83,5 @@ static int __init dhry_init(void)
|
||||
module_init(dhry_init);
|
||||
|
||||
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
|
||||
MODULE_DESCRIPTION("Dhrystone benchmark test module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/dim.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
/*
|
||||
* Net DIM profiles:
|
||||
@@ -11,12 +12,6 @@
|
||||
* There are different set of profiles for RX/TX CQs.
|
||||
* Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
|
||||
*/
|
||||
#define NET_DIM_PARAMS_NUM_PROFILES 5
|
||||
#define NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE 256
|
||||
#define NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE 128
|
||||
#define NET_DIM_DEF_PROFILE_CQE 1
|
||||
#define NET_DIM_DEF_PROFILE_EQE 1
|
||||
|
||||
#define NET_DIM_RX_EQE_PROFILES { \
|
||||
{.usec = 1, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
|
||||
{.usec = 8, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
|
||||
@@ -101,6 +96,143 @@ net_dim_get_def_tx_moderation(u8 cq_period_mode)
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
|
||||
|
||||
int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags,
|
||||
u8 coal_flags, u8 rx_mode, u8 tx_mode,
|
||||
void (*rx_dim_work)(struct work_struct *work),
|
||||
void (*tx_dim_work)(struct work_struct *work))
|
||||
{
|
||||
struct dim_cq_moder *rxp = NULL, *txp;
|
||||
struct dim_irq_moder *moder;
|
||||
int len;
|
||||
|
||||
dev->irq_moder = kzalloc(sizeof(*dev->irq_moder), GFP_KERNEL);
|
||||
if (!dev->irq_moder)
|
||||
return -ENOMEM;
|
||||
|
||||
moder = dev->irq_moder;
|
||||
len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*moder->rx_profile);
|
||||
|
||||
moder->coal_flags = coal_flags;
|
||||
moder->profile_flags = profile_flags;
|
||||
|
||||
if (profile_flags & DIM_PROFILE_RX) {
|
||||
moder->rx_dim_work = rx_dim_work;
|
||||
moder->dim_rx_mode = rx_mode;
|
||||
rxp = kmemdup(rx_profile[rx_mode], len, GFP_KERNEL);
|
||||
if (!rxp)
|
||||
goto free_moder;
|
||||
|
||||
rcu_assign_pointer(moder->rx_profile, rxp);
|
||||
}
|
||||
|
||||
if (profile_flags & DIM_PROFILE_TX) {
|
||||
moder->tx_dim_work = tx_dim_work;
|
||||
moder->dim_tx_mode = tx_mode;
|
||||
txp = kmemdup(tx_profile[tx_mode], len, GFP_KERNEL);
|
||||
if (!txp)
|
||||
goto free_rxp;
|
||||
|
||||
rcu_assign_pointer(moder->tx_profile, txp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_rxp:
|
||||
kfree(rxp);
|
||||
free_moder:
|
||||
kfree(moder);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_init_irq_moder);
|
||||
|
||||
/* RTNL lock is held. */
|
||||
void net_dim_free_irq_moder(struct net_device *dev)
|
||||
{
|
||||
struct dim_cq_moder *rxp, *txp;
|
||||
|
||||
if (!dev->irq_moder)
|
||||
return;
|
||||
|
||||
rxp = rtnl_dereference(dev->irq_moder->rx_profile);
|
||||
txp = rtnl_dereference(dev->irq_moder->tx_profile);
|
||||
|
||||
rcu_assign_pointer(dev->irq_moder->rx_profile, NULL);
|
||||
rcu_assign_pointer(dev->irq_moder->tx_profile, NULL);
|
||||
|
||||
kfree_rcu(rxp, rcu);
|
||||
kfree_rcu(txp, rcu);
|
||||
kfree(dev->irq_moder);
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_free_irq_moder);
|
||||
|
||||
void net_dim_setting(struct net_device *dev, struct dim *dim, bool is_tx)
|
||||
{
|
||||
struct dim_irq_moder *irq_moder = dev->irq_moder;
|
||||
|
||||
if (!irq_moder)
|
||||
return;
|
||||
|
||||
if (is_tx) {
|
||||
INIT_WORK(&dim->work, irq_moder->tx_dim_work);
|
||||
dim->mode = READ_ONCE(irq_moder->dim_tx_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_WORK(&dim->work, irq_moder->rx_dim_work);
|
||||
dim->mode = READ_ONCE(irq_moder->dim_rx_mode);
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_setting);
|
||||
|
||||
void net_dim_work_cancel(struct dim *dim)
|
||||
{
|
||||
cancel_work_sync(&dim->work);
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_work_cancel);
|
||||
|
||||
struct dim_cq_moder net_dim_get_rx_irq_moder(struct net_device *dev,
|
||||
struct dim *dim)
|
||||
{
|
||||
struct dim_cq_moder res, *profile;
|
||||
|
||||
rcu_read_lock();
|
||||
profile = rcu_dereference(dev->irq_moder->rx_profile);
|
||||
res = profile[dim->profile_ix];
|
||||
rcu_read_unlock();
|
||||
|
||||
res.cq_period_mode = dim->mode;
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_get_rx_irq_moder);
|
||||
|
||||
struct dim_cq_moder net_dim_get_tx_irq_moder(struct net_device *dev,
|
||||
struct dim *dim)
|
||||
{
|
||||
struct dim_cq_moder res, *profile;
|
||||
|
||||
rcu_read_lock();
|
||||
profile = rcu_dereference(dev->irq_moder->tx_profile);
|
||||
res = profile[dim->profile_ix];
|
||||
rcu_read_unlock();
|
||||
|
||||
res.cq_period_mode = dim->mode;
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_get_tx_irq_moder);
|
||||
|
||||
void net_dim_set_rx_mode(struct net_device *dev, u8 rx_mode)
|
||||
{
|
||||
WRITE_ONCE(dev->irq_moder->dim_rx_mode, rx_mode);
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_set_rx_mode);
|
||||
|
||||
void net_dim_set_tx_mode(struct net_device *dev, u8 tx_mode)
|
||||
{
|
||||
WRITE_ONCE(dev->irq_moder->dim_tx_mode, tx_mode);
|
||||
}
|
||||
EXPORT_SYMBOL(net_dim_set_tx_mode);
|
||||
|
||||
static int net_dim_step(struct dim *dim)
|
||||
{
|
||||
if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
|
||||
|
||||
@@ -54,14 +54,19 @@ void __init dump_stack_set_arch_desc(const char *fmt, ...)
|
||||
*/
|
||||
void dump_stack_print_info(const char *log_lvl)
|
||||
{
|
||||
printk("%sCPU: %d PID: %d Comm: %.20s %s%s %s %.*s" BUILD_ID_FMT "\n",
|
||||
log_lvl, raw_smp_processor_id(), current->pid, current->comm,
|
||||
printk("%sCPU: %d UID: %u PID: %d Comm: %.20s %s%s %s %.*s" BUILD_ID_FMT "\n",
|
||||
log_lvl, raw_smp_processor_id(),
|
||||
__kuid_val(current_real_cred()->euid),
|
||||
current->pid, current->comm,
|
||||
kexec_crash_loaded() ? "Kdump: loaded " : "",
|
||||
print_tainted(),
|
||||
init_utsname()->release,
|
||||
(int)strcspn(init_utsname()->version, " "),
|
||||
init_utsname()->version, BUILD_ID_VAL);
|
||||
|
||||
if (get_taint())
|
||||
printk("%s%s\n", log_lvl, print_tainted_verbose());
|
||||
|
||||
if (dump_stack_arch_desc_str[0] != '\0')
|
||||
printk("%sHardware name: %s\n",
|
||||
log_lvl, dump_stack_arch_desc_str);
|
||||
|
||||
@@ -194,4 +194,5 @@ static int __init find_bit_test(void)
|
||||
}
|
||||
module_init(find_bit_test);
|
||||
|
||||
MODULE_DESCRIPTION("Test for find_*_bit functions");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -10,7 +10,7 @@ if FONT_SUPPORT
|
||||
|
||||
config FONTS
|
||||
bool "Select compiled-in fonts"
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
|
||||
help
|
||||
Say Y here if you would like to use fonts other than the default
|
||||
your frame buffer console usually use.
|
||||
@@ -23,7 +23,7 @@ config FONTS
|
||||
|
||||
config FONT_8x8
|
||||
bool "VGA 8x8 font" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
|
||||
default y if !SPARC && !FONTS
|
||||
help
|
||||
This is the "high resolution" font for the VGA frame buffer (the one
|
||||
@@ -46,7 +46,7 @@ config FONT_8x16
|
||||
|
||||
config FONT_6x11
|
||||
bool "Mac console 6x11 font (not supported by all drivers)" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
|
||||
default y if !SPARC && !FONTS && MAC
|
||||
help
|
||||
Small console font with Macintosh-style high-half glyphs. Some Mac
|
||||
@@ -54,7 +54,7 @@ config FONT_6x11
|
||||
|
||||
config FONT_7x14
|
||||
bool "console 7x14 font (not supported by all drivers)" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
help
|
||||
Console font with characters just a bit smaller than the default.
|
||||
If the standard 8x16 font is a little too big for you, say Y.
|
||||
@@ -62,7 +62,7 @@ config FONT_7x14
|
||||
|
||||
config FONT_PEARL_8x8
|
||||
bool "Pearl (old m68k) console 8x8 font" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
default y if !SPARC && !FONTS && AMIGA
|
||||
help
|
||||
Small console font with PC-style control-character and high-half
|
||||
@@ -70,7 +70,7 @@ config FONT_PEARL_8x8
|
||||
|
||||
config FONT_ACORN_8x8
|
||||
bool "Acorn console 8x8 font" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
default y if !SPARC && !FONTS && ARM && ARCH_ACORN
|
||||
help
|
||||
Small console font with PC-style control characters and high-half
|
||||
@@ -90,7 +90,7 @@ config FONT_6x10
|
||||
|
||||
config FONT_10x18
|
||||
bool "console 10x18 font (not supported by all drivers)" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
help
|
||||
This is a high resolution console font for machines with very
|
||||
big letters. It fits between the sun 12x22 and the normal 8x16 font.
|
||||
@@ -105,7 +105,8 @@ config FONT_SUN8x16
|
||||
|
||||
config FONT_SUN12x22
|
||||
bool "Sparc console 12x22 font (not supported by all drivers)"
|
||||
depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
depends on !SPARC && FONTS
|
||||
help
|
||||
This is the high resolution console font for Sun machines with very
|
||||
big letters (like the letters used in the SPARC PROM). If the
|
||||
@@ -113,7 +114,8 @@ config FONT_SUN12x22
|
||||
|
||||
config FONT_TER16x32
|
||||
bool "Terminus 16x32 font (not supported by all drivers)"
|
||||
depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
depends on !SPARC && FONTS || SPARC
|
||||
help
|
||||
Terminus Font is a clean, fixed width bitmap font, designed
|
||||
for long (8 and more hours per day) work with computers.
|
||||
@@ -122,7 +124,7 @@ config FONT_TER16x32
|
||||
|
||||
config FONT_6x8
|
||||
bool "OLED 6x8 font" if FONTS
|
||||
depends on FRAMEBUFFER_CONSOLE
|
||||
depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
|
||||
help
|
||||
This font is useful for small displays (OLED).
|
||||
|
||||
|
||||
@@ -233,8 +233,6 @@ static void fortify_test_alloc_size_##allocator##_dynamic(struct kunit *test) \
|
||||
kfree(p)); \
|
||||
checker(expected_size, \
|
||||
kmalloc_array_node(alloc_size, 1, gfp, NUMA_NO_NODE), \
|
||||
kfree(p)); \
|
||||
checker(expected_size, __kmalloc(alloc_size, gfp), \
|
||||
kfree(p)); \
|
||||
\
|
||||
orig = kmalloc(alloc_size, gfp); \
|
||||
@@ -374,7 +372,7 @@ static const char * const test_strs[] = {
|
||||
for (i = 0; i < ARRAY_SIZE(test_strs); i++) { \
|
||||
len = strlen(test_strs[i]); \
|
||||
KUNIT_EXPECT_EQ(test, __builtin_constant_p(len), 0); \
|
||||
checker(len, kmemdup_array(test_strs[i], len, 1, gfp), \
|
||||
checker(len, kmemdup_array(test_strs[i], 1, len, gfp), \
|
||||
kfree(p)); \
|
||||
checker(len, kmemdup(test_strs[i], len, gfp), \
|
||||
kfree(p)); \
|
||||
@@ -910,10 +908,9 @@ static void fortify_test_##memfunc(struct kunit *test) \
|
||||
memfunc(zero.buf, srcB, 0 + unconst); \
|
||||
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
|
||||
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
|
||||
/* We currently explicitly ignore zero-sized dests. */ \
|
||||
memfunc(zero.buf, srcB, 1 + unconst); \
|
||||
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
|
||||
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
|
||||
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); \
|
||||
}
|
||||
__fortify_test(memcpy)
|
||||
__fortify_test(memmove)
|
||||
@@ -1096,4 +1093,5 @@ static struct kunit_suite fortify_test_suite = {
|
||||
|
||||
kunit_test_suite(fortify_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Runtime test cases for CONFIG_FORTIFY_SOURCE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -314,4 +314,5 @@ static struct kunit_suite hashtable_test_module = {
|
||||
|
||||
kunit_test_suites(&hashtable_test_module);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for the Kernel Hashtable structures");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -46,4 +46,5 @@ static struct kunit_suite is_signed_type_test_suite = {
|
||||
|
||||
kunit_test_suite(is_signed_type_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("is_signed_type() KUnit test suite");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
|
||||
@@ -433,8 +433,23 @@ static void zap_modalias_env(struct kobj_uevent_env *env)
|
||||
len = strlen(env->envp[i]) + 1;
|
||||
|
||||
if (i != env->envp_idx - 1) {
|
||||
/* @env->envp[] contains pointers to @env->buf[]
|
||||
* with @env->buflen chars, and we are removing
|
||||
* variable MODALIAS here pointed by @env->envp[i]
|
||||
* with length @len as shown below:
|
||||
*
|
||||
* 0 @env->buf[] @env->buflen
|
||||
* ---------------------------------------------
|
||||
* ^ ^ ^ ^
|
||||
* | |-> @len <-| target block |
|
||||
* @env->envp[0] @env->envp[i] @env->envp[i + 1]
|
||||
*
|
||||
* so the "target block" indicated above is moved
|
||||
* backward by @len, and its right size is
|
||||
* @env->buflen - (@env->envp[i + 1] - @env->envp[0]).
|
||||
*/
|
||||
memmove(env->envp[i], env->envp[i + 1],
|
||||
env->buflen - len);
|
||||
env->buflen - (env->envp[i + 1] - env->envp[0]));
|
||||
|
||||
for (j = i; j < env->envp_idx - 1; j++)
|
||||
env->envp[j] = env->envp[j + 1] - len;
|
||||
|
||||
@@ -2,6 +2,7 @@ obj-$(CONFIG_KUNIT) += kunit.o
|
||||
|
||||
kunit-objs += test.o \
|
||||
resource.o \
|
||||
user_alloc.o \
|
||||
static_stub.o \
|
||||
string-stream.o \
|
||||
assert.o \
|
||||
@@ -22,6 +23,7 @@ obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
|
||||
# string-stream-test compiles built-in only.
|
||||
ifeq ($(CONFIG_KUNIT_TEST),y)
|
||||
obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o
|
||||
obj-$(CONFIG_KUNIT_TEST) += assert_test.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include <kunit/assert.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "string-stream.h"
|
||||
|
||||
@@ -30,8 +31,9 @@ void kunit_assert_prologue(const struct kunit_loc *loc,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_assert_prologue);
|
||||
|
||||
static void kunit_assert_print_msg(const struct va_format *message,
|
||||
struct string_stream *stream)
|
||||
VISIBLE_IF_KUNIT
|
||||
void kunit_assert_print_msg(const struct va_format *message,
|
||||
struct string_stream *stream)
|
||||
{
|
||||
if (message->fmt)
|
||||
string_stream_add(stream, "\n%pV", message);
|
||||
@@ -89,7 +91,7 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
|
||||
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);
|
||||
|
||||
/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
|
||||
static bool is_literal(const char *text, long long value)
|
||||
VISIBLE_IF_KUNIT bool is_literal(const char *text, long long value)
|
||||
{
|
||||
char *buffer;
|
||||
int len;
|
||||
@@ -166,7 +168,7 @@ EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);
|
||||
/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
|
||||
* Note: `text` will have ""s where as `value` will not.
|
||||
*/
|
||||
static bool is_str_literal(const char *text, const char *value)
|
||||
VISIBLE_IF_KUNIT bool is_str_literal(const char *text, const char *value)
|
||||
{
|
||||
int len;
|
||||
|
||||
@@ -208,10 +210,11 @@ EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
|
||||
/* Adds a hexdump of a buffer to a string_stream comparing it with
|
||||
* a second buffer. The different bytes are marked with <>.
|
||||
*/
|
||||
static void kunit_assert_hexdump(struct string_stream *stream,
|
||||
const void *buf,
|
||||
const void *compared_buf,
|
||||
const size_t len)
|
||||
VISIBLE_IF_KUNIT
|
||||
void kunit_assert_hexdump(struct string_stream *stream,
|
||||
const void *buf,
|
||||
const void *compared_buf,
|
||||
const size_t len)
|
||||
{
|
||||
size_t i;
|
||||
const u8 *buf1 = buf;
|
||||
|
||||
388
lib/kunit/assert_test.c
Normal file
388
lib/kunit/assert_test.c
Normal file
@@ -0,0 +1,388 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* KUnit test for the assertion formatting functions.
|
||||
* Author: Ivan Orlov <ivan.orlov0322@gmail.com>
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include "string-stream.h"
|
||||
|
||||
#define TEST_PTR_EXPECTED_BUF_SIZE 32
|
||||
#define HEXDUMP_TEST_BUF_LEN 5
|
||||
#define ASSERT_TEST_EXPECT_CONTAIN(test, str, substr) KUNIT_EXPECT_TRUE(test, strstr(str, substr))
|
||||
#define ASSERT_TEST_EXPECT_NCONTAIN(test, str, substr) KUNIT_EXPECT_FALSE(test, strstr(str, substr))
|
||||
|
||||
static void kunit_test_is_literal(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("5", 5));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("0", 0));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("1234567890", 1234567890));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("-1234567890", -1234567890));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("05", 5));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("", 0));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("-0", 0));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("12#45", 1245));
|
||||
}
|
||||
|
||||
static void kunit_test_is_str_literal(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"Hello, World!\"", "Hello, World!"));
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"", ""));
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"\"", "\""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("", ""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"", "\""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba", "Abacaba"));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("Abacaba\"", "Abacaba"));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba\"", "\"Abacaba\""));
|
||||
}
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
|
||||
|
||||
/* this function is used to get a "char *" string from the string stream and defer its cleanup */
|
||||
static char *get_str_from_stream(struct kunit *test, struct string_stream *stream)
|
||||
{
|
||||
char *str = string_stream_get_string(stream);
|
||||
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, str);
|
||||
kunit_add_action(test, kfree_wrapper, (void *)str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void kunit_test_assert_prologue(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
char *str;
|
||||
const struct kunit_loc location = {
|
||||
.file = "testfile.c",
|
||||
.line = 1337,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* Test an expectation fail prologue */
|
||||
kunit_assert_prologue(&location, KUNIT_EXPECTATION, stream);
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "EXPECTATION");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
|
||||
|
||||
/* Test an assertion fail prologue */
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_prologue(&location, KUNIT_ASSERTION, stream);
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "ASSERTION");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
|
||||
}
|
||||
|
||||
/*
|
||||
* This function accepts an arbitrary count of parameters and generates a va_format struct,
|
||||
* which can be used to validate kunit_assert_print_msg function
|
||||
*/
|
||||
static void verify_assert_print_msg(struct kunit *test,
|
||||
struct string_stream *stream,
|
||||
char *expected, const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
const struct va_format vformat = {
|
||||
.fmt = format,
|
||||
.va = &list,
|
||||
};
|
||||
|
||||
va_start(list, format);
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_print_msg(&vformat, stream);
|
||||
KUNIT_EXPECT_STREQ(test, get_str_from_stream(test, stream), expected);
|
||||
}
|
||||
|
||||
static void kunit_test_assert_print_msg(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
verify_assert_print_msg(test, stream, "\nTest", "Test");
|
||||
verify_assert_print_msg(test, stream, "\nAbacaba -123 234", "%s %d %u",
|
||||
"Abacaba", -123, 234U);
|
||||
verify_assert_print_msg(test, stream, "", NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Further code contains the tests for different assert format functions.
|
||||
* This helper function accepts the assert format function, executes it and
|
||||
* validates the result string from the stream by checking that all of the
|
||||
* substrings exist in the output.
|
||||
*/
|
||||
static void validate_assert(assert_format_t format_func, struct kunit *test,
|
||||
const struct kunit_assert *assert,
|
||||
struct string_stream *stream, int num_checks, ...)
|
||||
{
|
||||
size_t i;
|
||||
va_list checks;
|
||||
char *cur_substr_exp;
|
||||
struct va_format message = { NULL, NULL };
|
||||
|
||||
va_start(checks, num_checks);
|
||||
string_stream_clear(stream);
|
||||
format_func(assert, &message, stream);
|
||||
|
||||
for (i = 0; i < num_checks; i++) {
|
||||
cur_substr_exp = va_arg(checks, char *);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, get_str_from_stream(test, stream), cur_substr_exp);
|
||||
}
|
||||
}
|
||||
|
||||
static void kunit_test_unary_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_unary_assert un_assert = {
|
||||
.assert = assert,
|
||||
.condition = "expr",
|
||||
.expected_true = true,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
|
||||
stream, 2, "true", "is false");
|
||||
|
||||
un_assert.expected_true = false;
|
||||
validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
|
||||
stream, 2, "false", "is true");
|
||||
}
|
||||
|
||||
static void kunit_test_ptr_not_err_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_ptr_not_err_assert not_err_assert = {
|
||||
.assert = assert,
|
||||
.text = "expr",
|
||||
.value = NULL,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* Value is NULL. The corresponding message should be printed out */
|
||||
validate_assert(kunit_ptr_not_err_assert_format, test,
|
||||
¬_err_assert.assert,
|
||||
stream, 1, "null");
|
||||
|
||||
/* Value is not NULL, but looks like an error pointer. Error should be printed out */
|
||||
not_err_assert.value = (void *)-12;
|
||||
validate_assert(kunit_ptr_not_err_assert_format, test,
|
||||
¬_err_assert.assert, stream, 2,
|
||||
"error", "-12");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "1 + 2",
|
||||
.operation = "==",
|
||||
.right_text = "2",
|
||||
};
|
||||
const struct kunit_binary_assert binary_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = 3,
|
||||
.right_value = 2,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/*
|
||||
* Printed values should depend on the input we provide: the left text, right text, left
|
||||
* value and the right value.
|
||||
*/
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 4, "1 + 2", "2", "3", "==");
|
||||
|
||||
text.right_text = "4 - 2";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 3, "==", "1 + 2", "4 - 2");
|
||||
|
||||
text.left_text = "3";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 4, "3", "4 - 2", "2", "==");
|
||||
|
||||
text.right_text = "2";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 3, "3", "2", "==");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_ptr_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
char *addr_var_a, *addr_var_b;
|
||||
static const void *var_a = (void *)0xDEADBEEF;
|
||||
static const void *var_b = (void *)0xBADDCAFE;
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "var_a",
|
||||
.operation = "==",
|
||||
.right_text = "var_b",
|
||||
};
|
||||
struct kunit_binary_ptr_assert binary_ptr_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = var_a,
|
||||
.right_value = var_b,
|
||||
};
|
||||
|
||||
addr_var_a = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_a);
|
||||
addr_var_b = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_b);
|
||||
/*
|
||||
* Print the addresses to the buffers first.
|
||||
* This is necessary as we may have different count of leading zeros in the pointer
|
||||
* on different architectures.
|
||||
*/
|
||||
snprintf(addr_var_a, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_a);
|
||||
snprintf(addr_var_b, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_b);
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
validate_assert(kunit_binary_ptr_assert_format, test, &binary_ptr_assert.assert,
|
||||
stream, 3, addr_var_a, addr_var_b, "==");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_str_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
static const char *var_a = "abacaba";
|
||||
static const char *var_b = "kernel";
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "var_a",
|
||||
.operation = "==",
|
||||
.right_text = "var_b",
|
||||
};
|
||||
struct kunit_binary_str_assert binary_str_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = var_a,
|
||||
.right_value = var_b,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
validate_assert(kunit_binary_str_assert_format, test,
|
||||
&binary_str_assert.assert,
|
||||
stream, 5, "var_a", "var_b", "\"abacaba\"",
|
||||
"\"kernel\"", "==");
|
||||
|
||||
text.left_text = "\"abacaba\"";
|
||||
validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
|
||||
stream, 4, "\"abacaba\"", "var_b", "\"kernel\"", "==");
|
||||
|
||||
text.right_text = "\"kernel\"";
|
||||
validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
|
||||
stream, 3, "\"abacaba\"", "\"kernel\"", "==");
|
||||
}
|
||||
|
||||
static const u8 hex_testbuf1[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
|
||||
0x45, 0x9d, 0x47, 0xd6, 0x47,
|
||||
0x2, 0x89, 0x8c, 0x81, 0x94,
|
||||
0x12, 0xfe, 0x01 };
|
||||
static const u8 hex_testbuf2[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
|
||||
0x45, 0x9d, 0x47, 0x21, 0x47,
|
||||
0xcd, 0x89, 0x24, 0x50, 0x94,
|
||||
0x12, 0xba, 0x01 };
|
||||
static void kunit_test_assert_hexdump(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
char *str;
|
||||
size_t i;
|
||||
char buf[HEXDUMP_TEST_BUF_LEN];
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
/* Check that we are getting output like <xx> for non-matching numbers. */
|
||||
kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf2, sizeof(hex_testbuf1));
|
||||
str = get_str_from_stream(test, stream);
|
||||
for (i = 0; i < sizeof(hex_testbuf1); i++) {
|
||||
snprintf(buf, HEXDUMP_TEST_BUF_LEN, "<%02x>", hex_testbuf1[i]);
|
||||
if (hex_testbuf1[i] != hex_testbuf2[i])
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, buf);
|
||||
}
|
||||
/* We shouldn't get any <xx> numbers when comparing the buffer with itself. */
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf1, sizeof(hex_testbuf1));
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_NCONTAIN(test, str, "<");
|
||||
ASSERT_TEST_EXPECT_NCONTAIN(test, str, ">");
|
||||
}
|
||||
|
||||
static void kunit_test_mem_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct string_stream *expected_stream;
|
||||
struct kunit_assert assert = {};
|
||||
static const struct kunit_binary_assert_text text = {
|
||||
.left_text = "hex_testbuf1",
|
||||
.operation = "==",
|
||||
.right_text = "hex_testbuf2",
|
||||
};
|
||||
struct kunit_mem_assert mem_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = NULL,
|
||||
.right_value = hex_testbuf2,
|
||||
.size = sizeof(hex_testbuf1),
|
||||
};
|
||||
|
||||
expected_stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_stream);
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* The left value is NULL */
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 2, "hex_testbuf1", "is not null");
|
||||
|
||||
/* The right value is NULL, the left value is not NULL */
|
||||
mem_assert.left_value = hex_testbuf1;
|
||||
mem_assert.right_value = NULL;
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 2, "hex_testbuf2", "is not null");
|
||||
|
||||
/* Both arguments are not null */
|
||||
mem_assert.left_value = hex_testbuf1;
|
||||
mem_assert.right_value = hex_testbuf2;
|
||||
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 3, "hex_testbuf1", "hex_testbuf2", "==");
|
||||
}
|
||||
|
||||
static struct kunit_case assert_test_cases[] = {
|
||||
KUNIT_CASE(kunit_test_is_literal),
|
||||
KUNIT_CASE(kunit_test_is_str_literal),
|
||||
KUNIT_CASE(kunit_test_assert_prologue),
|
||||
KUNIT_CASE(kunit_test_assert_print_msg),
|
||||
KUNIT_CASE(kunit_test_unary_assert_format),
|
||||
KUNIT_CASE(kunit_test_ptr_not_err_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_ptr_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_str_assert_format),
|
||||
KUNIT_CASE(kunit_test_assert_hexdump),
|
||||
KUNIT_CASE(kunit_test_mem_assert_format),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite assert_test_suite = {
|
||||
.name = "kunit-assert",
|
||||
.test_cases = assert_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&assert_test_suite);
|
||||
@@ -70,32 +70,26 @@ struct kunit_glob_filter {
|
||||
static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
|
||||
const char *filter_glob)
|
||||
{
|
||||
const int len = strlen(filter_glob);
|
||||
const char *period = strchr(filter_glob, '.');
|
||||
|
||||
if (!period) {
|
||||
parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL);
|
||||
parsed->suite_glob = kstrdup(filter_glob, GFP_KERNEL);
|
||||
if (!parsed->suite_glob)
|
||||
return -ENOMEM;
|
||||
|
||||
parsed->test_glob = NULL;
|
||||
strcpy(parsed->suite_glob, filter_glob);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
|
||||
parsed->suite_glob = kstrndup(filter_glob, period - filter_glob, GFP_KERNEL);
|
||||
if (!parsed->suite_glob)
|
||||
return -ENOMEM;
|
||||
|
||||
parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
|
||||
parsed->test_glob = kstrdup(period + 1, GFP_KERNEL);
|
||||
if (!parsed->test_glob) {
|
||||
kfree(parsed->suite_glob);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
|
||||
strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ static struct kunit_suite *alloc_fake_suite(struct kunit *test,
|
||||
|
||||
/* We normally never expect to allocate suites, hence the non-const cast. */
|
||||
suite = kunit_kzalloc(test, sizeof(*suite), GFP_KERNEL);
|
||||
strncpy((char *)suite->name, suite_name, sizeof(suite->name) - 1);
|
||||
strscpy((char *)suite->name, suite_name, sizeof(suite->name));
|
||||
suite->test_cases = test_cases;
|
||||
|
||||
return suite;
|
||||
|
||||
@@ -374,4 +374,5 @@ static struct kunit_suite example_init_test_suite = {
|
||||
*/
|
||||
kunit_test_init_section_suites(&example_init_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Example KUnit test suite");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -871,4 +871,5 @@ kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
|
||||
&kunit_current_test_suite, &kunit_device_test_suite,
|
||||
&kunit_fault_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for core test infrastructure");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -938,4 +938,5 @@ static void __exit kunit_exit(void)
|
||||
}
|
||||
module_exit(kunit_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Base unit test (KUnit) API");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
117
lib/kunit/user_alloc.c
Normal file
117
lib/kunit/user_alloc.c
Normal file
@@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KUnit userspace memory allocation resource management.
|
||||
*/
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
struct kunit_vm_mmap_resource {
|
||||
unsigned long addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* vm_mmap() arguments */
|
||||
struct kunit_vm_mmap_params {
|
||||
struct file *file;
|
||||
unsigned long addr;
|
||||
unsigned long len;
|
||||
unsigned long prot;
|
||||
unsigned long flag;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
/* Create and attach a new mm if it doesn't already exist. */
|
||||
static int kunit_attach_mm(void)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
|
||||
if (current->mm)
|
||||
return 0;
|
||||
|
||||
/* arch_pick_mmap_layout() is only sane with MMU systems. */
|
||||
if (!IS_ENABLED(CONFIG_MMU))
|
||||
return -EINVAL;
|
||||
|
||||
mm = mm_alloc();
|
||||
if (!mm)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Define the task size. */
|
||||
mm->task_size = TASK_SIZE;
|
||||
|
||||
/* Make sure we can allocate new VMAs. */
|
||||
arch_pick_mmap_layout(mm, ¤t->signal->rlim[RLIMIT_STACK]);
|
||||
|
||||
/* Attach the mm. It will be cleaned up when the process dies. */
|
||||
kthread_use_mm(mm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
|
||||
{
|
||||
struct kunit_vm_mmap_params *p = context;
|
||||
struct kunit_vm_mmap_resource vres;
|
||||
int ret;
|
||||
|
||||
ret = kunit_attach_mm();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vres.size = p->len;
|
||||
vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
|
||||
if (!vres.addr)
|
||||
return -ENOMEM;
|
||||
res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
|
||||
if (!res->data) {
|
||||
vm_munmap(vres.addr, vres.size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kunit_vm_mmap_free(struct kunit_resource *res)
|
||||
{
|
||||
struct kunit_vm_mmap_resource *vres = res->data;
|
||||
|
||||
/*
|
||||
* Since this is executed from the test monitoring process,
|
||||
* the test's mm has already been torn down. We don't need
|
||||
* to run vm_munmap(vres->addr, vres->size), only clean up
|
||||
* the vres.
|
||||
*/
|
||||
|
||||
kfree(vres);
|
||||
res->data = NULL;
|
||||
}
|
||||
|
||||
unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
|
||||
unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flag,
|
||||
unsigned long offset)
|
||||
{
|
||||
struct kunit_vm_mmap_params params = {
|
||||
.file = file,
|
||||
.addr = addr,
|
||||
.len = len,
|
||||
.prot = prot,
|
||||
.flag = flag,
|
||||
.offset = offset,
|
||||
};
|
||||
struct kunit_vm_mmap_resource *vres;
|
||||
|
||||
vres = kunit_alloc_resource(test,
|
||||
kunit_vm_mmap_init,
|
||||
kunit_vm_mmap_free,
|
||||
GFP_KERNEL,
|
||||
¶ms);
|
||||
if (vres)
|
||||
return vres->addr;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_vm_mmap);
|
||||
|
||||
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
|
||||
@@ -1201,12 +1201,6 @@ static struct kunit_suite hlist_test_module = {
|
||||
};
|
||||
|
||||
|
||||
struct klist_test_struct {
|
||||
int data;
|
||||
struct klist klist;
|
||||
struct klist_node klist_node;
|
||||
};
|
||||
|
||||
static int node_count;
|
||||
static struct klist_node *last_node;
|
||||
|
||||
@@ -1499,4 +1493,5 @@ static struct kunit_suite klist_test_module = {
|
||||
|
||||
kunit_test_suites(&list_test_module, &hlist_test_module, &klist_test_module);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for the Kernel Linked-list structures");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -4203,31 +4203,28 @@ slow_path:
|
||||
*
|
||||
* Return: The contents that was stored at the index.
|
||||
*/
|
||||
static inline void *mas_wr_store_entry(struct ma_wr_state *wr_mas)
|
||||
static inline void mas_wr_store_entry(struct ma_wr_state *wr_mas)
|
||||
{
|
||||
struct ma_state *mas = wr_mas->mas;
|
||||
|
||||
wr_mas->content = mas_start(mas);
|
||||
if (mas_is_none(mas) || mas_is_ptr(mas)) {
|
||||
mas_store_root(mas, wr_mas->entry);
|
||||
return wr_mas->content;
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(!mas_wr_walk(wr_mas))) {
|
||||
mas_wr_spanning_store(wr_mas);
|
||||
return wr_mas->content;
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point, we are at the leaf node that needs to be altered. */
|
||||
mas_wr_end_piv(wr_mas);
|
||||
/* New root for a single pointer */
|
||||
if (unlikely(!mas->index && mas->last == ULONG_MAX)) {
|
||||
if (unlikely(!mas->index && mas->last == ULONG_MAX))
|
||||
mas_new_root(mas, wr_mas->entry);
|
||||
return wr_mas->content;
|
||||
}
|
||||
|
||||
mas_wr_modify(wr_mas);
|
||||
return wr_mas->content;
|
||||
else
|
||||
mas_wr_modify(wr_mas);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -311,4 +311,5 @@ module_exit(primes_exit);
|
||||
module_param_named(selftest, selftest_max, ulong, 0400);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Prime number library");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -53,4 +53,5 @@ static struct kunit_suite rational_test_suite = {
|
||||
|
||||
kunit_test_suites(&rational_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Rational fractions unit test");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -108,4 +108,5 @@ void rational_best_approximation(
|
||||
|
||||
EXPORT_SYMBOL(rational_best_approximation);
|
||||
|
||||
MODULE_DESCRIPTION("Rational fraction support library");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -510,4 +510,5 @@ static struct kunit_suite memcpy_test_suite = {
|
||||
|
||||
kunit_test_suite(memcpy_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("test cases for memcpy(), memmove(), and memset()");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1178,14 +1178,28 @@ struct foo {
|
||||
s16 array[] __counted_by(counter);
|
||||
};
|
||||
|
||||
struct bar {
|
||||
int a;
|
||||
u32 counter;
|
||||
s16 array[];
|
||||
};
|
||||
|
||||
static void DEFINE_FLEX_test(struct kunit *test)
|
||||
{
|
||||
DEFINE_RAW_FLEX(struct foo, two, array, 2);
|
||||
/* Using _RAW_ on a __counted_by struct will initialize "counter" to zero */
|
||||
DEFINE_RAW_FLEX(struct foo, two_but_zero, array, 2);
|
||||
#if __has_attribute(__counted_by__)
|
||||
int expected_raw_size = sizeof(struct foo);
|
||||
#else
|
||||
int expected_raw_size = sizeof(struct foo) + 2 * sizeof(s16);
|
||||
#endif
|
||||
/* Without annotation, it will always be on-stack size. */
|
||||
DEFINE_RAW_FLEX(struct bar, two, array, 2);
|
||||
DEFINE_FLEX(struct foo, eight, array, counter, 8);
|
||||
DEFINE_FLEX(struct foo, empty, array, counter, 0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, __struct_size(two),
|
||||
sizeof(struct foo) + sizeof(s16) + sizeof(s16));
|
||||
KUNIT_EXPECT_EQ(test, __struct_size(two_but_zero), expected_raw_size);
|
||||
KUNIT_EXPECT_EQ(test, __struct_size(two), sizeof(struct bar) + 2 * sizeof(s16));
|
||||
KUNIT_EXPECT_EQ(test, __struct_size(eight), 24);
|
||||
KUNIT_EXPECT_EQ(test, __struct_size(empty), sizeof(struct foo));
|
||||
}
|
||||
@@ -1223,4 +1237,5 @@ static struct kunit_suite overflow_test_suite = {
|
||||
|
||||
kunit_test_suite(overflow_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for arithmetic overflow checks");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
|
||||
@@ -73,17 +73,50 @@ void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
|
||||
EXPORT_SYMBOL(percpu_counter_set);
|
||||
|
||||
/*
|
||||
* local_irq_save() is needed to make the function irq safe:
|
||||
* - The slow path would be ok as protected by an irq-safe spinlock.
|
||||
* - this_cpu_add would be ok as it is irq-safe by definition.
|
||||
* But:
|
||||
* The decision slow path/fast path and the actual update must be atomic, too.
|
||||
* Add to a counter while respecting batch size.
|
||||
*
|
||||
* There are 2 implementations, both dealing with the following problem:
|
||||
*
|
||||
* The decision slow path/fast path and the actual update must be atomic.
|
||||
* Otherwise a call in process context could check the current values and
|
||||
* decide that the fast path can be used. If now an interrupt occurs before
|
||||
* the this_cpu_add(), and the interrupt updates this_cpu(*fbc->counters),
|
||||
* then the this_cpu_add() that is executed after the interrupt has completed
|
||||
* can produce values larger than "batch" or even overflows.
|
||||
*/
|
||||
#ifdef CONFIG_HAVE_CMPXCHG_LOCAL
|
||||
/*
|
||||
* Safety against interrupts is achieved in 2 ways:
|
||||
* 1. the fast path uses local cmpxchg (note: no lock prefix)
|
||||
* 2. the slow path operates with interrupts disabled
|
||||
*/
|
||||
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
|
||||
{
|
||||
s64 count;
|
||||
unsigned long flags;
|
||||
|
||||
count = this_cpu_read(*fbc->counters);
|
||||
do {
|
||||
if (unlikely(abs(count + amount) >= batch)) {
|
||||
raw_spin_lock_irqsave(&fbc->lock, flags);
|
||||
/*
|
||||
* Note: by now we might have migrated to another CPU
|
||||
* or the value might have changed.
|
||||
*/
|
||||
count = __this_cpu_read(*fbc->counters);
|
||||
fbc->count += count + amount;
|
||||
__this_cpu_sub(*fbc->counters, count);
|
||||
raw_spin_unlock_irqrestore(&fbc->lock, flags);
|
||||
return;
|
||||
}
|
||||
} while (!this_cpu_try_cmpxchg(*fbc->counters, &count, count + amount));
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* local_irq_save() is used to make the function irq safe:
|
||||
* - The slow path would be ok as protected by an irq-safe spinlock.
|
||||
* - this_cpu_add would be ok as it is irq-safe by definition.
|
||||
*/
|
||||
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
|
||||
{
|
||||
s64 count;
|
||||
@@ -101,6 +134,7 @@ void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(percpu_counter_add_batch);
|
||||
|
||||
/*
|
||||
|
||||
42
lib/plist.c
42
lib/plist.c
@@ -47,8 +47,8 @@ static void plist_check_list(struct list_head *top)
|
||||
|
||||
plist_check_prev_next(top, prev, next);
|
||||
while (next != top) {
|
||||
prev = next;
|
||||
next = prev->next;
|
||||
WRITE_ONCE(prev, next);
|
||||
WRITE_ONCE(next, prev->next);
|
||||
plist_check_prev_next(top, prev, next);
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ static void plist_check_head(struct plist_head *head)
|
||||
*/
|
||||
void plist_add(struct plist_node *node, struct plist_head *head)
|
||||
{
|
||||
struct plist_node *first, *iter, *prev = NULL;
|
||||
struct plist_node *first, *iter, *prev = NULL, *last, *reverse_iter;
|
||||
struct list_head *node_next = &head->node_list;
|
||||
|
||||
plist_check_head(head);
|
||||
@@ -83,16 +83,26 @@ void plist_add(struct plist_node *node, struct plist_head *head)
|
||||
goto ins_node;
|
||||
|
||||
first = iter = plist_first(head);
|
||||
last = reverse_iter = list_entry(first->prio_list.prev, struct plist_node, prio_list);
|
||||
|
||||
do {
|
||||
if (node->prio < iter->prio) {
|
||||
node_next = &iter->node_list;
|
||||
break;
|
||||
} else if (node->prio >= reverse_iter->prio) {
|
||||
prev = reverse_iter;
|
||||
iter = list_entry(reverse_iter->prio_list.next,
|
||||
struct plist_node, prio_list);
|
||||
if (likely(reverse_iter != last))
|
||||
node_next = &iter->node_list;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = iter;
|
||||
iter = list_entry(iter->prio_list.next,
|
||||
struct plist_node, prio_list);
|
||||
reverse_iter = list_entry(reverse_iter->prio_list.prev,
|
||||
struct plist_node, prio_list);
|
||||
} while (iter != first);
|
||||
|
||||
if (!prev || prev->prio != node->prio)
|
||||
@@ -255,6 +265,32 @@ static int __init plist_test(void)
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "end plist test\n");
|
||||
|
||||
/* Worst case test for plist_add() */
|
||||
unsigned int test_data[241];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_data); i++)
|
||||
test_data[i] = i;
|
||||
|
||||
ktime_t start, end, time_elapsed = 0;
|
||||
|
||||
plist_head_init(&test_head);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_node); i++) {
|
||||
plist_node_init(test_node + i, 0);
|
||||
test_node[i].prio = test_data[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_node); i++) {
|
||||
if (plist_node_empty(test_node + i)) {
|
||||
start = ktime_get();
|
||||
plist_add(test_node + i, &test_head);
|
||||
end = ktime_get();
|
||||
time_elapsed += (end - start);
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("plist_add worst case test time elapsed %lld\n", time_elapsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -297,9 +297,9 @@ ____rb_erase_color(struct rb_node *parent, struct rb_root *root,
|
||||
* / \ / \
|
||||
* N S --> N sl
|
||||
* / \ \
|
||||
* sl Sr S
|
||||
* sl sr S
|
||||
* \
|
||||
* Sr
|
||||
* sr
|
||||
*
|
||||
* Note: p might be red, and then both
|
||||
* p and sl are red after rotation(which
|
||||
@@ -312,9 +312,9 @@ ____rb_erase_color(struct rb_node *parent, struct rb_root *root,
|
||||
* / \ / \
|
||||
* N sl --> P S
|
||||
* \ / \
|
||||
* S N Sr
|
||||
* S N sr
|
||||
* \
|
||||
* Sr
|
||||
* sr
|
||||
*/
|
||||
tmp1 = tmp2->rb_right;
|
||||
WRITE_ONCE(sibling->rb_left, tmp1);
|
||||
|
||||
@@ -60,12 +60,30 @@ static inline void update_alloc_hint_after_get(struct sbitmap *sb,
|
||||
/*
|
||||
* See if we have deferred clears that we can batch move
|
||||
*/
|
||||
static inline bool sbitmap_deferred_clear(struct sbitmap_word *map)
|
||||
static inline bool sbitmap_deferred_clear(struct sbitmap_word *map,
|
||||
unsigned int depth, unsigned int alloc_hint, bool wrap)
|
||||
{
|
||||
unsigned long mask;
|
||||
unsigned long mask, word_mask;
|
||||
|
||||
if (!READ_ONCE(map->cleared))
|
||||
return false;
|
||||
guard(spinlock_irqsave)(&map->swap_lock);
|
||||
|
||||
if (!map->cleared) {
|
||||
if (depth == 0)
|
||||
return false;
|
||||
|
||||
word_mask = (~0UL) >> (BITS_PER_LONG - depth);
|
||||
/*
|
||||
* The current behavior is to always retry after moving
|
||||
* ->cleared to word, and we change it to retry in case
|
||||
* of any free bits. To avoid an infinite loop, we need
|
||||
* to take wrap & alloc_hint into account, otherwise a
|
||||
* soft lockup may occur.
|
||||
*/
|
||||
if (!wrap && alloc_hint)
|
||||
word_mask &= ~((1UL << alloc_hint) - 1);
|
||||
|
||||
return (READ_ONCE(map->word) & word_mask) != word_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* First get a stable cleared mask, setting the old mask to 0.
|
||||
@@ -85,6 +103,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
|
||||
bool alloc_hint)
|
||||
{
|
||||
unsigned int bits_per_word;
|
||||
int i;
|
||||
|
||||
if (shift < 0)
|
||||
shift = sbitmap_calculate_shift(depth);
|
||||
@@ -116,6 +135,9 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < sb->map_nr; i++)
|
||||
spin_lock_init(&sb->map[i].swap_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sbitmap_init_node);
|
||||
@@ -126,7 +148,7 @@ void sbitmap_resize(struct sbitmap *sb, unsigned int depth)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sb->map_nr; i++)
|
||||
sbitmap_deferred_clear(&sb->map[i]);
|
||||
sbitmap_deferred_clear(&sb->map[i], 0, 0, 0);
|
||||
|
||||
sb->depth = depth;
|
||||
sb->map_nr = DIV_ROUND_UP(sb->depth, bits_per_word);
|
||||
@@ -179,7 +201,7 @@ static int sbitmap_find_bit_in_word(struct sbitmap_word *map,
|
||||
alloc_hint, wrap);
|
||||
if (nr != -1)
|
||||
break;
|
||||
if (!sbitmap_deferred_clear(map))
|
||||
if (!sbitmap_deferred_clear(map, depth, alloc_hint, wrap))
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
@@ -496,7 +518,7 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,
|
||||
unsigned int map_depth = __map_depth(sb, index);
|
||||
unsigned long val;
|
||||
|
||||
sbitmap_deferred_clear(map);
|
||||
sbitmap_deferred_clear(map, 0, 0, 0);
|
||||
val = READ_ONCE(map->word);
|
||||
if (val == (1UL << (map_depth - 1)) - 1)
|
||||
goto next;
|
||||
|
||||
@@ -194,4 +194,5 @@ static struct kunit_suite siphash_test_suite = {
|
||||
kunit_test_suite(siphash_test_suite);
|
||||
|
||||
MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");
|
||||
MODULE_DESCRIPTION("Test cases for siphash.c");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -140,7 +140,7 @@ static void test_kmalloc_redzone_access(struct kunit *test)
|
||||
{
|
||||
struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32,
|
||||
SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE);
|
||||
u8 *p = kmalloc_trace(s, GFP_KERNEL, 18);
|
||||
u8 *p = __kmalloc_cache_noprof(s, GFP_KERNEL, 18);
|
||||
|
||||
kasan_disable_current();
|
||||
|
||||
|
||||
14
lib/sort.c
14
lib/sort.c
@@ -5,13 +5,11 @@
|
||||
* This performs n*log2(n) + 0.37*n + o(n) comparisons on average,
|
||||
* and 1.5*n*log2(n) + O(n) in the (very contrived) worst case.
|
||||
*
|
||||
* Glibc qsort() manages n*log2(n) - 1.26*n for random inputs (1.63*n
|
||||
* Quicksort manages n*log2(n) - 1.26*n for random inputs (1.63*n
|
||||
* better) at the expense of stack usage and much larger code to avoid
|
||||
* quicksort's O(n^2) worst case.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/sort.h>
|
||||
@@ -252,10 +250,7 @@ void sort_r(void *base, size_t num, size_t size,
|
||||
a = size << shift;
|
||||
n -= size;
|
||||
do_swap(base + a, base + n, size, swap_func, priv);
|
||||
} else if (n > size) { /* Sorting: Extract root */
|
||||
n -= size;
|
||||
do_swap(base, base + n, size, swap_func, priv);
|
||||
} else { /* Sort complete */
|
||||
} else { /* Sort complete */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -285,6 +280,11 @@ void sort_r(void *base, size_t num, size_t size,
|
||||
do_swap(base + b, base + c, size, swap_func, priv);
|
||||
}
|
||||
}
|
||||
|
||||
n -= size;
|
||||
do_swap(base, base + n, size, swap_func, priv);
|
||||
if (n == size * 2 && do_cmp(base, base + size, cmp_func, priv) > 0)
|
||||
do_swap(base, base + size, size, swap_func, priv);
|
||||
}
|
||||
EXPORT_SYMBOL(sort_r);
|
||||
|
||||
|
||||
@@ -471,4 +471,5 @@ static struct kunit_suite stackinit_test_suite = {
|
||||
|
||||
kunit_test_suites(&stackinit_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for compiler-based stack variable zeroing");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -625,4 +625,5 @@ static struct kunit_suite string_helpers_test_suite = {
|
||||
|
||||
kunit_test_suites(&string_helpers_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for string helpers module");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -633,4 +633,5 @@ static struct kunit_suite string_test_suite = {
|
||||
|
||||
kunit_test_suites(&string_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for string functions");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -732,4 +732,5 @@ static int __init test_kstrtox_init(void)
|
||||
return -EINVAL;
|
||||
}
|
||||
module_init(test_kstrtox_init);
|
||||
MODULE_DESCRIPTION("Module test for kstrto*() APIs");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -1486,4 +1486,5 @@ static void __init selftest(void)
|
||||
|
||||
KSTM_MODULE_LOADERS(test_bitmap);
|
||||
MODULE_AUTHOR("david decotigny <david.decotigny@googlers.com>");
|
||||
MODULE_DESCRIPTION("Test cases for bitmap API");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -72,4 +72,5 @@ static struct kunit_suite bits_test_suite = {
|
||||
};
|
||||
kunit_test_suite(bits_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for functions and macros in bits.h");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -96,4 +96,5 @@ module_init(test_blackholedev_init);
|
||||
module_exit(test_blackholedev_exit);
|
||||
|
||||
MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>");
|
||||
MODULE_DESCRIPTION("module test of the blackhole_dev");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1740,7 +1740,7 @@ static int __bpf_emit_cmpxchg32(struct bpf_test *self, void *arg,
|
||||
/* Result unsuccessful */
|
||||
insns[i++] = BPF_STX_MEM(BPF_W, R10, R1, -4);
|
||||
insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4);
|
||||
insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */
|
||||
insns[i++] = BPF_ZEXT_REG(R0); /* Zext always inserted by verifier */
|
||||
insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4);
|
||||
|
||||
insns[i++] = BPF_JMP32_REG(BPF_JEQ, R1, R3, 2);
|
||||
@@ -1754,7 +1754,7 @@ static int __bpf_emit_cmpxchg32(struct bpf_test *self, void *arg,
|
||||
/* Result successful */
|
||||
i += __bpf_ld_imm64(&insns[i], R0, dst);
|
||||
insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4);
|
||||
insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */
|
||||
insns[i++] = BPF_ZEXT_REG(R0); /* Zext always inserted by verifier */
|
||||
insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4);
|
||||
|
||||
insns[i++] = BPF_JMP32_REG(BPF_JEQ, R2, R3, 2);
|
||||
@@ -15198,6 +15198,7 @@ struct tail_call_test {
|
||||
int flags;
|
||||
int result;
|
||||
int stack_depth;
|
||||
bool has_tail_call;
|
||||
};
|
||||
|
||||
/* Flags that can be passed to tail call test cases */
|
||||
@@ -15273,6 +15274,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = 3,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call 3",
|
||||
@@ -15283,6 +15285,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = 6,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call 4",
|
||||
@@ -15293,6 +15296,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = 10,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call load/store leaf",
|
||||
@@ -15323,6 +15327,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
},
|
||||
.result = 0,
|
||||
.stack_depth = 16,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call error path, max count reached",
|
||||
@@ -15335,6 +15340,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
},
|
||||
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
|
||||
.result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call count preserved across function calls",
|
||||
@@ -15357,6 +15363,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
.stack_depth = 8,
|
||||
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
|
||||
.result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call error path, NULL target",
|
||||
@@ -15369,6 +15376,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
},
|
||||
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
|
||||
.result = MAX_TESTRUNS,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
{
|
||||
"Tail call error path, index out of range",
|
||||
@@ -15381,6 +15389,7 @@ static struct tail_call_test tail_call_tests[] = {
|
||||
},
|
||||
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
|
||||
.result = MAX_TESTRUNS,
|
||||
.has_tail_call = true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15430,6 +15439,7 @@ static __init int prepare_tail_call_tests(struct bpf_array **pprogs)
|
||||
fp->len = len;
|
||||
fp->type = BPF_PROG_TYPE_SOCKET_FILTER;
|
||||
fp->aux->stack_depth = test->stack_depth;
|
||||
fp->aux->tail_call_reachable = test->has_tail_call;
|
||||
memcpy(fp->insnsi, test->insns, len * sizeof(struct bpf_insn));
|
||||
|
||||
/* Relocate runtime tail call offsets and addresses */
|
||||
|
||||
@@ -162,4 +162,5 @@ module_init(test_dynamic_debug_init);
|
||||
module_exit(test_dynamic_debug_exit);
|
||||
|
||||
MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
|
||||
MODULE_DESCRIPTION("Kernel module for testing dynamic_debug");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1567,4 +1567,5 @@ static void __exit test_firmware_exit(void)
|
||||
module_exit(test_firmware_exit);
|
||||
|
||||
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
|
||||
MODULE_DESCRIPTION("interface to trigger and test firmware loading");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -59,4 +59,5 @@ static void __exit test_fpu_exit(void)
|
||||
module_init(test_fpu_init);
|
||||
module_exit(test_fpu_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for floating point operations");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -44,4 +44,5 @@ static void m_ex(void)
|
||||
module_init(m_in);
|
||||
module_exit(m_ex);
|
||||
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
|
||||
MODULE_DESCRIPTION("Check that free_pages() doesn't leak memory");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -235,4 +235,5 @@ static struct kunit_suite hash_test_suite = {
|
||||
|
||||
kunit_test_suite(hash_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for <linux/hash.h> and <linux/stringhash.h>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -253,4 +253,5 @@ static void __exit test_hexdump_exit(void)
|
||||
module_exit(test_hexdump_exit);
|
||||
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Test cases for lib/hexdump.c module");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -1550,4 +1550,5 @@ static void __exit hmm_dmirror_exit(void)
|
||||
|
||||
module_init(hmm_dmirror_init);
|
||||
module_exit(hmm_dmirror_exit);
|
||||
MODULE_DESCRIPTION("HMM (Heterogeneous Memory Management) test module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -214,4 +214,5 @@ static void ida_exit(void)
|
||||
module_init(ida_checks);
|
||||
module_exit(ida_exit);
|
||||
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
|
||||
MODULE_DESCRIPTION("Test the IDA API");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1223,4 +1223,5 @@ static void __exit test_kmod_exit(void)
|
||||
module_exit(test_kmod_exit);
|
||||
|
||||
MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
|
||||
MODULE_DESCRIPTION("kmod stress test driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* test_kprobes.c - simple sanity test for *probes
|
||||
* test_kprobes.c - simple sanity test for k*probes
|
||||
*
|
||||
* Copyright IBM Corp. 2008
|
||||
*/
|
||||
@@ -400,4 +400,5 @@ static struct kunit_suite kprobes_test_suite = {
|
||||
|
||||
kunit_test_suites(&kprobes_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("simple sanity test for k*probes");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -216,4 +216,5 @@ static struct kunit_suite range_test_module = {
|
||||
|
||||
kunit_test_suites(&range_test_module);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for the linear_ranges helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -119,4 +119,5 @@ static struct kunit_suite list_sort_suite = {
|
||||
|
||||
kunit_test_suites(&list_sort_suite);
|
||||
|
||||
MODULE_DESCRIPTION("list_sort() KUnit test suite");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -3946,4 +3946,5 @@ static void __exit maple_tree_harvest(void)
|
||||
module_init(maple_tree_seed);
|
||||
module_exit(maple_tree_harvest);
|
||||
MODULE_AUTHOR("Liam R. Howlett <Liam.Howlett@Oracle.com>");
|
||||
MODULE_DESCRIPTION("maple tree API test module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -112,4 +112,5 @@ static void __exit test_memcat_p_exit(void)
|
||||
module_init(test_memcat_p_init);
|
||||
module_exit(test_memcat_p_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for memcat_p() in lib/memcat_p.c");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -436,4 +436,5 @@ static int __init test_meminit_init(void)
|
||||
}
|
||||
module_init(test_meminit_init);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for SL[AOU]B/page initialization at alloc/free time");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -11,17 +11,19 @@
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
static __init bool less_than(const void *lhs, const void *rhs)
|
||||
DEFINE_MIN_HEAP(int, min_heap_test);
|
||||
|
||||
static __init bool less_than(const void *lhs, const void *rhs, void __always_unused *args)
|
||||
{
|
||||
return *(int *)lhs < *(int *)rhs;
|
||||
}
|
||||
|
||||
static __init bool greater_than(const void *lhs, const void *rhs)
|
||||
static __init bool greater_than(const void *lhs, const void *rhs, void __always_unused *args)
|
||||
{
|
||||
return *(int *)lhs > *(int *)rhs;
|
||||
}
|
||||
|
||||
static __init void swap_ints(void *lhs, void *rhs)
|
||||
static __init void swap_ints(void *lhs, void *rhs, void __always_unused *args)
|
||||
{
|
||||
int temp = *(int *)lhs;
|
||||
|
||||
@@ -30,7 +32,7 @@ static __init void swap_ints(void *lhs, void *rhs)
|
||||
}
|
||||
|
||||
static __init int pop_verify_heap(bool min_heap,
|
||||
struct min_heap *heap,
|
||||
struct min_heap_test *heap,
|
||||
const struct min_heap_callbacks *funcs)
|
||||
{
|
||||
int *values = heap->data;
|
||||
@@ -38,7 +40,7 @@ static __init int pop_verify_heap(bool min_heap,
|
||||
int last;
|
||||
|
||||
last = values[0];
|
||||
min_heap_pop(heap, funcs);
|
||||
min_heap_pop(heap, funcs, NULL);
|
||||
while (heap->nr > 0) {
|
||||
if (min_heap) {
|
||||
if (last > values[0]) {
|
||||
@@ -54,7 +56,7 @@ static __init int pop_verify_heap(bool min_heap,
|
||||
}
|
||||
}
|
||||
last = values[0];
|
||||
min_heap_pop(heap, funcs);
|
||||
min_heap_pop(heap, funcs, NULL);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -63,20 +65,19 @@ static __init int test_heapify_all(bool min_heap)
|
||||
{
|
||||
int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
|
||||
-3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
|
||||
struct min_heap heap = {
|
||||
struct min_heap_test heap = {
|
||||
.data = values,
|
||||
.nr = ARRAY_SIZE(values),
|
||||
.size = ARRAY_SIZE(values),
|
||||
};
|
||||
struct min_heap_callbacks funcs = {
|
||||
.elem_size = sizeof(int),
|
||||
.less = min_heap ? less_than : greater_than,
|
||||
.swp = swap_ints,
|
||||
};
|
||||
int i, err;
|
||||
|
||||
/* Test with known set of values. */
|
||||
min_heapify_all(&heap, &funcs);
|
||||
min_heapify_all(&heap, &funcs, NULL);
|
||||
err = pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
|
||||
@@ -85,7 +86,7 @@ static __init int test_heapify_all(bool min_heap)
|
||||
for (i = 0; i < heap.nr; i++)
|
||||
values[i] = get_random_u32();
|
||||
|
||||
min_heapify_all(&heap, &funcs);
|
||||
min_heapify_all(&heap, &funcs, NULL);
|
||||
err += pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
return err;
|
||||
@@ -96,13 +97,12 @@ static __init int test_heap_push(bool min_heap)
|
||||
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
|
||||
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
|
||||
int values[ARRAY_SIZE(data)];
|
||||
struct min_heap heap = {
|
||||
struct min_heap_test heap = {
|
||||
.data = values,
|
||||
.nr = 0,
|
||||
.size = ARRAY_SIZE(values),
|
||||
};
|
||||
struct min_heap_callbacks funcs = {
|
||||
.elem_size = sizeof(int),
|
||||
.less = min_heap ? less_than : greater_than,
|
||||
.swp = swap_ints,
|
||||
};
|
||||
@@ -110,14 +110,14 @@ static __init int test_heap_push(bool min_heap)
|
||||
|
||||
/* Test with known set of values copied from data. */
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++)
|
||||
min_heap_push(&heap, &data[i], &funcs);
|
||||
min_heap_push(&heap, &data[i], &funcs, NULL);
|
||||
|
||||
err = pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
/* Test with randomly generated values. */
|
||||
while (heap.nr < heap.size) {
|
||||
temp = get_random_u32();
|
||||
min_heap_push(&heap, &temp, &funcs);
|
||||
min_heap_push(&heap, &temp, &funcs, NULL);
|
||||
}
|
||||
err += pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
@@ -129,13 +129,12 @@ static __init int test_heap_pop_push(bool min_heap)
|
||||
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
|
||||
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
|
||||
int values[ARRAY_SIZE(data)];
|
||||
struct min_heap heap = {
|
||||
struct min_heap_test heap = {
|
||||
.data = values,
|
||||
.nr = 0,
|
||||
.size = ARRAY_SIZE(values),
|
||||
};
|
||||
struct min_heap_callbacks funcs = {
|
||||
.elem_size = sizeof(int),
|
||||
.less = min_heap ? less_than : greater_than,
|
||||
.swp = swap_ints,
|
||||
};
|
||||
@@ -144,28 +143,62 @@ static __init int test_heap_pop_push(bool min_heap)
|
||||
/* Fill values with data to pop and replace. */
|
||||
temp = min_heap ? 0x80000000 : 0x7FFFFFFF;
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++)
|
||||
min_heap_push(&heap, &temp, &funcs);
|
||||
min_heap_push(&heap, &temp, &funcs, NULL);
|
||||
|
||||
/* Test with known set of values copied from data. */
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++)
|
||||
min_heap_pop_push(&heap, &data[i], &funcs);
|
||||
min_heap_pop_push(&heap, &data[i], &funcs, NULL);
|
||||
|
||||
err = pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
heap.nr = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++)
|
||||
min_heap_push(&heap, &temp, &funcs);
|
||||
min_heap_push(&heap, &temp, &funcs, NULL);
|
||||
|
||||
/* Test with randomly generated values. */
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||
temp = get_random_u32();
|
||||
min_heap_pop_push(&heap, &temp, &funcs);
|
||||
min_heap_pop_push(&heap, &temp, &funcs, NULL);
|
||||
}
|
||||
err += pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __init int test_heap_del(bool min_heap)
|
||||
{
|
||||
int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
|
||||
-3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
|
||||
struct min_heap_test heap;
|
||||
|
||||
min_heap_init(&heap, values, ARRAY_SIZE(values));
|
||||
heap.nr = ARRAY_SIZE(values);
|
||||
struct min_heap_callbacks funcs = {
|
||||
.less = min_heap ? less_than : greater_than,
|
||||
.swp = swap_ints,
|
||||
};
|
||||
int i, err;
|
||||
|
||||
/* Test with known set of values. */
|
||||
min_heapify_all(&heap, &funcs, NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
|
||||
min_heap_del(&heap, get_random_u32() % heap.nr, &funcs, NULL);
|
||||
err = pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
|
||||
/* Test with randomly generated values. */
|
||||
heap.nr = ARRAY_SIZE(values);
|
||||
for (i = 0; i < heap.nr; i++)
|
||||
values[i] = get_random_u32();
|
||||
min_heapify_all(&heap, &funcs, NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
|
||||
min_heap_del(&heap, get_random_u32() % heap.nr, &funcs, NULL);
|
||||
err += pop_verify_heap(min_heap, &heap, &funcs);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init test_min_heap_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
@@ -176,6 +209,8 @@ static int __init test_min_heap_init(void)
|
||||
err += test_heap_push(false);
|
||||
err += test_heap_pop_push(true);
|
||||
err += test_heap_pop_push(false);
|
||||
err += test_heap_del(true);
|
||||
err += test_heap_del(false);
|
||||
if (err) {
|
||||
pr_err("test failed with %d errors\n", err);
|
||||
return -EINVAL;
|
||||
@@ -191,4 +226,5 @@ static void __exit test_min_heap_exit(void)
|
||||
}
|
||||
module_exit(test_min_heap_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Test cases for the min max heap");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -31,4 +31,5 @@ static void __exit test_module_exit(void)
|
||||
module_exit(test_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
|
||||
MODULE_DESCRIPTION("module loading subsystem test module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -824,4 +824,5 @@ static void __init selftest(void)
|
||||
|
||||
KSTM_MODULE_LOADERS(test_printf);
|
||||
MODULE_AUTHOR("Rasmus Villemoes <linux@rasmusvillemoes.dk>");
|
||||
MODULE_DESCRIPTION("Test cases for printf facility");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Referrence tracker self test.
|
||||
* Reference tracker self test.
|
||||
*
|
||||
* Copyright (c) 2021 Eric Dumazet <edumazet@google.com>
|
||||
*/
|
||||
@@ -112,4 +112,5 @@ static void __exit test_ref_tracker_exit(void)
|
||||
module_init(test_ref_tracker_init);
|
||||
module_exit(test_ref_tracker_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Reference tracker self test");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -810,4 +810,5 @@ static void __init selftest(void)
|
||||
|
||||
KSTM_MODULE_LOADERS(test_scanf);
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_DESCRIPTION("Test cases for sscanf facility");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -29,7 +29,19 @@ static void test_sort(struct kunit *test)
|
||||
|
||||
sort(a, TEST_LEN, sizeof(*a), cmpint, NULL);
|
||||
|
||||
for (i = 0; i < TEST_LEN-1; i++)
|
||||
for (i = 0; i < TEST_LEN - 1; i++)
|
||||
KUNIT_ASSERT_LE(test, a[i], a[i + 1]);
|
||||
|
||||
r = 48;
|
||||
|
||||
for (i = 0; i < TEST_LEN - 1; i++) {
|
||||
r = (r * 725861) % 6599;
|
||||
a[i] = r;
|
||||
}
|
||||
|
||||
sort(a, TEST_LEN - 1, sizeof(*a), cmpint, NULL);
|
||||
|
||||
for (i = 0; i < TEST_LEN - 2; i++)
|
||||
KUNIT_ASSERT_LE(test, a[i], a[i + 1]);
|
||||
}
|
||||
|
||||
@@ -45,4 +57,5 @@ static struct kunit_suite sort_test_suite = {
|
||||
|
||||
kunit_test_suites(&sort_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("sort() KUnit test suite");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -57,4 +57,5 @@ module_init(test_static_key_base_init);
|
||||
module_exit(test_static_key_base_exit);
|
||||
|
||||
MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
|
||||
MODULE_DESCRIPTION("Kernel module to support testing static keys");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -236,4 +236,5 @@ module_init(test_static_key_init);
|
||||
module_exit(test_static_key_exit);
|
||||
|
||||
MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
|
||||
MODULE_DESCRIPTION("Kernel module for testing static keys");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -280,4 +280,5 @@ static void __exit test_sysctl_exit(void)
|
||||
module_exit(test_sysctl_exit);
|
||||
|
||||
MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
|
||||
MODULE_DESCRIPTION("proc sysctl test driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -156,4 +156,5 @@ static void __exit test_ubsan_exit(void)
|
||||
module_exit(test_ubsan_exit);
|
||||
|
||||
MODULE_AUTHOR("Jinbum Park <jinb.park7@gmail.com>");
|
||||
MODULE_DESCRIPTION("UBSAN unit test");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Kernel module for testing copy_to/from_user infrastructure.
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved
|
||||
*
|
||||
* Authors:
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/*
|
||||
* Several 32-bit architectures support 64-bit {get,put}_user() calls.
|
||||
* As there doesn't appear to be anything that can safely determine
|
||||
* their capability at compile-time, we just have to opt-out certain archs.
|
||||
*/
|
||||
#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
|
||||
!defined(CONFIG_M68K) && \
|
||||
!defined(CONFIG_MICROBLAZE) && \
|
||||
!defined(CONFIG_NIOS2) && \
|
||||
!defined(CONFIG_PPC32) && \
|
||||
!defined(CONFIG_SUPERH))
|
||||
# define TEST_U64
|
||||
#endif
|
||||
|
||||
#define test(condition, msg, ...) \
|
||||
({ \
|
||||
int cond = (condition); \
|
||||
if (cond) \
|
||||
pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \
|
||||
cond; \
|
||||
})
|
||||
|
||||
static bool is_zeroed(void *from, size_t size)
|
||||
{
|
||||
return memchr_inv(from, 0x0, size) == NULL;
|
||||
}
|
||||
|
||||
static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t start, end, i, zero_start, zero_end;
|
||||
|
||||
if (test(size < 2 * PAGE_SIZE, "buffer too small"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We want to cross a page boundary to exercise the code more
|
||||
* effectively. We also don't want to make the size we scan too large,
|
||||
* otherwise the test can take a long time and cause soft lockups. So
|
||||
* scan a 1024 byte region across the page boundary.
|
||||
*/
|
||||
size = 1024;
|
||||
start = PAGE_SIZE - (size / 2);
|
||||
|
||||
kmem += start;
|
||||
umem += start;
|
||||
|
||||
zero_start = size / 4;
|
||||
zero_end = size - zero_start;
|
||||
|
||||
/*
|
||||
* We conduct a series of check_nonzero_user() tests on a block of
|
||||
* memory with the following byte-pattern (trying every possible
|
||||
* [start,end] pair):
|
||||
*
|
||||
* [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
|
||||
*
|
||||
* And we verify that check_nonzero_user() acts identically to
|
||||
* memchr_inv().
|
||||
*/
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
for (i = 1; i < zero_start; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
for (i = zero_end; i < size; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
|
||||
ret |= test(copy_to_user(umem, kmem, size),
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
for (start = 0; start <= size; start++) {
|
||||
for (end = start; end <= size; end++) {
|
||||
size_t len = end - start;
|
||||
int retval = check_zeroed_user(umem + start, len);
|
||||
int expected = is_zeroed(kmem + start, len);
|
||||
|
||||
ret |= test(retval != expected,
|
||||
"check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
|
||||
retval, expected, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_copy_struct_from_user(char *kmem, char __user *umem,
|
||||
size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
char *umem_src = NULL, *expected = NULL;
|
||||
size_t ksize, usize;
|
||||
|
||||
umem_src = kmalloc(size, GFP_KERNEL);
|
||||
ret = test(umem_src == NULL, "kmalloc failed");
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
expected = kmalloc(size, GFP_KERNEL);
|
||||
ret = test(expected == NULL, "kmalloc failed");
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
/* Fill umem with a fixed byte pattern. */
|
||||
memset(umem_src, 0x3e, size);
|
||||
ret |= test(copy_to_user(umem, umem_src, size),
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
/* Check basic case -- (usize == ksize). */
|
||||
ksize = size;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize == ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize == ksize) gives unexpected copy");
|
||||
|
||||
/* Old userspace case -- (usize < ksize). */
|
||||
ksize = size;
|
||||
usize = size / 2;
|
||||
|
||||
memcpy(expected, umem_src, usize);
|
||||
memset(expected + usize, 0x0, ksize - usize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize < ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize < ksize) gives unexpected copy");
|
||||
|
||||
/* New userspace (-E2BIG) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG,
|
||||
"copy_struct_from_user(usize > ksize) didn't give E2BIG");
|
||||
|
||||
/* New userspace (success) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
ret |= test(clear_user(umem + ksize, usize - ksize),
|
||||
"legitimate clear_user failed");
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize > ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize > ksize) gives unexpected copy");
|
||||
|
||||
out_free:
|
||||
kfree(expected);
|
||||
kfree(umem_src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init test_user_copy_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
char *kmem;
|
||||
char __user *usermem;
|
||||
char *bad_usermem;
|
||||
unsigned long user_addr;
|
||||
u8 val_u8;
|
||||
u16 val_u16;
|
||||
u32 val_u32;
|
||||
#ifdef TEST_U64
|
||||
u64 val_u64;
|
||||
#endif
|
||||
|
||||
kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
|
||||
if (!kmem)
|
||||
return -ENOMEM;
|
||||
|
||||
user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
if (user_addr >= (unsigned long)(TASK_SIZE)) {
|
||||
pr_warn("Failed to allocate user memory\n");
|
||||
kfree(kmem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usermem = (char __user *)user_addr;
|
||||
bad_usermem = (char *)user_addr;
|
||||
|
||||
/*
|
||||
* Legitimate usage: none of these copies should fail.
|
||||
*/
|
||||
memset(kmem, 0x3a, PAGE_SIZE * 2);
|
||||
ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
|
||||
"legitimate copy_to_user failed");
|
||||
memset(kmem, 0x0, PAGE_SIZE);
|
||||
ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
|
||||
"legitimate copy_from_user failed");
|
||||
ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE),
|
||||
"legitimate usercopy failed to copy data");
|
||||
|
||||
#define test_legit(size, check) \
|
||||
do { \
|
||||
val_##size = check; \
|
||||
ret |= test(put_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate put_user (" #size ") failed"); \
|
||||
val_##size = 0; \
|
||||
ret |= test(get_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate get_user (" #size ") failed"); \
|
||||
ret |= test(val_##size != check, \
|
||||
"legitimate get_user (" #size ") failed to do copy"); \
|
||||
if (val_##size != check) { \
|
||||
pr_info("0x%llx != 0x%llx\n", \
|
||||
(unsigned long long)val_##size, \
|
||||
(unsigned long long)check); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
test_legit(u8, 0x5a);
|
||||
test_legit(u16, 0x5a5b);
|
||||
test_legit(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_legit(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_legit
|
||||
|
||||
/* Test usage of check_nonzero_user(). */
|
||||
ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE);
|
||||
/* Test usage of copy_struct_from_user(). */
|
||||
ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE);
|
||||
|
||||
/*
|
||||
* Invalid usage: none of these copies should succeed.
|
||||
*/
|
||||
|
||||
/* Prepare kernel memory with check values. */
|
||||
memset(kmem, 0x5a, PAGE_SIZE);
|
||||
memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
|
||||
|
||||
/* Reject kernel-to-kernel copies through copy_from_user(). */
|
||||
ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
|
||||
PAGE_SIZE),
|
||||
"illegal all-kernel copy_from_user passed");
|
||||
|
||||
/* Destination half of buffer should have been zeroed. */
|
||||
ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE),
|
||||
"zeroing failure for illegal all-kernel copy_from_user");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* When running with SMAP/PAN/etc, this will Oops the kernel
|
||||
* due to the zeroing of userspace memory on failure. This needs
|
||||
* to be tested in LKDTM instead, since this test module does not
|
||||
* expect to explode.
|
||||
*/
|
||||
ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
|
||||
PAGE_SIZE),
|
||||
"illegal reversed copy_from_user passed");
|
||||
#endif
|
||||
ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
|
||||
PAGE_SIZE),
|
||||
"illegal all-kernel copy_to_user passed");
|
||||
ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
|
||||
PAGE_SIZE),
|
||||
"illegal reversed copy_to_user passed");
|
||||
|
||||
#define test_illegal(size, check) \
|
||||
do { \
|
||||
val_##size = (check); \
|
||||
ret |= test(!get_user(val_##size, (size __user *)kmem), \
|
||||
"illegal get_user (" #size ") passed"); \
|
||||
ret |= test(val_##size != (size)0, \
|
||||
"zeroing failure for illegal get_user (" #size ")"); \
|
||||
if (val_##size != (size)0) { \
|
||||
pr_info("0x%llx != 0\n", \
|
||||
(unsigned long long)val_##size); \
|
||||
} \
|
||||
ret |= test(!put_user(val_##size, (size __user *)kmem), \
|
||||
"illegal put_user (" #size ") passed"); \
|
||||
} while (0)
|
||||
|
||||
test_illegal(u8, 0x5a);
|
||||
test_illegal(u16, 0x5a5b);
|
||||
test_illegal(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_illegal
|
||||
|
||||
vm_munmap(user_addr, PAGE_SIZE * 2);
|
||||
kfree(kmem);
|
||||
|
||||
if (ret == 0) {
|
||||
pr_info("tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
module_init(test_user_copy_init);
|
||||
|
||||
static void __exit test_user_copy_exit(void)
|
||||
{
|
||||
pr_info("unloaded.\n");
|
||||
}
|
||||
|
||||
module_exit(test_user_copy_exit);
|
||||
|
||||
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -130,4 +130,5 @@ static void __exit test_uuid_exit(void)
|
||||
module_exit(test_uuid_exit);
|
||||
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Test cases for lib/uuid.c module");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -2173,4 +2173,5 @@ static void xarray_exit(void)
|
||||
module_init(xarray_checks);
|
||||
module_exit(xarray_exit);
|
||||
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
|
||||
MODULE_DESCRIPTION("XArray API test module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -216,6 +216,7 @@ static void __exit exit_bm(void)
|
||||
textsearch_unregister(&bm_ops);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("Boyer-Moore text search implementation");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_bm);
|
||||
|
||||
@@ -331,6 +331,7 @@ static void __exit exit_fsm(void)
|
||||
textsearch_unregister(&fsm_ops);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("naive finite state machine text search");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_fsm);
|
||||
|
||||
@@ -147,6 +147,7 @@ static void __exit exit_kmp(void)
|
||||
textsearch_unregister(&kmp_ops);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("Knuth-Morris-Pratt text search implementation");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_kmp);
|
||||
|
||||
@@ -12,40 +12,18 @@
|
||||
|
||||
/* out-of-line parts */
|
||||
|
||||
#ifndef INLINE_COPY_FROM_USER
|
||||
#if !defined(INLINE_COPY_FROM_USER) || defined(CONFIG_RUST)
|
||||
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
unsigned long res = n;
|
||||
might_fault();
|
||||
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
|
||||
/*
|
||||
* Ensure that bad access_ok() speculation will not
|
||||
* lead to nasty side effects *after* the copy is
|
||||
* finished:
|
||||
*/
|
||||
barrier_nospec();
|
||||
instrument_copy_from_user_before(to, from, n);
|
||||
res = raw_copy_from_user(to, from, n);
|
||||
instrument_copy_from_user_after(to, from, n, res);
|
||||
}
|
||||
if (unlikely(res))
|
||||
memset(to + (n - res), 0, res);
|
||||
return res;
|
||||
return _inline_copy_from_user(to, from, n);
|
||||
}
|
||||
EXPORT_SYMBOL(_copy_from_user);
|
||||
#endif
|
||||
|
||||
#ifndef INLINE_COPY_TO_USER
|
||||
#if !defined(INLINE_COPY_TO_USER) || defined(CONFIG_RUST)
|
||||
unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
might_fault();
|
||||
if (should_fail_usercopy())
|
||||
return n;
|
||||
if (likely(access_ok(to, n))) {
|
||||
instrument_copy_to_user(to, from, n);
|
||||
n = raw_copy_to_user(to, from, n);
|
||||
}
|
||||
return n;
|
||||
return _inline_copy_to_user(to, from, n);
|
||||
}
|
||||
EXPORT_SYMBOL(_copy_to_user);
|
||||
#endif
|
||||
|
||||
335
lib/usercopy_kunit.c
Normal file
335
lib/usercopy_kunit.c
Normal file
@@ -0,0 +1,335 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Kernel module for testing copy_to/from_user infrastructure.
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved
|
||||
*
|
||||
* Authors:
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
/*
|
||||
* Several 32-bit architectures support 64-bit {get,put}_user() calls.
|
||||
* As there doesn't appear to be anything that can safely determine
|
||||
* their capability at compile-time, we just have to opt-out certain archs.
|
||||
*/
|
||||
#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
|
||||
!defined(CONFIG_M68K) && \
|
||||
!defined(CONFIG_MICROBLAZE) && \
|
||||
!defined(CONFIG_NIOS2) && \
|
||||
!defined(CONFIG_PPC32) && \
|
||||
!defined(CONFIG_SUPERH))
|
||||
# define TEST_U64
|
||||
#endif
|
||||
|
||||
struct usercopy_test_priv {
|
||||
char *kmem;
|
||||
char __user *umem;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static bool is_zeroed(void *from, size_t size)
|
||||
{
|
||||
return memchr_inv(from, 0x0, size) == NULL;
|
||||
}
|
||||
|
||||
/* Test usage of check_nonzero_user(). */
|
||||
static void usercopy_test_check_nonzero_user(struct kunit *test)
|
||||
{
|
||||
size_t start, end, i, zero_start, zero_end;
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *umem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
size_t size = priv->size;
|
||||
|
||||
KUNIT_ASSERT_GE_MSG(test, size, 2 * PAGE_SIZE, "buffer too small");
|
||||
|
||||
/*
|
||||
* We want to cross a page boundary to exercise the code more
|
||||
* effectively. We also don't want to make the size we scan too large,
|
||||
* otherwise the test can take a long time and cause soft lockups. So
|
||||
* scan a 1024 byte region across the page boundary.
|
||||
*/
|
||||
size = 1024;
|
||||
start = PAGE_SIZE - (size / 2);
|
||||
|
||||
kmem += start;
|
||||
umem += start;
|
||||
|
||||
zero_start = size / 4;
|
||||
zero_end = size - zero_start;
|
||||
|
||||
/*
|
||||
* We conduct a series of check_nonzero_user() tests on a block of
|
||||
* memory with the following byte-pattern (trying every possible
|
||||
* [start,end] pair):
|
||||
*
|
||||
* [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
|
||||
*
|
||||
* And we verify that check_nonzero_user() acts identically to
|
||||
* memchr_inv().
|
||||
*/
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
for (i = 1; i < zero_start; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
for (i = zero_end; i < size; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_to_user(umem, kmem, size), 0,
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
for (start = 0; start <= size; start++) {
|
||||
for (end = start; end <= size; end++) {
|
||||
size_t len = end - start;
|
||||
int retval = check_zeroed_user(umem + start, len);
|
||||
int expected = is_zeroed(kmem + start, len);
|
||||
|
||||
KUNIT_ASSERT_EQ_MSG(test, retval, expected,
|
||||
"check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
|
||||
retval, expected, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test usage of copy_struct_from_user(). */
|
||||
static void usercopy_test_copy_struct_from_user(struct kunit *test)
|
||||
{
|
||||
char *umem_src = NULL, *expected = NULL;
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *umem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
size_t size = priv->size;
|
||||
size_t ksize, usize;
|
||||
|
||||
umem_src = kunit_kmalloc(test, size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, umem_src);
|
||||
|
||||
expected = kunit_kmalloc(test, size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected);
|
||||
|
||||
/* Fill umem with a fixed byte pattern. */
|
||||
memset(umem_src, 0x3e, size);
|
||||
KUNIT_ASSERT_EQ_MSG(test, copy_to_user(umem, umem_src, size), 0,
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
/* Check basic case -- (usize == ksize). */
|
||||
ksize = size;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize == ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize == ksize) gives unexpected copy");
|
||||
|
||||
/* Old userspace case -- (usize < ksize). */
|
||||
ksize = size;
|
||||
usize = size / 2;
|
||||
|
||||
memcpy(expected, umem_src, usize);
|
||||
memset(expected + usize, 0x0, ksize - usize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize < ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize < ksize) gives unexpected copy");
|
||||
|
||||
/* New userspace (-E2BIG) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), -E2BIG,
|
||||
"copy_struct_from_user(usize > ksize) didn't give E2BIG");
|
||||
|
||||
/* New userspace (success) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
KUNIT_EXPECT_EQ_MSG(test, clear_user(umem + ksize, usize - ksize), 0,
|
||||
"legitimate clear_user failed");
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize > ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize > ksize) gives unexpected copy");
|
||||
}
|
||||
|
||||
/*
|
||||
* Legitimate usage: none of these copies should fail.
|
||||
*/
|
||||
static void usercopy_test_valid(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *usermem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
|
||||
memset(kmem, 0x3a, PAGE_SIZE * 2);
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, copy_to_user(usermem, kmem, PAGE_SIZE),
|
||||
"legitimate copy_to_user failed");
|
||||
memset(kmem, 0x0, PAGE_SIZE);
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, copy_from_user(kmem, usermem, PAGE_SIZE),
|
||||
"legitimate copy_from_user failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, kmem + PAGE_SIZE, PAGE_SIZE,
|
||||
"legitimate usercopy failed to copy data");
|
||||
|
||||
#define test_legit(size, check) \
|
||||
do { \
|
||||
size val_##size = (check); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, \
|
||||
put_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate put_user (" #size ") failed"); \
|
||||
val_##size = 0; \
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, \
|
||||
get_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate get_user (" #size ") failed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, val_##size, check, \
|
||||
"legitimate get_user (" #size ") failed to do copy"); \
|
||||
} while (0)
|
||||
|
||||
test_legit(u8, 0x5a);
|
||||
test_legit(u16, 0x5a5b);
|
||||
test_legit(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_legit(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_legit
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalid usage: none of these copies should succeed.
|
||||
*/
|
||||
static void usercopy_test_invalid(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *usermem = priv->umem;
|
||||
char *bad_usermem = (char *)usermem;
|
||||
char *kmem = priv->kmem;
|
||||
u64 *kmem_u64 = (u64 *)kmem;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ALTERNATE_USER_ADDRESS_SPACE) ||
|
||||
!IS_ENABLED(CONFIG_MMU)) {
|
||||
kunit_skip(test, "Testing for kernel/userspace address confusion is only sensible on architectures with a shared address space");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare kernel memory with check values. */
|
||||
memset(kmem, 0x5a, PAGE_SIZE);
|
||||
memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
|
||||
|
||||
/* Reject kernel-to-kernel copies through copy_from_user(). */
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
|
||||
PAGE_SIZE), 0,
|
||||
"illegal all-kernel copy_from_user passed");
|
||||
|
||||
/* Destination half of buffer should have been zeroed. */
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem + PAGE_SIZE, kmem, PAGE_SIZE,
|
||||
"zeroing failure for illegal all-kernel copy_from_user");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* When running with SMAP/PAN/etc, this will Oops the kernel
|
||||
* due to the zeroing of userspace memory on failure. This needs
|
||||
* to be tested in LKDTM instead, since this test module does not
|
||||
* expect to explode.
|
||||
*/
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_from_user(bad_usermem, (char __user *)kmem,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal reversed copy_from_user passed");
|
||||
#endif
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal all-kernel copy_to_user passed");
|
||||
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, bad_usermem,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal reversed copy_to_user passed");
|
||||
|
||||
#define test_illegal(size, check) \
|
||||
do { \
|
||||
size val_##size = (check); \
|
||||
/* get_user() */ \
|
||||
KUNIT_EXPECT_NE_MSG(test, get_user(val_##size, (size __user *)kmem), 0, \
|
||||
"illegal get_user (" #size ") passed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, val_##size, 0, \
|
||||
"zeroing failure for illegal get_user (" #size ")"); \
|
||||
/* put_user() */ \
|
||||
*kmem_u64 = 0xF09FA4AFF09FA4AF; \
|
||||
KUNIT_EXPECT_NE_MSG(test, put_user(val_##size, (size __user *)kmem), 0, \
|
||||
"illegal put_user (" #size ") passed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, *kmem_u64, 0xF09FA4AFF09FA4AF, \
|
||||
"illegal put_user (" #size ") wrote to kernel memory!"); \
|
||||
} while (0)
|
||||
|
||||
test_illegal(u8, 0x5a);
|
||||
test_illegal(u16, 0x5a5b);
|
||||
test_illegal(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_illegal
|
||||
}
|
||||
|
||||
static int usercopy_test_init(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv;
|
||||
unsigned long user_addr;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MMU)) {
|
||||
kunit_skip(test, "Userspace allocation testing not available on non-MMU systems");
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
|
||||
test->priv = priv;
|
||||
priv->size = PAGE_SIZE * 2;
|
||||
|
||||
priv->kmem = kunit_kmalloc(test, priv->size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->kmem);
|
||||
|
||||
user_addr = kunit_vm_mmap(test, NULL, 0, priv->size,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
KUNIT_ASSERT_NE_MSG(test, user_addr, 0,
|
||||
"Could not create userspace mm");
|
||||
KUNIT_ASSERT_LT_MSG(test, user_addr, (unsigned long)TASK_SIZE,
|
||||
"Failed to allocate user memory");
|
||||
priv->umem = (char __user *)user_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kunit_case usercopy_test_cases[] = {
|
||||
KUNIT_CASE(usercopy_test_valid),
|
||||
KUNIT_CASE(usercopy_test_invalid),
|
||||
KUNIT_CASE(usercopy_test_check_nonzero_user),
|
||||
KUNIT_CASE(usercopy_test_copy_struct_from_user),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite usercopy_test_suite = {
|
||||
.name = "usercopy",
|
||||
.init = usercopy_test_init,
|
||||
.test_cases = usercopy_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&usercopy_test_suite);
|
||||
MODULE_AUTHOR("Kees Cook <kees@kernel.org>");
|
||||
MODULE_DESCRIPTION("Kernel module for testing copy_to/from_user infrastructure");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user