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