File: livepatch-patch-hook.c

package info (click to toggle)
kpatch 0.9.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,716 kB
  • sloc: ansic: 9,716; sh: 2,592; makefile: 260; asm: 35
file content (606 lines) | stat: -rw-r--r-- 17,054 bytes parent folder | download
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/*
 * Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
 * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
 * 02110-1301, USA.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <generated/utsrelease.h>

#include <linux/livepatch.h>

#include "kpatch-patch.h"

#ifndef UTS_UBUNTU_RELEASE_ABI
#define UTS_UBUNTU_RELEASE_ABI 0
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) ||			\
    defined(RHEL_RELEASE_CODE)
#define HAVE_ELF_RELOCS
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) ||			\
    (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) &&			\
      UTS_UBUNTU_RELEASE_ABI >= 7) ||					\
    defined(RHEL_RELEASE_CODE)
#define HAVE_SYMPOS
#endif

#ifdef RHEL_RELEASE_CODE
# if RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7, 5)
#  define HAVE_IMMEDIATE
# endif
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) &&		\
       LINUX_VERSION_CODE <= KERNEL_VERSION(4, 15, 0))
# define HAVE_IMMEDIATE
#endif

#ifdef RHEL_RELEASE_CODE
# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 5)
#  define HAVE_CALLBACKS
# endif
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
# define HAVE_CALLBACKS
#endif

#ifdef RHEL_RELEASE_CODE
# if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 8) && 		\
	 RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)) || 		\
      RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2)
#  define HAVE_SIMPLE_ENABLE
# endif
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
# define HAVE_SIMPLE_ENABLE
#endif

#ifdef RHEL_RELEASE_CODE
# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2)
#  define HAVE_KLP_REPLACE
# endif
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
# define HAVE_KLP_REPLACE
#endif

#ifndef KLP_REPLACE_ENABLE
#define KLP_REPLACE_ENABLE true
#endif

/*
 * There are quite a few similar structures at play in this file:
 * - livepatch.h structs prefixed with klp_*
 * - kpatch-patch.h structs prefixed with kpatch_patch_*
 * - local scaffolding structs prefixed with patch_*
 *
 * The naming of the struct variables follows this convention:
 * - livepatch struct being with "l" (e.g. lfunc)
 * - kpatch_patch structs being with "k" (e.g. kfunc)
 * - local scaffolding structs have no prefix (e.g. func)
 *
 *  The program reads in kpatch_patch structures, arranges them into the
 *  scaffold structures, then creates a livepatch structure suitable for
 *  registration with the livepatch kernel API.  The scaffold structs only
 *  exist to allow the construction of the klp_patch struct.  Once that is
 *  done, the scaffold structs are no longer needed.
 */

/*
 * lpatch is the kernel data structure that will be created on patch
 * init, registered with the livepatch API on init, and finally
 * unregistered when the patch exits.  Its struct klp_object *objs
 * member must be dynamically allocated according to the number of
 * target objects it will be patching.
 */
static struct klp_patch *lpatch;

static LIST_HEAD(patch_objects);
static int patch_objects_nr;

/**
 * struct patch_object - scaffolding structure tracking patch target objects
 * @list:	list of patch_object (threaded onto patch_objects)
 * @funcs:	list of patch_func associated with this object
 * @relocs:	list of patch_reloc associated with this object
 * @callbacks:	kernel struct of object callbacks
 * @name:	patch target object name (NULL for vmlinux)
 * @funcs_nr:	count of kpatch_patch_func added to @funcs
 * @relocs_nr:	count of patch_reloc added to @relocs
 */
struct patch_object {
	struct list_head list;
	struct list_head funcs;
	struct list_head relocs;
#ifdef HAVE_CALLBACKS
	struct klp_callbacks callbacks;
#endif
	const char *name;
	int funcs_nr, relocs_nr;
};

