xref: /linux-6.15/lib/kunit/user_alloc.c (revision cdd30ebb)
151104c19SKees Cook // SPDX-License-Identifier: GPL-2.0
251104c19SKees Cook /*
351104c19SKees Cook  * KUnit userspace memory allocation resource management.
451104c19SKees Cook  */
551104c19SKees Cook #include <kunit/resource.h>
651104c19SKees Cook #include <kunit/test.h>
751104c19SKees Cook #include <linux/kthread.h>
851104c19SKees Cook #include <linux/mm.h>
951104c19SKees Cook 
1051104c19SKees Cook struct kunit_vm_mmap_resource {
1151104c19SKees Cook 	unsigned long addr;
1251104c19SKees Cook 	size_t size;
1351104c19SKees Cook };
1451104c19SKees Cook 
1551104c19SKees Cook /* vm_mmap() arguments */
1651104c19SKees Cook struct kunit_vm_mmap_params {
1751104c19SKees Cook 	struct file *file;
1851104c19SKees Cook 	unsigned long addr;
1951104c19SKees Cook 	unsigned long len;
2051104c19SKees Cook 	unsigned long prot;
2151104c19SKees Cook 	unsigned long flag;
2251104c19SKees Cook 	unsigned long offset;
2351104c19SKees Cook };
2451104c19SKees Cook 
2551104c19SKees Cook /* Create and attach a new mm if it doesn't already exist. */
kunit_attach_mm(void)2651104c19SKees Cook static int kunit_attach_mm(void)
2751104c19SKees Cook {
2851104c19SKees Cook 	struct mm_struct *mm;
2951104c19SKees Cook 
3051104c19SKees Cook 	if (current->mm)
3151104c19SKees Cook 		return 0;
3251104c19SKees Cook 
334d6cf248SKees Cook 	/* arch_pick_mmap_layout() is only sane with MMU systems. */
344d6cf248SKees Cook 	if (!IS_ENABLED(CONFIG_MMU))
354d6cf248SKees Cook 		return -EINVAL;
364d6cf248SKees Cook 
3751104c19SKees Cook 	mm = mm_alloc();
3851104c19SKees Cook 	if (!mm)
3951104c19SKees Cook 		return -ENOMEM;
4051104c19SKees Cook 
4151104c19SKees Cook 	/* Define the task size. */
4251104c19SKees Cook 	mm->task_size = TASK_SIZE;
4351104c19SKees Cook 
4451104c19SKees Cook 	/* Make sure we can allocate new VMAs. */
4551104c19SKees Cook 	arch_pick_mmap_layout(mm, &current->signal->rlim[RLIMIT_STACK]);
4651104c19SKees Cook 
4751104c19SKees Cook 	/* Attach the mm. It will be cleaned up when the process dies. */
4851104c19SKees Cook 	kthread_use_mm(mm);
4951104c19SKees Cook 
5051104c19SKees Cook 	return 0;
5151104c19SKees Cook }
5251104c19SKees Cook 
kunit_vm_mmap_init(struct kunit_resource * res,void * context)5351104c19SKees Cook static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
5451104c19SKees Cook {
5551104c19SKees Cook 	struct kunit_vm_mmap_params *p = context;
5651104c19SKees Cook 	struct kunit_vm_mmap_resource vres;
5751104c19SKees Cook 	int ret;
5851104c19SKees Cook 
5951104c19SKees Cook 	ret = kunit_attach_mm();
6051104c19SKees Cook 	if (ret)
6151104c19SKees Cook 		return ret;
6251104c19SKees Cook 
6351104c19SKees Cook 	vres.size = p->len;
6451104c19SKees Cook 	vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
6551104c19SKees Cook 	if (!vres.addr)
6651104c19SKees Cook 		return -ENOMEM;
6751104c19SKees Cook 	res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
6851104c19SKees Cook 	if (!res->data) {
6951104c19SKees Cook 		vm_munmap(vres.addr, vres.size);
7051104c19SKees Cook 		return -ENOMEM;
7151104c19SKees Cook 	}
7251104c19SKees Cook 
7351104c19SKees Cook 	return 0;
7451104c19SKees Cook }
7551104c19SKees Cook 
kunit_vm_mmap_free(struct kunit_resource * res)7651104c19SKees Cook static void kunit_vm_mmap_free(struct kunit_resource *res)
7751104c19SKees Cook {
7851104c19SKees Cook 	struct kunit_vm_mmap_resource *vres = res->data;
7951104c19SKees Cook 
8051104c19SKees Cook 	/*
8151104c19SKees Cook 	 * Since this is executed from the test monitoring process,
8251104c19SKees Cook 	 * the test's mm has already been torn down. We don't need
8351104c19SKees Cook 	 * to run vm_munmap(vres->addr, vres->size), only clean up
8451104c19SKees Cook 	 * the vres.
8551104c19SKees Cook 	 */
8651104c19SKees Cook 
8751104c19SKees Cook 	kfree(vres);
8851104c19SKees Cook 	res->data = NULL;
8951104c19SKees Cook }
9051104c19SKees Cook 
kunit_vm_mmap(struct kunit * test,struct file * file,unsigned long addr,unsigned long len,unsigned long prot,unsigned long flag,unsigned long offset)9151104c19SKees Cook unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
9251104c19SKees Cook 			    unsigned long addr, unsigned long len,
9351104c19SKees Cook 			    unsigned long prot, unsigned long flag,
9451104c19SKees Cook 			    unsigned long offset)
9551104c19SKees Cook {
9651104c19SKees Cook 	struct kunit_vm_mmap_params params = {
9751104c19SKees Cook 		.file = file,
9851104c19SKees Cook 		.addr = addr,
9951104c19SKees Cook 		.len = len,
10051104c19SKees Cook 		.prot = prot,
10151104c19SKees Cook 		.flag = flag,
10251104c19SKees Cook 		.offset = offset,
10351104c19SKees Cook 	};
10451104c19SKees Cook 	struct kunit_vm_mmap_resource *vres;
10551104c19SKees Cook 
10651104c19SKees Cook 	vres = kunit_alloc_resource(test,
10751104c19SKees Cook 				    kunit_vm_mmap_init,
10851104c19SKees Cook 				    kunit_vm_mmap_free,
10951104c19SKees Cook 				    GFP_KERNEL,
11051104c19SKees Cook 				    &params);
11151104c19SKees Cook 	if (vres)
11251104c19SKees Cook 		return vres->addr;
11351104c19SKees Cook 	return 0;
11451104c19SKees Cook }
11551104c19SKees Cook EXPORT_SYMBOL_GPL(kunit_vm_mmap);
11651104c19SKees Cook 
117*cdd30ebbSPeter Zijlstra MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
118