Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kunit: test: Add vm_mmap() allocation resource manager

For tests that need to allocate using vm_mmap() (e.g. usercopy and
execve), provide the interface to have the allocation tracked by KUnit
itself. This requires bringing up a placeholder userspace mm.

This combines my earlier attempt at this with Mark Rutland's version[1].

Normally alloc_mm() and arch_pick_mmap_layout() aren't exported for
modules, so export these only for KUnit testing.

Link: https://lore.kernel.org/lkml/20230321122514.1743889-2-mark.rutland@arm.com/ [1]
Co-developed-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: David Gow <davidgow@google.com>
Signed-off-by: Kees Cook <kees@kernel.org>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Kees Cook and committed by
Shuah Khan
51104c19 425ae3ab

+137
+17
include/kunit/test.h
··· 480 480 return kunit_kmalloc_array(test, n, size, gfp | __GFP_ZERO); 481 481 } 482 482 483 + /** 484 + * kunit_vm_mmap() - Allocate KUnit-tracked vm_mmap() area 485 + * @test: The test context object. 486 + * @file: struct file pointer to map from, if any 487 + * @addr: desired address, if any 488 + * @len: how many bytes to allocate 489 + * @prot: mmap PROT_* bits 490 + * @flag: mmap flags 491 + * @offset: offset into @file to start mapping from. 492 + * 493 + * See vm_mmap() for more information. 494 + */ 495 + unsigned long kunit_vm_mmap(struct kunit *test, struct file *file, 496 + unsigned long addr, unsigned long len, 497 + unsigned long prot, unsigned long flag, 498 + unsigned long offset); 499 + 483 500 void kunit_cleanup(struct kunit *test); 484 501 485 502 void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...);
+3
kernel/fork.c
··· 115 115 #define CREATE_TRACE_POINTS 116 116 #include <trace/events/task.h> 117 117 118 + #include <kunit/visibility.h> 119 + 118 120 /* 119 121 * Minimum number of threads to boot the kernel 120 122 */ ··· 1336 1334 memset(mm, 0, sizeof(*mm)); 1337 1335 return mm_init(mm, current, current_user_ns()); 1338 1336 } 1337 + EXPORT_SYMBOL_IF_KUNIT(mm_alloc); 1339 1338 1340 1339 static inline void __mmput(struct mm_struct *mm) 1341 1340 {
+1
lib/kunit/Makefile
··· 2 2 3 3 kunit-objs += test.o \ 4 4 resource.o \ 5 + user_alloc.o \ 5 6 static_stub.o \ 6 7 string-stream.o \ 7 8 assert.o \
+113
lib/kunit/user_alloc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * KUnit userspace memory allocation resource management. 4 + */ 5 + #include <kunit/resource.h> 6 + #include <kunit/test.h> 7 + #include <linux/kthread.h> 8 + #include <linux/mm.h> 9 + 10 + struct kunit_vm_mmap_resource { 11 + unsigned long addr; 12 + size_t size; 13 + }; 14 + 15 + /* vm_mmap() arguments */ 16 + struct kunit_vm_mmap_params { 17 + struct file *file; 18 + unsigned long addr; 19 + unsigned long len; 20 + unsigned long prot; 21 + unsigned long flag; 22 + unsigned long offset; 23 + }; 24 + 25 + /* Create and attach a new mm if it doesn't already exist. */ 26 + static int kunit_attach_mm(void) 27 + { 28 + struct mm_struct *mm; 29 + 30 + if (current->mm) 31 + return 0; 32 + 33 + mm = mm_alloc(); 34 + if (!mm) 35 + return -ENOMEM; 36 + 37 + /* Define the task size. */ 38 + mm->task_size = TASK_SIZE; 39 + 40 + /* Make sure we can allocate new VMAs. */ 41 + arch_pick_mmap_layout(mm, &current->signal->rlim[RLIMIT_STACK]); 42 + 43 + /* Attach the mm. It will be cleaned up when the process dies. */ 44 + kthread_use_mm(mm); 45 + 46 + return 0; 47 + } 48 + 49 + static int kunit_vm_mmap_init(struct kunit_resource *res, void *context) 50 + { 51 + struct kunit_vm_mmap_params *p = context; 52 + struct kunit_vm_mmap_resource vres; 53 + int ret; 54 + 55 + ret = kunit_attach_mm(); 56 + if (ret) 57 + return ret; 58 + 59 + vres.size = p->len; 60 + vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset); 61 + if (!vres.addr) 62 + return -ENOMEM; 63 + res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL); 64 + if (!res->data) { 65 + vm_munmap(vres.addr, vres.size); 66 + return -ENOMEM; 67 + } 68 + 69 + return 0; 70 + } 71 + 72 + static void kunit_vm_mmap_free(struct kunit_resource *res) 73 + { 74 + struct kunit_vm_mmap_resource *vres = res->data; 75 + 76 + /* 77 + * Since this is executed from the test monitoring process, 78 + * the test's mm has already been torn down. We don't need 79 + * to run vm_munmap(vres->addr, vres->size), only clean up 80 + * the vres. 81 + */ 82 + 83 + kfree(vres); 84 + res->data = NULL; 85 + } 86 + 87 + unsigned long kunit_vm_mmap(struct kunit *test, struct file *file, 88 + unsigned long addr, unsigned long len, 89 + unsigned long prot, unsigned long flag, 90 + unsigned long offset) 91 + { 92 + struct kunit_vm_mmap_params params = { 93 + .file = file, 94 + .addr = addr, 95 + .len = len, 96 + .prot = prot, 97 + .flag = flag, 98 + .offset = offset, 99 + }; 100 + struct kunit_vm_mmap_resource *vres; 101 + 102 + vres = kunit_alloc_resource(test, 103 + kunit_vm_mmap_init, 104 + kunit_vm_mmap_free, 105 + GFP_KERNEL, 106 + &params); 107 + if (vres) 108 + return vres->addr; 109 + return 0; 110 + } 111 + EXPORT_SYMBOL_GPL(kunit_vm_mmap); 112 + 113 + MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
+3
mm/util.c
··· 26 26 27 27 #include <linux/uaccess.h> 28 28 29 + #include <kunit/visibility.h> 30 + 29 31 #include "internal.h" 30 32 #include "swap.h" 31 33 ··· 484 482 clear_bit(MMF_TOPDOWN, &mm->flags); 485 483 } 486 484 #endif 485 + EXPORT_SYMBOL_IF_KUNIT(arch_pick_mmap_layout); 487 486 488 487 /** 489 488 * __account_locked_vm - account locked pages to an mm's locked_vm