xref: /linux/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c (revision 9611c0ce215a66770ccbe5c126bf57ba8c31bcad)
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 /*
16  * Before CONFIG_ARCH_HAS_SYSCALL_WRAPPER was introduced there were no
17  * prefixes for system calls.
18  * powerpc set this config based on configs, so it can be enabled or not.
19  */
20 #if defined(CONFIG_ARCH_HAS_SYSCALL_WRAPPER)
21   #if defined(__x86_64__)
22     #define FN_PREFIX __x64_
23   #elif defined(__s390x__)
24     #define FN_PREFIX __s390x_
25   #elif defined(__aarch64__)
26     #define FN_PREFIX __arm64_
27   #elif defined(__powerpc__)
28     #define FN_PREFIX
29   #else
30     #error "Missing syscall wrapper for the given architecture."
31   #endif
32 #else
33   /* Do not set a prefix for architectures that do not enable wrappers. */
34   #define FN_PREFIX
35 #endif
36 
37 /* Protects klp_pids */
38 static DEFINE_MUTEX(kpid_mutex);
39 
40 static unsigned int npids, npids_pending;
41 static int klp_pids[NR_CPUS];
42 module_param_array(klp_pids, int, &npids_pending, 0);
43 MODULE_PARM_DESC(klp_pids, "Array of pids to be transitioned to livepatched state.");
44 
45 static ssize_t npids_show(struct kobject *kobj, struct kobj_attribute *attr,
46 			  char *buf)
47 {
48 	return sprintf(buf, "%u\n", npids_pending);
49 }
50 
51 static struct kobj_attribute klp_attr = __ATTR_RO(npids);
52 static struct kobject *klp_kobj;
53 
54 static asmlinkage long lp_sys_getpid(void)
55 {
56 	int i;
57 
58 	mutex_lock(&kpid_mutex);
59 	if (npids_pending > 0) {
60 		for (i = 0; i < npids; i++) {
61 			if (current->pid == klp_pids[i]) {
62 				klp_pids[i] = 0;
63 				npids_pending--;
64 				break;
65 			}
66 		}
67 	}
68 	mutex_unlock(&kpid_mutex);
69 
70 	return task_tgid_vnr(current);
71 }
72 
73 static struct klp_func vmlinux_funcs[] = {
74 	{
75 		.old_name = __stringify(FN_PREFIX) "sys_getpid",
76 		.new_func = lp_sys_getpid,
77 	}, {}
78 };
79 
80 static struct klp_object objs[] = {
81 	{
82 		/* name being NULL means vmlinux */
83 		.funcs = vmlinux_funcs,
84 	}, {}
85 };
86 
87 static struct klp_patch patch = {
88 	.mod = THIS_MODULE,
89 	.objs = objs,
90 };
91 
92 static int livepatch_init(void)
93 {
94 	int ret;
95 
96 	klp_kobj = kobject_create_and_add("test_klp_syscall", kernel_kobj);
97 	if (!klp_kobj)
98 		return -ENOMEM;
99 
100 	ret = sysfs_create_file(klp_kobj, &klp_attr.attr);
101 	if (ret) {
102 		kobject_put(klp_kobj);
103 		return ret;
104 	}
105 
106 	/*
107 	 * Save the number pids to transition to livepatched state before the
108 	 * number of pending pids is decremented.
109 	 */
110 	npids = npids_pending;
111 
112 	ret = klp_enable_patch(&patch);
113 	if (ret)
114 		kobject_put(klp_kobj);
115 
116 	return ret;
117 }
118 
119 static void livepatch_exit(void)
120 {
121 	kobject_put(klp_kobj);
122 }
123 
124 module_init(livepatch_init);
125 module_exit(livepatch_exit);
126 MODULE_LICENSE("GPL");
127 MODULE_INFO(livepatch, "Y");
128 MODULE_AUTHOR("Libor Pechacek <lpechacek@suse.cz>");
129 MODULE_AUTHOR("Nicolai Stange <nstange@suse.de>");
130 MODULE_AUTHOR("Marcos Paulo de Souza <mpdesouza@suse.com>");
131 MODULE_DESCRIPTION("Livepatch test: syscall transition");
132