Skip to content

Commit 1e21b5c

Browse files
committed
Merge tag 'livepatching-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching
Pull livepatching updates from Petr Mladek: "Improvements and cleanups of livepatching selftests" * tag 'livepatching-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching: selftests/livepatch: adopt to newer sysctl error format selftests/livepatch: Use "comm" instead of "diff" for dmesg selftests/livepatch: add test delimiter to dmesg selftests/livepatch: refine dmesg 'taints' in dmesg comparison selftests/livepatch: Don't clear dmesg when running tests selftests/livepatch: fix mem leaks in test-klp-shadow-vars selftests/livepatch: more verification in test-klp-shadow-vars selftests/livepatch: rework test-klp-shadow-vars selftests/livepatch: simplify test-klp-callbacks busy target tests
2 parents bfdd5aa + 5e4d468 commit 1e21b5c

File tree

9 files changed

+296
-245
lines changed

9 files changed

+296
-245
lines changed

lib/livepatch/test_klp_callbacks_busy.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,53 @@
55

66
#include <linux/module.h>
77
#include <linux/kernel.h>
8+
#include <linux/sched.h>
89
#include <linux/workqueue.h>
910
#include <linux/delay.h>
1011

11-
static int sleep_secs;
12-
module_param(sleep_secs, int, 0644);
13-
MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
12+
/* load/run-time control from sysfs writer */
13+
static bool block_transition;
14+
module_param(block_transition, bool, 0644);
15+
MODULE_PARM_DESC(block_transition, "block_transition (default=false)");
1416

1517
static void busymod_work_func(struct work_struct *work);
16-
static DECLARE_DELAYED_WORK(work, busymod_work_func);
18+
static DECLARE_WORK(work, busymod_work_func);
1719

1820
static void busymod_work_func(struct work_struct *work)
1921
{
20-
pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
21-
msleep(sleep_secs * 1000);
22+
pr_info("%s enter\n", __func__);
23+
24+
while (READ_ONCE(block_transition)) {
25+
/*
26+
* Busy-wait until the sysfs writer has acknowledged a
27+
* blocked transition and clears the flag.
28+
*/
29+
msleep(20);
30+
}
31+
2232
pr_info("%s exit\n", __func__);
2333
}
2434

2535
static int test_klp_callbacks_busy_init(void)
2636
{
2737
pr_info("%s\n", __func__);
28-
schedule_delayed_work(&work,
29-
msecs_to_jiffies(1000 * 0));
38+
schedule_work(&work);
39+
40+
if (!block_transition) {
41+
/*
42+
* Serialize output: print all messages from the work
43+
* function before returning from init().
44+
*/
45+
flush_work(&work);
46+
}
47+
3048
return 0;
3149
}
3250

3351
static void test_klp_callbacks_busy_exit(void)
3452
{
35-
cancel_delayed_work_sync(&work);
53+
WRITE_ONCE(block_transition, false);
54+
flush_work(&work);
3655
pr_info("%s\n", __func__);
3756
}
3857

lib/livepatch/test_klp_shadow_vars.c

Lines changed: 132 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
109109
static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
110110
{
111111
klp_shadow_free_all(id, dtor);
112-
pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n",
113-
__func__, id, ptr_id(dtor));
112+
pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", __func__, id, ptr_id(dtor));
114113
}
115114

116115

@@ -124,12 +123,16 @@ static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
124123
return -EINVAL;
125124

126125
*sv = *var;
127-
pr_info("%s: PTR%d -> PTR%d\n",
128-
__func__, ptr_id(sv), ptr_id(*var));
126+
pr_info("%s: PTR%d -> PTR%d\n", __func__, ptr_id(sv), ptr_id(*var));
129127

130128
return 0;
131129
}
132130

131+
/*
132+
* With more than one item to free in the list, order is not determined and
133+
* shadow_dtor will not be passed to shadow_free_all() which would make the
134+
* test fail. (see pass 6)
135+
*/
133136
static void shadow_dtor(void *obj, void *shadow_data)
134137
{
135138
int **sv = shadow_data;
@@ -138,132 +141,153 @@ static void shadow_dtor(void *obj, void *shadow_data)
138141
__func__, ptr_id(obj), ptr_id(sv));
139142
}
140143

