aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c
blob: dd802783ea849f54d2e771a53d96c8ac608ad404 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2017-2023 SUSE
 * Authors: Libor Pechacek <lpechacek@suse.cz>
 *          Nicolai Stange <nstange@suse.de>
 *          Marcos Paulo de Souza <mpdesouza@suse.com>
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/livepatch.h>

#if defined(__x86_64__)
#define FN_PREFIX __x64_
#elif defined(__s390x__)
#define FN_PREFIX __s390x_
#elif defined(__aarch64__)
#define FN_PREFIX __arm64_
#else
/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER */
#define FN_PREFIX
#endif

/* Protects klp_pids */
static DEFINE_MUTEX(kpid_mutex);

static unsigned int npids, npids_pending;
static int klp_pids[NR_CPUS];
module_param_array(klp_pids, int, &npids_pending, 0);
MODULE_PARM_DESC(klp_pids, "Array of pids to be transitioned to livepatched state.");

static ssize_t npids_show(struct kobject *kobj, struct kobj_attribute *attr,
			  char *buf)
{
	return sprintf(buf, "%u\n", npids_pending);
}

static struct kobj_attribute klp_attr = __ATTR_RO(npids);
static struct kobject *klp_kobj;

static asmlinkage long lp_sys_getpid(void)
{
	int i;

	mutex_lock(&kpid_mutex);
	if (npids_pending > 0) {
		for (i = 0; i < npids; i++) {
			if (current->pid == klp_pids[i]) {
				klp_pids[i] = 0;
				npids_pending--;
				break;
			}
		}
	}
	mutex_unlock(&kpid_mutex);

	return task_tgid_vnr(current);
}

static struct klp_func vmlinux_funcs[] = {
	{
		.old_name = __stringify(FN_PREFIX) "sys_getpid",
		.new_func = lp_sys_getpid,
	}, {}
};

static struct klp_object objs[] = {
	{
		/* name being NULL means vmlinux */
		.funcs = vmlinux_funcs,
	}, {}
};

static struct klp_patch patch = {
	.mod = THIS_MODULE,
	.objs = objs,
};

static int livepatch_init(void)
{
	int ret;

	klp_kobj = kobject_create_and_add("test_klp_syscall", kernel_kobj);
	if (!klp_kobj)
		return -ENOMEM;

	ret = sysfs_create_file(klp_kobj, &klp_attr.attr);
	if (ret) {
		kobject_put(klp_kobj);
		return ret;
	}

	/*
	 * Save the number pids to transition to livepatched state before the
	 * number of pending pids is decremented.
	 */
	npids = npids_pending;

	return klp_enable_patch(&patch);
}

static void livepatch_exit(void)
{
	kobject_put(klp_kobj);
}

module_init(livepatch_init);
module_exit(livepatch_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");
MODULE_AUTHOR("Libor Pechacek <lpechacek@suse.cz>");
MODULE_AUTHOR("Nicolai Stange <nstange@suse.de>");
MODULE_AUTHOR("Marcos Paulo de Souza <mpdesouza@suse.com>");
MODULE_DESCRIPTION("Livepatch test: syscall transition");