/**
 * struct patch_func - scaffolding structure for kpatch_patch_func
 * @list:	list of patch_func (threaded onto patch_object.funcs)
 * @kfunc:	array of kpatch_patch_func
 */
struct patch_func {
	struct list_head list;
	struct kpatch_patch_func *kfunc;
};

/**
 * struct patch_reloc - scaffolding structure for kpatch_patch_dynrela
 * @list:	list of patch_reloc (threaded onto patch_object.relocs)
 * @kdynrela:	array of kpatch_patch_dynrela
 */
struct patch_reloc {
	struct list_head list;
	struct kpatch_patch_dynrela *kdynrela;
};

/**
 * patch_alloc_new_object() - creates and initializes a new patch_object
 * @name:	target object name
 *
 * Return: pointer to new patch_object, NULL on failure.
 *
 * Does not check for previously created patch_objects with the same
 * name.  Updates patch_objects_nr and threads new data structure onto
 * the patch_objects list.
 */
static struct patch_object *patch_alloc_new_object(const char *name)
{
	struct patch_object *object;

	object = kzalloc(sizeof(*object), GFP_KERNEL);
	if (!object)
		return NULL;
	INIT_LIST_HEAD(&object->funcs);
#ifndef HAVE_ELF_RELOCS
	INIT_LIST_HEAD(&object->relocs);
#endif
	if (strcmp(name, "vmlinux"))
		object->name = name;
	list_add(&object->list, &patch_objects);
	patch_objects_nr++;
	return object;
}

/**
 * patch_find_object_by_name() - find or create a patch_object with a
 * 				  given name
 * @name:	target object name
 *
 * Return: pointer to patch_object, NULL on failure.
 *
 * Searches the patch_objects list for an already created instance with
 * @name, otherwise tries to create it via patch_alloc_new_object()
 */
static struct patch_object *patch_find_object_by_name(const char *name)
{
	struct patch_object *object;

	list_for_each_entry(object, &patch_objects, list)
		if ((!strcmp(name, "vmlinux") && !object->name) ||
		    (object->name && !strcmp(object->name, name)))
			return object;
	return patch_alloc_new_object(name);
}

/**
 * patch_add_func_to_object() - create scaffolding from kpatch_patch_func data
 *
 * @kfunc:	Individual kpatch_patch_func pointer
 *
 * Return: 0 on success, -ENOMEM on failure.
 *
 * Builds scaffolding data structures from .kpatch.funcs section's array
 * of kpatch_patch_func structures.  Updates the associated
 * patch_object's funcs_nr count.
 */
static int patch_add_func_to_object(struct kpatch_patch_func *kfunc)
{
	struct patch_func *func;
	struct patch_object *object;

	func = kzalloc(sizeof(*func), GFP_KERNEL);
	if (!func)
		return -ENOMEM;
	INIT_LIST_HEAD(&func->list);
	func->kfunc = kfunc;

	object = patch_find_object_by_name(kfunc->objname);
	if (!object) {
		kfree(func);
		return -ENOMEM;
	}
	list_add(&func->list, &object->funcs);
	object->funcs_nr++;
	return 0;
}

#ifndef HAVE_ELF_RELOCS
/**
 * patch_add_reloc_to_object() - create scaffolding from kpatch_patch_dynrela data
 *
 * @kdynrela:	Individual kpatch_patch_dynrela pointer
 *
 * Return: 0 on success, -ENOMEM on failure.
 *
 * Builds scaffolding data structures from .kpatch.dynrelas section's array
 * of kpatch_patch_dynrela structures.  Updates the associated
 * patch_object's relocs_nr count.
 */