141-
static int test_klp_shadow_vars_init(void)
142-
{
143-
void *obj = THIS_MODULE;
144-
int id = 0x1234;
145-
gfp_t gfp_flags = GFP_KERNEL;
144+
/* number of objects we simulate that need shadow vars */
145+
#define NUM_OBJS 3
146146

147-
int var1, var2, var3, var4;
148-
int *pv1, *pv2, *pv3, *pv4;
149-
int **sv1, **sv2, **sv3, **sv4;
147+
/* dynamically created obj fields have the following shadow var id values */
148+
#define SV_ID1 0x1234
149+
#define SV_ID2 0x1235
150150

151-
int **sv;
151+
/*
152+
* The main test case adds/removes new fields (shadow var) to each of these
153+
* test structure instances. The last group of fields in the struct represent
154+
* the idea that shadow variables may be added and removed to and from the
155+
* struct during execution.
156+
*/
157+
struct test_object {
158+
/* add anything here below and avoid to define an empty struct */
159+
struct shadow_ptr sp;
152160

153-
pv1 = &var1;
154-
pv2 = &var2;
155-
pv3 = &var3;
156-
pv4 = &var4;
161+
/* these represent shadow vars added and removed with SV_ID{1,2} */
162+
/* char nfield1; */
163+
/* int nfield2; */
164+
};
165+
166+
static int test_klp_shadow_vars_init(void)
167+
{
168+
struct test_object objs[NUM_OBJS];
169+
char nfields1[NUM_OBJS], *pnfields1[NUM_OBJS], **sv1[NUM_OBJS];
170+
char *pndup[NUM_OBJS];
171+
int nfields2[NUM_OBJS], *pnfields2[NUM_OBJS], **sv2[NUM_OBJS];
172+
void **sv;
173+
int ret;
174+
int i;
157175

158176
ptr_id(NULL);
159-
ptr_id(pv1);
160-
ptr_id(pv2);
161-
ptr_id(pv3);
162-
ptr_id(pv4);
163177

164178
/*
165179
* With an empty shadow variable hash table, expect not to find
166180
* any matches.
167181
*/
168-
sv = shadow_get(obj, id);
182+
sv = shadow_get(&objs[0], SV_ID1);
169183
if (!sv)
170184
pr_info(" got expected NULL result\n");
171185

172-
/*
173-
* Allocate a few shadow variables with different <obj> and <id>.
174-
*/
175-
sv1 = shadow_alloc(obj, id, sizeof(pv1), gfp_flags, shadow_ctor, &pv1);
176-
if (!sv1)
177-
return -ENOMEM;
178-
179-
sv2 = shadow_alloc(obj + 1, id, sizeof(pv2), gfp_flags, shadow_ctor, &pv2);
180-
if (!sv2)
181-
return -ENOMEM;
182-
183-
sv3 = shadow_alloc(obj, id + 1, sizeof(pv3), gfp_flags, shadow_ctor, &pv3);
184-
if (!sv3)
185-
return -ENOMEM;
186-
187-
/*
188-
* Verify we can find our new shadow variables and that they point
189-
* to expected data.
190-
*/
191-
sv = shadow_get(obj, id);
192-
if (!sv)
193-
return -EINVAL;
194-
if (sv == sv1 && *sv1 == pv1)
195-
pr_info(" got expected PTR%d -> PTR%d result\n",
196-
ptr_id(sv1), ptr_id(*sv1));
197-
198-
sv = shadow_get(obj + 1, id);
199-
if (!sv)
200-
return -EINVAL;
201-
if (sv == sv2 && *sv2 == pv2)
202-
pr_info(" got expected PTR%d -> PTR%d result\n",
203-
ptr_id(sv2), ptr_id(*sv2));
204-
sv = shadow_get(obj, id + 1);
205-
if (!sv)
206-
return -EINVAL;
207-
if (sv == sv3 && *sv3 == pv3)
208-
pr_info(" got expected PTR%d -> PTR%d result\n",
209-
ptr_id(sv3), ptr_id(*sv3));
210-
211-
/*
212-
* Allocate or get a few more, this time with the same <obj>, <id>.
213-
* The second invocation should return the same shadow var.
214-
*/
215-
sv4 = shadow_get_or_alloc(obj + 2, id, sizeof(pv4), gfp_flags, shadow_ctor, &pv4);
216-
if (!sv4)
217-
return -ENOMEM;
218-
219-
sv = shadow_get_or_alloc(obj + 2, id, sizeof(pv4), gfp_flags, shadow_ctor, &pv4);
220-
if (!sv)
221-
return -EINVAL;
222-
if (sv == sv4 && *sv4 == pv4)
223-
pr_info(" got expected PTR%d -> PTR%d result\n",
224-
ptr_id(sv4), ptr_id(*sv4));
225-
226-
/*
227-
* Free the <obj=*, id> shadow variables and check that we can no
228-
* longer find them.
229-
*/
230-
shadow_free(obj, id, shadow_dtor); /* sv1 */
231-
sv = shadow_get(obj, id);
232-
if (!sv)
233-
pr_info(" got expected NULL result\n");
186+
/* pass 1: init & alloc a char+int pair of svars for each objs */
187+
for (i = 0; i < NUM_OBJS; i++) {
188+
pnfields1[i] = &nfields1[i];
189+
ptr_id(pnfields1[i]);
190+
191+
if (i % 2) {
192+
sv1[i] = shadow_alloc(&objs[i], SV_ID1,
193+
sizeof(pnfields1[i]), GFP_KERNEL,
194+
shadow_ctor, &pnfields1[i]);
195+
} else {
196+
sv1[i] = shadow_get_or_alloc(&objs[i], SV_ID1,
197+
sizeof(pnfields1[i]), GFP_KERNEL,
198+
shadow_ctor, &pnfields1[i]);
199+
}
200+
if (!sv1[i]) {
201+
ret = -ENOMEM;
202+
goto out;
203+
}
204+
205+
pnfields2[i] = &nfields2[i];
206+
ptr_id(pnfields2[i]);
207+
sv2[i] = shadow_alloc(&objs[i], SV_ID2, sizeof(pnfields2[i]),
208+
GFP_KERNEL, shadow_ctor, &pnfields2[i]);
209+
if (!sv2[i]) {
210+
ret = -ENOMEM;
211+
goto out;
212+
}
213+
}
234214

235-
shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
236-
sv = shadow_get(obj + 1, id);
237-
if (!sv)
238-
pr_info(" got expected NULL result\n");
215+
/* pass 2: verify we find allocated svars and where they point to */
216+
for (i = 0; i < NUM_OBJS; i++) {
217+
/* check the "char" svar for all objects */
218+
sv = shadow_get(&objs[i], SV_ID1);
219+
if (!sv) {
220+
ret = -EINVAL;
221+
goto out;
222+
}
223+
if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
224+
pr_info(" got expected PTR%d -> PTR%d result\n",
225+
ptr_id(sv1[i]), ptr_id(*sv1[i]));
226+
227+
/* check the "int" svar for all objects */
228+
sv = shadow_get(&objs[i], SV_ID2);
229+
if (!sv) {
230+
ret = -EINVAL;
231+
goto out;
232+
}
233+
if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
234+
pr_info(" got expected PTR%d -> PTR%d result\n",
235+
ptr_id(sv2[i]), ptr_id(*sv2[i]));
236+
}
239237

240-
shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
241-
sv = shadow_get(obj + 2, id);
242-
if (!sv)
243-
pr_info(" got expected NULL result\n");
238+
/* pass 3: verify that 'get_or_alloc' returns already allocated svars */
239+
for (i = 0; i < NUM_OBJS; i++) {
240+
pndup[i] = &nfields1[i];
241+
ptr_id(pndup[i]);
242+
243+
sv = shadow_get_or_alloc(&objs[i], SV_ID1, sizeof(pndup[i]),
244+
GFP_KERNEL, shadow_ctor, &pndup[i]);
245+
if (!sv) {
246+
ret = -EINVAL;
247+
goto out;
248+
}
249+
if ((char **)sv == sv1[i] && *sv1[i] == pnfields1[i])
250+
pr_info(" got expected PTR%d -> PTR%d result\n",
251+
ptr_id(sv1[i]), ptr_id(*sv1[i]));
252+
}
244253

245-
/*
246-
* We should still find an <id+1> variable.
247-
*/
248-
sv = shadow_get(obj, id + 1);
249-
if (!sv)
250-
return -EINVAL;
251-
if (sv == sv3 && *sv3 == pv3)
252-
pr_info(" got expected PTR%d -> PTR%d result\n",
253-
ptr_id(sv3), ptr_id(*sv3));
254+
/* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */
255+
for (i = 0; i < NUM_OBJS; i++) {
256+
shadow_free(&objs[i], SV_ID1, shadow_dtor); /* 'char' pairs */
257+
sv = shadow_get(&objs[i], SV_ID1);
258+
if (!sv)
259+
pr_info(" got expected NULL result\n");
260+
}
254261

255-
/*
256-
* Free all the <id+1> variables, too.
257-
*/
258-
shadow_free_all(id + 1, shadow_dtor); /* sv3 */
259-
sv = shadow_get(obj, id);
260-
if (!sv)
261-
pr_info(" shadow_get() got expected NULL result\n");
262+
/* pass 5: check we still find <objs[*], SV_ID2> svar pairs */
263+
for (i = 0; i < NUM_OBJS; i++) {
264+
sv = shadow_get(&objs[i], SV_ID2); /* 'int' pairs */
265+
if (!sv) {
266+
ret = -EINVAL;
267+
goto out;
268+
}
269+
if ((int **)sv == sv2[i] && *sv2[i] == pnfields2[i])
270+
pr_info(" got expected PTR%d -> PTR%d result\n",
271+
ptr_id(sv2[i]), ptr_id(*sv2[i]));
272+
}
262273

274+
/* pass 6: free all the <objs[*], SV_ID2> svar pairs too. */
275+
shadow_free_all(SV_ID2, NULL); /* 'int' pairs */
276+
for (i = 0; i < NUM_OBJS; i++) {
277+
sv = shadow_get(&objs[i], SV_ID2);
278+
if (!sv)
279+
pr_info(" got expected NULL result\n");
280+
}
263281

264282
free_ptr_list();
265283

266284
return 0;
285+
out:
286+
shadow_free_all(SV_ID1, NULL); /* 'char' pairs */
287+
shadow_free_all(SV_ID2, NULL); /* 'int' pairs */
288+
free_ptr_list();
289+
290+
return ret;
267291
}
268292

269293
static void test_klp_shadow_vars_exit(void)

tools/testing/selftests/livepatch/README

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ This is a small set of sanity tests for the kernel livepatching.
66

77
The test suite loads and unloads several test kernel modules to verify
88
livepatch behavior. Debug information is logged to the kernel's message
9-
buffer and parsed for expected messages. (Note: the tests will clear
10-
the message buffer between individual tests.)
9+
buffer and parsed for expected messages. (Note: the tests will compare
10+
the message buffer for only the duration of each individual test.)
1111

1212

1313
Config
@@ -35,9 +35,9 @@ Adding tests
3535
------------
3636

3737
See the common functions.sh file for the existing collection of utility
38-
functions, most importantly setup_config() and check_result(). The
39-
latter function greps the kernel's ring buffer for "livepatch:" and
40-
"test_klp" strings, so tests be sure to include one of those strings for
41-
result comparison. Other utility functions include general module
42-
loading and livepatch loading helpers (waiting for patch transitions,
43-
sysfs entries, etc.)
38+
functions, most importantly setup_config(), start_test() and
39+
check_result(). The latter function greps the kernel's ring buffer for
40+
"livepatch:" and "test_klp" strings, so tests be sure to include one of
41+
those strings for result comparison. Other utility functions include
42+
general module loading and livepatch loading helpers (waiting for patch
43+
transitions, sysfs entries, etc.)

0 commit comments

Comments
 (0)