xref: /linux/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c (revision 6f47c7ae8c7afaf9ad291d39f0d3974f191a7946)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2017-2023 SUSE
4  * Authors: Libor Pechacek <lpechacek@suse.cz>
5  *          Nicolai Stange <nstange@suse.de>
6  *          Marcos Paulo de Souza <mpdesouza@suse.com>
7  */
8 
9 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <linux/sched.h>
12 #include <linux/slab.h>
13 #include <linux/livepatch.h>
14 
15 #if defined(__x86_64__)
16 #define FN_PREFIX __x64_
17 #elif defined(__s390x__)
18 #define FN_PREFIX __s390x_
19 #elif defined(__aarch64__)
20 #define FN_PREFIX __arm64_
21 #else
22 /* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER */
23 #define FN_PREFIX
24 #endif
25 
26 /* Protects klp_pids */
27 static DEFINE_MUTEX(kpid_mutex);
28 
29 static unsigned int npids, npids_pending;
30 static int klp_pids[NR_CPUS];
31 module_param_array(klp_pids, int, &npids_pending, 0);
32 MODULE_PARM_DESC(klp_pids, "Array of pids to be transitioned to livepatched state.");
33 
34 static ssize_t npids_show(struct kobject *kobj, struct kobj_attribute *attr,
35 			  char *buf)
36 {
37 	return sprintf(buf, "%u\n", npids_pending);
38 }
39 
40 static struct kobj_attribute klp_attr = __ATTR_RO(npids);
41 static struct kobject *klp_kobj;
42 
43 static asmlinkage long lp_sys_getpid(void)
44 {
45 	int i;
46 
47 	mutex_lock(&kpid_mutex);
48 	if (npids_pending > 0) {
49 		for (i = 0; i < npids; i++) {
50 			if (current->pid == klp_pids[i]) {
51 				klp_pids[i] = 0;
52 				npids_pending--;
53 				break;
54 			}
55 		}
56 	}
57 	mutex_unlock(&kpid_mutex);
58 
59 	return task_tgid_vnr(current);
60 }
61 
62 static struct klp_func vmlinux_funcs[] = {
63 	{
64 		.old_name = __stringify(FN_PREFIX) "sys_getpid",
65 		.new_func = lp_sys_getpid,
66 	}, {}
67 };
68 
69 static struct klp_object objs[] = {
70 	{
71 		/* name being NULL means vmlinux */
72 		.funcs = vmlinux_funcs,
73 	}, {}
74 };
75 
76 static struct klp_patch patch = {
77 	.mod = THIS_MODULE,
78 	.objs = objs,
79 };
80 
81 static int livepatch_init(void)
82 {
83 	int ret;
84 
85 	klp_kobj = kobject_create_and_add("test_klp_syscall", kernel_kobj);
86 	if (!klp_kobj)
87 		return -ENOMEM;
88 
89 	ret = sysfs_create_file(klp_kobj, &klp_attr.attr);
90 	if (ret) {
91 		kobject_put(klp_kobj);
92 		return ret;
93 	}
94 
95 	/*
96 	 * Save the number pids to transition to livepatched state before the
97 	 * number of pending pids is decremented.
98 	 */
99 	npids = npids_pending;
100 
101 	return klp_enable_patch(&patch);
102 }
103 
104 static void livepatch_exit(void)
105 {
106 	kobject_put(klp_kobj);
107 }
108 
109 module_init(livepatch_init);
110 module_exit(livepatch_exit);
111 MODULE_LICENSE("GPL");
112 MODULE_INFO(livepatch, "Y");
113 MODULE_AUTHOR("Libor Pechacek <lpechacek@suse.cz>");
114 MODULE_AUTHOR("Nicolai Stange <nstange@suse.de>");
115 MODULE_AUTHOR("Marcos Paulo de Souza <mpdesouza@suse.com>");
116 MODULE_DESCRIPTION("Livepatch test: syscall transition");
117