static int patch_add_reloc_to_object(struct kpatch_patch_dynrela *kdynrela)
{
	struct patch_reloc *reloc;
	struct patch_object *object;

	reloc = kzalloc(sizeof(*reloc), GFP_KERNEL);
	if (!reloc)
		return -ENOMEM;
	INIT_LIST_HEAD(&reloc->list);
	reloc->kdynrela = kdynrela;

	object = patch_find_object_by_name(kdynrela->objname);
	if (!object) {
		kfree(reloc);
		return -ENOMEM;
	}
	list_add(&reloc->list, &object->relocs);
	object->relocs_nr++;
	return 0;
}
#endif

/**
 * patch_free_scaffold() - tear down the temporary kpatch scaffolding
 */
static void patch_free_scaffold(void) {
	struct patch_func *func, *safefunc;
	struct patch_object *object, *safeobject;
#ifndef HAVE_ELF_RELOCS
	struct patch_reloc *reloc, *safereloc;
#endif

	list_for_each_entry_safe(object, safeobject, &patch_objects, list) {
		list_for_each_entry_safe(func, safefunc,
		                         &object->funcs, list) {
			list_del(&func->list);
			kfree(func);
		}
#ifndef HAVE_ELF_RELOCS
		list_for_each_entry_safe(reloc, safereloc,
		                         &object->relocs, list) {
			list_del(&reloc->list);
			kfree(reloc);
		}
#endif
		list_del(&object->list);
		kfree(object);
	}
}

/**
 * patch_free_livepatch() - release the klp_patch and friends
 */
static void patch_free_livepatch(struct klp_patch *patch)
{
	struct klp_object *object;

	if (patch) {
		for (object = patch->objs; object && object->funcs; object++) {
			if (object->funcs)
				kfree(object->funcs);
#ifndef HAVE_ELF_RELOCS
			if (object->relocs)
				kfree(object->relocs);
#endif
		}
		if (patch->objs)
			kfree(patch->objs);
		kfree(patch);
	}
}

/* Defined by kpatch.lds.S */
extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch_callbacks_pre_patch_end[];
extern struct kpatch_post_patch_callback __kpatch_callbacks_post_patch[], __kpatch_callbacks_post_patch_end[];
extern struct kpatch_pre_unpatch_callback __kpatch_callbacks_pre_unpatch[], __kpatch_callbacks_pre_unpatch_end[];
extern struct kpatch_post_unpatch_callback __kpatch_callbacks_post_unpatch[], __kpatch_callbacks_post_unpatch_end[];

#ifdef HAVE_CALLBACKS
/**
 * add_callbacks_to_patch_objects() - create patch_objects that have callbacks
 *
 * Return: 0 on success, -ENOMEM or -EINVAL on failure
 *
 * Iterates through all kpatch pre/post-(un)patch callback data
 * structures and creates scaffolding patch_objects for them.
 */
