mirror of
https://github.com/dynup/kpatch
synced 2024-12-23 21:52:07 +00:00
b64ab2b5e4
Support patching objects that have duplicated function names. This feature was introduced upstream in Linux v4.5. This patch appends the symbol position to the symbol structure when lookup_local_symbol is called. This pos variable is then used when creating the funcs and dynrelas sections. Finally, incorporate sympos into the livepatch patch hook only if the kernel version is greater than v4.5. In other cases the older format is used. Fixes: #493 Signed-off-by: Chris J Arges <chris.j.arges@canonical.com>
307 lines
7.7 KiB
C
307 lines
7.7 KiB
C
/*
|
|
* 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 <linux/livepatch.h>
|
|
|
|
#include "kpatch-patch.h"
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
struct klp_patch *lpatch;
|
|
|
|
static LIST_HEAD(patch_objects);
|
|
static int patch_objects_nr;
|
|
struct patch_object {
|
|
struct list_head list;
|
|
struct list_head funcs;
|
|
struct list_head relocs;
|
|
const char *name;
|
|
int funcs_nr, relocs_nr;
|
|
};
|
|
|
|
struct patch_func {
|
|
struct list_head list;
|
|
struct kpatch_patch_func *kfunc;
|
|
};
|
|
|
|
struct patch_reloc {
|
|
struct list_head list;
|
|
struct kpatch_patch_dynrela *kdynrela;
|
|
};
|
|
|
|
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);
|
|
INIT_LIST_HEAD(&object->relocs);
|
|
if (strcmp(name, "vmlinux"))
|
|
object->name = name;
|
|
list_add(&object->list, &patch_objects);
|
|
patch_objects_nr++;
|
|
return 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void patch_free_scaffold(void) {
|
|
struct patch_func *func, *safefunc;
|
|
struct patch_reloc *reloc, *safereloc;
|
|
struct patch_object *object, *safeobject;
|
|
|
|
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);
|
|
}
|
|
list_for_each_entry_safe(reloc, safereloc,
|
|
&object->relocs, list) {
|
|
list_del(&reloc->list);
|
|
kfree(reloc);
|
|
}
|
|
list_del(&object->list);
|
|
kfree(object);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (object->relocs)
|
|
kfree(object->relocs);
|
|
}
|
|
if (patch->objs)
|
|
kfree(patch->objs);
|
|
kfree(patch);
|
|
}
|
|
}
|
|
|
|
extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[];
|
|
extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[];
|
|
|
|
static int __init patch_init(void)
|
|
{
|
|
struct kpatch_patch_func *kfunc;
|
|
struct kpatch_patch_dynrela *kdynrela;
|
|
struct klp_object *lobjects, *lobject;
|
|
struct klp_func *lfuncs, *lfunc;
|
|
struct klp_reloc *lrelocs, *lreloc;
|
|
struct patch_object *object;
|
|
struct patch_func *func;
|
|
struct patch_reloc *reloc;
|
|
int ret = 0, i, j;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
for (kdynrela = __kpatch_dynrelas;
|
|
kdynrela != __kpatch_dynrelas_end;
|
|
kdynrela++) {
|
|
ret = patch_add_reloc_to_object(kdynrela);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* past this point, only possible return code is -ENOMEM */
|
|
ret = -ENOMEM;
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
|
lfunc->old_sympos = func->kfunc->sympos;
|
|
#else
|
|
lfunc->old_addr = func->kfunc->old_addr;
|
|
#endif
|
|
j++;
|
|
}
|
|
|
|
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;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
|
lreloc->sympos = reloc->kdynrela->sympos;
|
|
#else
|
|
lreloc->val = reloc->kdynrela->src;
|
|
#endif
|
|
lreloc->type = reloc->kdynrela->type;
|
|
lreloc->name = reloc->kdynrela->name;
|
|
lreloc->addend = reloc->kdynrela->addend;
|
|
lreloc->external = reloc->kdynrela->external;
|
|
j++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/*
|
|
* Once the patch structure that the live patching API expects
|
|
* has been built, we can release the scaffold structure.
|
|
*/
|
|
patch_free_scaffold();
|
|
|
|
ret = klp_register_patch(lpatch);
|
|
if (ret) {
|
|
patch_free_livepatch(lpatch);
|
|
return ret;
|
|
}
|
|
|
|
ret = klp_enable_patch(lpatch);
|
|
if (ret) {
|
|
WARN_ON(klp_unregister_patch(lpatch));
|
|
patch_free_livepatch(lpatch);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
out:
|
|
patch_free_livepatch(lpatch);
|
|
patch_free_scaffold();
|
|
return ret;
|
|
}
|
|
|
|
static void __exit patch_exit(void)
|
|
{
|
|
WARN_ON(klp_unregister_patch(lpatch));
|
|
}
|
|
|
|
module_init(patch_init);
|
|
module_exit(patch_exit);
|
|
MODULE_LICENSE("GPL");
|