static int add_callbacks_to_patch_objects(void)
{
	struct kpatch_pre_patch_callback *p_pre_patch_callback;
	struct kpatch_post_patch_callback *p_post_patch_callback;
	struct kpatch_pre_unpatch_callback *p_pre_unpatch_callback;
	struct kpatch_post_unpatch_callback *p_post_unpatch_callback;
	struct patch_object *object;

	for (p_pre_patch_callback = __kpatch_callbacks_pre_patch;
	     p_pre_patch_callback < __kpatch_callbacks_pre_patch_end;
	     p_pre_patch_callback++) {
		object = patch_find_object_by_name(p_pre_patch_callback->objname);
		if (!object)
			return -ENOMEM;
		if (object->callbacks.pre_patch) {
			pr_err("extra pre-patch callback for object: %s\n",
				object->name ? object->name : "vmlinux");
			return -EINVAL;
		}
		object->callbacks.pre_patch = (int (*)(struct klp_object *))
					       p_pre_patch_callback->callback;
	}

	for (p_post_patch_callback = __kpatch_callbacks_post_patch;
	     p_post_patch_callback < __kpatch_callbacks_post_patch_end;
	     p_post_patch_callback++) {
		object = patch_find_object_by_name(p_post_patch_callback->objname);
		if (!object)
			return -ENOMEM;
		if (object->callbacks.post_patch) {
			pr_err("extra post-patch callback for object: %s\n",
				object->name ? object->name : "vmlinux");
			return -EINVAL;
		}
		object->callbacks.post_patch = (void (*)(struct klp_object *))
						p_post_patch_callback->callback;
	}

	for (p_pre_unpatch_callback = __kpatch_callbacks_pre_unpatch;
	     p_pre_unpatch_callback < __kpatch_callbacks_pre_unpatch_end;
	     p_pre_unpatch_callback++) {
		object = patch_find_object_by_name(p_pre_unpatch_callback->objname);
		if (!object)
			return -ENOMEM;
		if (object->callbacks.pre_unpatch) {
			pr_err("extra pre-unpatch callback for object: %s\n",
				object->name ? object->name : "vmlinux");
			return -EINVAL;
		}
		object->callbacks.pre_unpatch = (void (*)(struct klp_object *))
						p_pre_unpatch_callback->callback;
	}

	for (p_post_unpatch_callback = __kpatch_callbacks_post_unpatch;
	     p_post_unpatch_callback < __kpatch_callbacks_post_unpatch_end;
	     p_post_unpatch_callback++) {
		object = patch_find_object_by_name(p_post_unpatch_callback->objname);
		if (!object)
			return -ENOMEM;
		if (object->callbacks.post_unpatch) {
			pr_err("extra post-unpatch callback for object: %s\n",
				object->name ? object->name : "vmlinux");
			return -EINVAL;
		}
		object->callbacks.post_unpatch = (void (*)(struct klp_object *))
						p_post_unpatch_callback->callback;
	}

	return 0;
}
#else /* HAVE_CALLBACKS */
static inline int add_callbacks_to_patch_objects(void)
{
	if (__kpatch_callbacks_pre_patch !=
	    __kpatch_callbacks_pre_patch_end ||
	    __kpatch_callbacks_post_patch !=
	    __kpatch_callbacks_post_patch_end ||
	    __kpatch_callbacks_pre_unpatch !=
	    __kpatch_callbacks_pre_unpatch_end ||
	    __kpatch_callbacks_post_unpatch !=
	    __kpatch_callbacks_post_unpatch_end) {
		pr_err("patch callbacks are not supported\n");
		return -EINVAL;
	}

	return 0;
}
#endif /* HAVE_CALLBACKS */

/* Defined by kpatch.lds.S */
extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[];
#ifndef HAVE_ELF_RELOCS
extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[];
#endif

static int __init patch_init(void)
{
	struct kpatch_patch_func *kfunc;
	struct klp_object *lobjects, *lobject;
	struct klp_func *lfuncs, *lfunc;
	struct patch_object *object;
	struct patch_func *func;
	int ret = 0, i, j;
#ifndef HAVE_ELF_RELOCS
	struct kpatch_patch_dynrela *kdynrela;
	struct patch_reloc *reloc;
	struct klp_reloc *lrelocs, *lreloc;
#endif


	/*
	 * Step 1 - read from output.o, create temporary scaffolding
	 * data-structures
	 */

	/* organize functions and relocs by object in scaffold */
	for (kfunc = __kpatch_funcs;
	     kfunc != __kpatch_funcs_end;
	     kfunc++) {
		ret = patch_add_func_to_object(kfunc);
		if (ret)
			goto out;
	}

#ifndef HAVE_ELF_RELOCS
	for (kdynrela = __kpatch_dynrelas;
	     kdynrela != __kpatch_dynrelas_end;
	     kdynrela++) {
		ret = patch_add_reloc_to_object(kdynrela);
		if (ret)
			goto out;
	}
#endif

	ret = add_callbacks_to_patch_objects();
	if (ret)
		goto out;

	/* past this point, only possible return code is -ENOMEM */
	ret = -ENOMEM;

	/*
	 * Step 2 - create livepatch klp_patch and friends
	 *
	 * There are two dynamically allocated parts:
	 *
	 *   klp_patch
	 *     klp_object objs  [patch_objects_nr]  <= i
	 *       klp_func funcs [object->funcs_nr]  <= j
	 */

	/* allocate and fill livepatch structures */
	lpatch = kzalloc(sizeof(*lpatch), GFP_KERNEL);
	if (!lpatch)
		goto out;

	lobjects = kzalloc(sizeof(*lobjects) * (patch_objects_nr+1),
			   GFP_KERNEL);
	if (!lobjects)
		goto out;
	lpatch->mod = THIS_MODULE;
	lpatch->objs = lobjects;
#ifdef HAVE_KLP_REPLACE
	lpatch->replace = KLP_REPLACE_ENABLE;
#endif
#if defined(__powerpc64__) && defined(HAVE_IMMEDIATE)
	lpatch->immediate = true;
#endif

	i = 0;
	list_for_each_entry(object, &patch_objects, list) {
		lobject = &lobjects[i];
		lobject->name = object->name;
		lfuncs = kzalloc(sizeof(struct klp_func) *
		                 (object->funcs_nr+1), GFP_KERNEL);
		if (!lfuncs)
			goto out;
		lobject->funcs = lfuncs;
		j = 0;
		list_for_each_entry(func, &object->funcs, list) {
			lfunc = &lfuncs[j];
			lfunc->old_name = func->kfunc->name;
			lfunc->new_func = (void *)func->kfunc->new_addr;
#ifdef HAVE_SYMPOS
			lfunc->old_sympos = func->kfunc->sympos;
#else
			lfunc->old_addr = func->kfunc->old_addr;
#endif
			j++;
		}

#ifndef HAVE_ELF_RELOCS
		lrelocs = kzalloc(sizeof(struct klp_reloc) *
				  (object->relocs_nr+1), GFP_KERNEL);
		if (!lrelocs)
			goto out;
		lobject->relocs = lrelocs;
		j = 0;
		list_for_each_entry(reloc, &object->relocs, list) {
			lreloc = &lrelocs[j];
			lreloc->loc = reloc->kdynrela->dest;
#ifdef HAVE_SYMPOS
			lreloc->sympos = reloc->kdynrela->sympos;
#else
			lreloc->val = reloc->kdynrela->src;
#endif /* HAVE_SYMPOS */
			lreloc->type = reloc->kdynrela->type;
			lreloc->name = reloc->kdynrela->name;
			lreloc->addend = reloc->kdynrela->addend;
			lreloc->external = reloc->kdynrela->external;
			j++;
		}
#endif /* HAVE_ELF_RELOCS */

#ifdef HAVE_CALLBACKS
		lobject->callbacks = object->callbacks;
#endif

		i++;
	}

	/*
	 * Step 3 - throw away scaffolding
	 */

	/*
	 * Once the patch structure that the live patching API expects
	 * has been built, we can release the scaffold structure.
	 */
	patch_free_scaffold();

#ifndef HAVE_SIMPLE_ENABLE
	ret = klp_register_patch(lpatch);
	if (ret) {
		patch_free_livepatch(lpatch);
		return ret;
	}
#endif

	ret = klp_enable_patch(lpatch);
	if (ret) {
#ifndef HAVE_SIMPLE_ENABLE
		WARN_ON(klp_unregister_patch(lpatch));
#endif
		patch_free_livepatch(lpatch);
		return ret;
	}

	return 0;
out:
	patch_free_livepatch(lpatch);
	patch_free_scaffold();
	return ret;
}

static void __exit patch_exit(void)
{
#ifndef HAVE_SIMPLE_ENABLE
	WARN_ON(klp_unregister_patch(lpatch));
#endif
	patch_free_livepatch(lpatch);
}

module_init(patch_init);
module_exit(patch_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");