Commit 0f0836b7 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching

Pull livepatching updates from Jiri Kosina:

 - RO/NX attribute fixes for patch module relocations from Josh
   Poimboeuf.  As part of this effort, module.c has been cleaned up as
   well and livepatching is piggy-backing on this cleanup.  Rusty is OK
   with this whole lot going through livepatching tree.

 - symbol disambiguation support from Chris J Arges.  That series is
   also
Reviewed-by: default avatarMiroslav Benes <mbenes@suse.cz>

   but this came in only after I've alredy pushed out.  Didn't want to
   rebase because of that, hence I am mentioning it here.

 - symbol lookup fix from Miroslav Benes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching:
  livepatch: Cleanup module page permission changes
  module: keep percpu symbols in module's symtab
  module: clean up RO/NX handling.
  module: use a structure to encapsulate layout.
  gcov: use within_module() helper.
  module: Use the same logic for setting and unsetting RO/NX
  livepatch: function,sympos scheme in livepatch sysfs directory
  livepatch: add sympos as disambiguator field to klp_reloc
  livepatch: add old_sympos as disambiguator field to klp_func
parents c2848f2e b56b36ee
......@@ -33,7 +33,7 @@ Description:
The object directory contains subdirectories for each function
that is patched within the object.
What: /sys/kernel/livepatch/<patch>/<object>/<function>
What: /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
Date: Nov 2014
KernelVersion: 3.19.0
Contact: live-patching@vger.kernel.org
......@@ -41,4 +41,8 @@ Description:
The function directory contains attributes regarding the
properties and state of the patched function.
The directory name contains the patched function name and a
sympos number corresponding to the nth occurrence of the symbol
name in kallsyms for the patched object.
There are currently no such attributes.
......@@ -160,7 +160,7 @@ apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab,
/* The small sections were sorted to the end of the segment.
The following should definitely cover them. */
gp = (u64)me->module_core + me->core_size - 0x8000;
gp = (u64)me->core_layout.base + me->core_layout.size - 0x8000;
got = sechdrs[me->arch.gotsecindex].sh_addr;
for (i = 0; i < n; i++) {
......
......@@ -385,8 +385,8 @@ void *unwind_add_table(struct module *module, const void *table_start,
return NULL;
init_unwind_table(table, module->name,
module->module_core, module->core_size,
module->module_init, module->init_size,
module->core_layout.base, module->core_layout.size,
module->init_layout.base, module->init_layout.size,
table_start, table_size,
NULL, 0);
......
......@@ -32,7 +32,7 @@ struct plt_entries {
static bool in_init(const struct module *mod, u32 addr)
{
return addr - (u32)mod->module_init < mod->init_size;
return addr - (u32)mod->init_layout.base < mod->init_layout.size;
}
u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
......
......@@ -118,9 +118,9 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
* Increase core size to make room for GOT and set start
* offset for GOT.
*/
module->core_size = ALIGN(module->core_size, 4);
module->arch.got_offset = module->core_size;
module->core_size += module->arch.got_size;
module->core_layout.size = ALIGN(module->core_layout.size, 4);
module->arch.got_offset = module->core_layout.size;
module->core_layout.size += module->arch.got_size;
return 0;
......@@ -177,7 +177,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
if (!info->got_initialized) {
Elf32_Addr *gotent;
gotent = (module->module_core
gotent = (module->core_layout.base
+ module->arch.got_offset
+ info->got_offset);
*gotent = relocation;
......@@ -255,8 +255,8 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
*/
pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
relocation, module->arch.got_offset,
module->module_core);
relocation -= ((unsigned long)module->module_core
module->core_layout.base);
relocation -= ((unsigned long)module->core_layout.base
+ module->arch.got_offset);
*location = relocation;
break;
......
......@@ -486,13 +486,13 @@ module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
static inline int
in_init (const struct module *mod, uint64_t addr)
{
return addr - (uint64_t) mod->module_init < mod->init_size;
return addr - (uint64_t) mod->init_layout.base < mod->init_layout.size;
}
static inline int
in_core (const struct module *mod, uint64_t addr)
{
return addr - (uint64_t) mod->module_core < mod->core_size;
return addr - (uint64_t) mod->core_layout.base < mod->core_layout.size;
}
static inline int
......@@ -675,7 +675,7 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
break;
case RV_BDREL:
val -= (uint64_t) (in_init(mod, val) ? mod->module_init : mod->module_core);
val -= (uint64_t) (in_init(mod, val) ? mod->init_layout.base : mod->core_layout.base);
break;
case RV_LTV:
......@@ -810,15 +810,15 @@ apply_relocate_add (Elf64_Shdr *sechdrs, const char *strtab, unsigned int symind
* addresses have been selected...
*/
uint64_t gp;
if (mod->core_size > MAX_LTOFF)
if (mod->core_layout.size > MAX_LTOFF)
/*
* This takes advantage of fact that SHF_ARCH_SMALL gets allocated
* at the end of the module.
*/
gp = mod->core_size - MAX_LTOFF / 2;
gp = mod->core_layout.size - MAX_LTOFF / 2;
else
gp = mod->core_size / 2;
gp = (uint64_t) mod->module_core + ((gp + 7) & -8);
gp = mod->core_layout.size / 2;
gp = (uint64_t) mod->core_layout.base + ((gp + 7) & -8);
mod->arch.gp = gp;
DEBUGP("%s: placing gp at 0x%lx\n", __func__, gp);
}
......
......@@ -176,8 +176,8 @@ static uint32_t do_plt_call(void *location, Elf32_Addr val,
tramp[1] = 0xac000001 | ((val & 0x0000ffff) << 3);
/* Init, or core PLT? */
if (location >= mod->module_core
&& location < mod->module_core + mod->core_size)
if (location >= mod->core_layout.base
&& location < mod->core_layout.base + mod->core_layout.size)
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
......
......@@ -205,11 +205,11 @@ static void layout_sections(struct module *mod, const Elf_Ehdr *hdr,
|| s->sh_entsize != ~0UL)
continue;
s->sh_entsize =
get_offset((unsigned long *)&mod->core_size, s);
get_offset((unsigned long *)&mod->core_layout.size, s);
}
if (m == 0)
mod->core_text_size = mod->core_size;
mod->core_layout.text_size = mod->core_layout.size;
}
}
......@@ -641,7 +641,7 @@ static int vpe_elfload(struct vpe *v)
layout_sections(&mod, hdr, sechdrs, secstrings);
}
v->load_addr = alloc_progmem(mod.core_size);
v->load_addr = alloc_progmem(mod.core_layout.size);
if (!v->load_addr)
return -ENOMEM;
......
......@@ -42,9 +42,9 @@
* We are not doing SEGREL32 handling correctly. According to the ABI, we
* should do a value offset, like this:
* if (in_init(me, (void *)val))
* val -= (uint32_t)me->module_init;
* val -= (uint32_t)me->init_layout.base;
* else
* val -= (uint32_t)me->module_core;
* val -= (uint32_t)me->core_layout.base;
* However, SEGREL32 is used only for PARISC unwind entries, and we want
* those entries to have an absolute address, and not just an offset.
*
......@@ -100,14 +100,14 @@
* or init pieces the location is */
static inline int in_init(struct module *me, void *loc)
{
return (loc >= me->module_init &&
loc <= (me->module_init + me->init_size));
return (loc >= me->init_layout.base &&
loc <= (me->init_layout.base + me->init_layout.size));
}
static inline int in_core(struct module *me, void *loc)
{
return (loc >= me->module_core &&
loc <= (me->module_core + me->core_size));
return (loc >= me->core_layout.base &&
loc <= (me->core_layout.base + me->core_layout.size));
}
static inline int in_local(struct module *me, void *loc)
......@@ -367,13 +367,13 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
}
/* align things a bit */
me->core_size = ALIGN(me->core_size, 16);
me->arch.got_offset = me->core_size;
me->core_size += gots * sizeof(struct got_entry);
me->core_layout.size = ALIGN(me->core_layout.size, 16);
me->arch.got_offset = me->core_layout.size;
me->core_layout.size += gots * sizeof(struct got_entry);
me->core_size = ALIGN(me->core_size, 16);
me->arch.fdesc_offset = me->core_size;
me->core_size += fdescs * sizeof(Elf_Fdesc);
me->core_layout.size = ALIGN(me->core_layout.size, 16);
me->arch.fdesc_offset = me->core_layout.size;
me->core_layout.size += fdescs * sizeof(Elf_Fdesc);
me->arch.got_max = gots;
me->arch.fdesc_max = fdescs;
......@@ -391,7 +391,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
BUG_ON(value == 0);
got = me->module_core + me->arch.got_offset;
got = me->core_layout.base + me->arch.got_offset;
for (i = 0; got[i].addr; i++)
if (got[i].addr == value)
goto out;
......@@ -409,7 +409,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
#ifdef CONFIG_64BIT
static Elf_Addr get_fdesc(struct module *me, unsigned long value)
{
Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset;
Elf_Fdesc *fdesc = me->core_layout.base + me->arch.fdesc_offset;
if (!value) {
printk(KERN_ERR "%s: zero OPD requested!\n", me->name);
......@@ -427,7 +427,7 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
/* Create new one */
fdesc->addr = value;
fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset;
fdesc->gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
return (Elf_Addr)fdesc;
}
#endif /* CONFIG_64BIT */
......@@ -839,7 +839,7 @@ register_unwind_table(struct module *me,
table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr;
end = table + sechdrs[me->arch.unwind_section].sh_size;
gp = (Elf_Addr)me->module_core + me->arch.got_offset;
gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n",
me->arch.unwind_section, table, end, gp);
......
......@@ -188,8 +188,8 @@ static uint32_t do_plt_call(void *location,
pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
/* Init, or core PLT? */
if (location >= mod->module_core
&& location < mod->module_core + mod->core_size)
if (location >= mod->core_layout.base
&& location < mod->core_layout.base + mod->core_layout.size)
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
......@@ -296,7 +296,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
}
#ifdef CONFIG_DYNAMIC_FTRACE
module->arch.tramp =
do_plt_call(module->module_core,
do_plt_call(module->core_layout.base,
(unsigned long)ftrace_caller,
sechdrs, module);
#endif
......
......@@ -159,11 +159,11 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
/* Increase core size by size of got & plt and set start
offsets for got and plt. */
me->core_size = ALIGN(me->core_size, 4);
me->arch.got_offset = me->core_size;
me->core_size += me->arch.got_size;
me->arch.plt_offset = me->core_size;
me->core_size += me->arch.plt_size;
me->core_layout.size = ALIGN(me->core_layout.size, 4);
me->arch.got_offset = me->core_layout.size;
me->core_layout.size += me->arch.got_size;
me->arch.plt_offset = me->core_layout.size;
me->core_layout.size += me->arch.plt_size;
return 0;
}
......@@ -279,7 +279,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
if (info->got_initialized == 0) {
Elf_Addr *gotent;
gotent = me->module_core + me->arch.got_offset +
gotent = me->core_layout.base + me->arch.got_offset +
info->got_offset;
*gotent = val;
info->got_initialized = 1;
......@@ -302,7 +302,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
rc = apply_rela_bits(loc, val, 0, 64, 0);
else if (r_type == R_390_GOTENT ||
r_type == R_390_GOTPLTENT) {
val += (Elf_Addr) me->module_core - loc;
val += (Elf_Addr) me->core_layout.base - loc;
rc = apply_rela_bits(loc, val, 1, 32, 1);
}
break;
......@@ -315,7 +315,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */
if (info->plt_initialized == 0) {
unsigned int *ip;
ip = me->module_core + me->arch.plt_offset +
ip = me->core_layout.base + me->arch.plt_offset +
info->plt_offset;
ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
ip[1] = 0x100a0004;
......@@ -334,7 +334,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val - loc + 0xffffUL < 0x1ffffeUL) ||
(r_type == R_390_PLT32DBL &&
val - loc + 0xffffffffULL < 0x1fffffffeULL)))
val = (Elf_Addr) me->module_core +
val = (Elf_Addr) me->core_layout.base +
me->arch.plt_offset +
info->plt_offset;
val += rela->r_addend - loc;
......@@ -356,7 +356,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_GOTOFF32: /* 32 bit offset to GOT. */
case R_390_GOTOFF64: /* 64 bit offset to GOT. */
val = val + rela->r_addend -
((Elf_Addr) me->module_core + me->arch.got_offset);
((Elf_Addr) me->core_layout.base + me->arch.got_offset);
if (r_type == R_390_GOTOFF16)
rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOTOFF32)
......@@ -366,7 +366,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
val = (Elf_Addr) me->module_core + me->arch.got_offset +
val = (Elf_Addr) me->core_layout.base + me->arch.got_offset +
rela->r_addend - loc;
if (r_type == R_390_GOTPC)
rc = apply_rela_bits(loc, val, 1, 32, 0);
......
......@@ -20,8 +20,6 @@
#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/page_types.h>
#include <asm/elf.h>
#include <asm/livepatch.h>
......@@ -38,11 +36,10 @@
int klp_write_module_reloc(struct module *mod, unsigned long type,
unsigned long loc, unsigned long value)
{
int ret, numpages, size = 4;
bool readonly;
size_t size = 4;
unsigned long val;
unsigned long core = (unsigned long)mod->module_core;
unsigned long core_size = mod->core_size;
unsigned long core = (unsigned long)mod->core_layout.base;
unsigned long core_size = mod->core_layout.size;
switch (type) {
case R_X86_64_NONE:
......@@ -69,23 +66,5 @@ int klp_write_module_reloc(struct module *mod, unsigned long type,
/* loc does not point to any symbol inside the module */
return -EINVAL;
readonly = false;
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
if (loc < core + mod->core_ro_size)
readonly = true;
#endif
/* determine if the relocation spans a page boundary */
numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2;
if (readonly)
set_memory_rw(loc & PAGE_MASK, numpages);
ret = probe_kernel_write((void *)loc, &val, size);
if (readonly)
set_memory_ro(loc & PAGE_MASK, numpages);
return ret;
return probe_kernel_write((void *)loc, &val, size);
}
......@@ -37,8 +37,9 @@ enum klp_state {
* struct klp_func - function structure for live patching
* @old_name: name of the function to be patched
* @new_func: pointer to the patched function code
* @old_addr: a hint conveying at what address the old function
* can be found (optional, vmlinux patches only)
* @old_sympos: a hint indicating which symbol position the old function
* can be found (optional)
* @old_addr: the address of the function being patched
* @kobj: kobject for sysfs resources
* @state: tracks function-level patch application state
* @stack_node: list node for klp_ops func_stack list
......@@ -48,16 +49,16 @@ struct klp_func {
const char *old_name;
void *new_func;
/*
* The old_addr field is optional and can be used to resolve
* duplicate symbol names in the vmlinux object. If this
* information is not present, the symbol is located by name
* with kallsyms. If the name is not unique and old_addr is
* not provided, the patch application fails as there is no
* way to resolve the ambiguity.
* The old_sympos field is optional and can be used to resolve
* duplicate symbol names in livepatch objects. If this field is zero,
* it is expected the symbol is unique, otherwise patching fails. If
* this value is greater than zero then that occurrence of the symbol
* in kallsyms for the given object is used.
*/
unsigned long old_addr;
unsigned long old_sympos;
/* internal */
unsigned long old_addr;
struct kobject kobj;
enum klp_state state;
struct list_head stack_node;
......@@ -66,8 +67,7 @@ struct klp_func {
/**
* struct klp_reloc - relocation structure for live patching
* @loc: address where the relocation will be written
* @val: address of the referenced symbol (optional,
* vmlinux patches only)
* @sympos: position in kallsyms to disambiguate symbols (optional)
* @type: ELF relocation type
* @name: name of the referenced symbol (for lookup/verification)
* @addend: offset from the referenced symbol
......@@ -75,7 +75,7 @@ struct klp_func {
*/
struct klp_reloc {
unsigned long loc;
unsigned long val;
unsigned long sympos;
unsigned long type;
const char *name;
int addend;
......
......@@ -302,6 +302,28 @@ struct mod_tree_node {
struct latch_tree_node node;
};
struct module_layout {
/* The actual code + data. */
void *base;
/* Total size. */
unsigned int size;
/* The size of the executable code. */
unsigned int text_size;
/* Size of RO section of the module (text+rodata) */
unsigned int ro_size;
#ifdef CONFIG_MODULES_TREE_LOOKUP
struct mod_tree_node mtn;
#endif
};
#ifdef CONFIG_MODULES_TREE_LOOKUP
/* Only touch one cacheline for common rbtree-for-core-layout case. */
#define __module_layout_align ____cacheline_aligned
#else
#define __module_layout_align
#endif
struct module {
enum module_state state;
......@@ -366,37 +388,9 @@ struct module {
/* Startup function. */
int (*init)(void);
/*
* If this is non-NULL, vfree() after init() returns.
*
* Cacheline align here, such that:
* module_init, module_core, init_size, core_size,
* init_text_size, core_text_size and mtn_core::{mod,node[0]}
* are on the same cacheline.
*/
void *module_init ____cacheline_aligned;
/* Here is the actual code + data, vfree'd on unload. */
void *module_core;
/* Here are the sizes of the init and core sections */
unsigned int init_size, core_size;
/* The size of the executable code in each section. */
unsigned int init_text_size, core_text_size;
#ifdef CONFIG_MODULES_TREE_LOOKUP
/*
* We want mtn_core::{mod,node[0]} to be in the same cacheline as the
* above entries such that a regular lookup will only touch one
* cacheline.
*/
struct mod_tree_node mtn_core;
struct mod_tree_node mtn_init;
#endif
/* Size of RO sections of the module (text+rodata) */
unsigned int init_ro_size, core_ro_size;
/* Core layout: rbtree is accessed frequently, so keep together. */
struct module_layout core_layout __module_layout_align;
struct module_layout init_layout;
/* Arch-specific module values */
struct mod_arch_specific arch;
......@@ -505,15 +499,15 @@ bool is_module_text_address(unsigned long addr);
static inline bool within_module_core(unsigned long addr,
const struct module *mod)
{
return (unsigned long)mod->module_core <= addr &&
addr < (unsigned long)mod->module_core + mod->core_size;
return (unsigned long)mod->core_layout.base <= addr &&
addr < (unsigned long)mod->core_layout.base + mod->core_layout.size;
}
static inline bool within_module_init(unsigned long addr,
const struct module *mod)
{
return (unsigned long)mod->module_init <= addr &&
addr < (unsigned long)mod->module_init + mod->init_size;
return (unsigned long)mod->init_layout.base <= addr &&
addr < (unsigned long)mod->init_layout.base + mod->init_layout.size;
}
static inline bool within_module(unsigned long addr, const struct module *mod)
......@@ -768,9 +762,13 @@ extern int module_sysfs_initialized;
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
extern void set_all_modules_text_rw(void);
extern void set_all_modules_text_ro(void);
extern void module_enable_ro(const struct module *mod);
extern void module_disable_ro(const struct module *mod);
#else
static inline void set_all_modules_text_rw(void) { }
static inline void set_all_modules_text_ro(void) { }
static inline void module_enable_ro(const struct module *mod) { }
static inline void module_disable_ro(const struct module *mod) { }
#endif
#ifdef CONFIG_GENERIC_BUG
......
......@@ -2021,7 +2021,7 @@ static int kdb_lsmod(int argc, const char **argv)
continue;
kdb_printf("%-20s%8u 0x%p ", mod->name,
mod->core_size, (void *)mod);
mod->core_layout.size, (void *)mod);
#ifdef CONFIG_MODULE_UNLOAD
kdb_printf("%4d ", module_refcount(mod));
#endif
......@@ -2031,7 +2031,7 @@ static int kdb_lsmod(int argc, const char **argv)
kdb_printf(" (Loading)");
else
kdb_printf(" (Live)");
kdb_printf(" 0x%p", mod->module_core);
kdb_printf(" 0x%p", mod->core_layout.base);
#ifdef CONFIG_MODULE_UNLOAD
{
......
......@@ -123,11 +123,6 @@ void gcov_enable_events(void)
}
#ifdef CONFIG_MODULES
static inline int within(void *addr, void *start, unsigned long size)
{
return ((addr >= start) && (addr < start + size));
}
/* Update list and generate events when modules are unloaded. */
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
void *data)
......@@ -142,7 +137,7 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
/* Remove entries located in module from linked list. */
while ((info = gcov_info_next(info))) {
if (within(info, mod->module_core, mod->core_size)) {
if (within_module((unsigned long)info, mod)) {
gcov_info_unlink(prev, info);
if (gcov_events_enabled)
gcov_event(GCOV_REMOVE, info);
......
......@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/kallsyms.h>
#include <linux/livepatch.h>
#include <asm/cacheflush.h>
/**
* struct klp_ops - structure for tracking registered ftrace ops structs
......@@ -135,13 +136,8 @@ struct klp_find_arg {
const char *objname;
const char *name;
unsigned long addr;
/*
* If count == 0, the symbol was not found. If count == 1, a unique
* match was found and addr is set. If count > 1, there is
* unresolvable ambiguity among "count" number of symbols with the same
* name in the same object.
*/
unsigned long count;
unsigned long pos;
};
static int klp_find_callback(void *data, const char *name,
......@@ -158,37 +154,48 @@ static int klp_find_callback(void *data, const char *name,
if (args->objname && strcmp(args->objname, mod->name))
return 0;
/*
* args->addr might be overwritten if another match is found
* but klp_find_object_symbol() handles this and only returns the
* addr if count == 1.
*/
args->addr = addr;
args->count++;
/*
* Finish the search when the symbol is found for the desired position
* or the position is not defined for a non-unique symbol.
*/
if ((args->pos && (args->count == args->pos)) ||
(!args->pos && (args->count > 1)))
return 1;
return 0;
}
static int klp_find_object_symbol(const char *objname, const char *name,
unsigned long *addr)
unsigned long sympos, unsigned long *addr)
{
struct klp_find_arg args = {
.objname = objname,
.name = name,
.addr = 0,
.count = 0
.count = 0,
.pos = sympos,
};
mutex_lock(&module_mutex);
kallsyms_on_each_symbol(klp_find_callback, &args);
mutex_unlock(&module_mutex);
if (args.count == 0)
/*
* Ensure an address was found. If sympos is 0, ensure symbol is unique;
* otherwise ensure the symbol position count matches sympos.
*/
if (args.addr == 0)
pr_err("symbol '%s' not found in symbol table\n", name);
else if (args.count > 1)
else if (args.count > 1 && sympos == 0) {
pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
args.count, name, objname);
else {
} else if (sympos != args.count && sympos > 0) {
pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
sympos, name, objname ? objname : "vmlinux");
} else {
*addr = args.addr;
return 0;
}
......@@ -197,66 +204,6 @@ static int klp_find_object_symbol(const char *objname, const char *name,
return -EINVAL;
}
struct klp_verify_args {
const char *name;
const unsigned long addr;
};
static int klp_verify_callback(void *data, const char *name,
struct module *mod, unsigned long addr)
{
struct klp_verify_args *args = data;
if (!mod &&
!strcmp(args->name, name) &&
args->addr == addr)
return 1;
return 0;
}
static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
{
struct klp_verify_args args = {
.name = name,
.addr = addr,
};
int ret;
mutex_lock(&module_mutex);
ret = kallsyms_on_each_symbol(klp_verify_callback, &args);
mutex_unlock(&module_mutex);
if (!ret) {
pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?\n",
name, addr);
return -EINVAL;
}
return 0;
}
static int klp_find_verify_func_addr(struct klp_object *obj,
struct klp_func *func)
{
int ret;
#if defined(CONFIG_RANDOMIZE_BASE)
/* If KASLR has been enabled, adjust old_addr accordingly */
if (kaslr_enabled() && func->old_addr)
func->old_addr += kaslr_offset();
#endif
if (!func->old_addr || klp_is_module(obj))
ret = klp_find_object_symbol(obj->name, func->old_name,
&func->old_addr);
else
ret = klp_verify_vmlinux_symbol(func->old_name,
func->old_addr);
return ret;
}
/*
* external symbols are located outside the parent object (where the parent
* object is either vmlinux or the kmod being patched).
......@@ -276,14 +223,18 @@ static int klp_find_external_symbol(struct module *pmod, const char *name,
}
preempt_enable();
/* otherwise check if it's in another .o within the patch module */
return klp_find_object_symbol(pmod->name, name, addr);
/*
* Check if it's in another .o within the patch module. This also
* checks that the external symbol is unique.
*/
return klp_find_object_symbol(pmod->name, name, 0, addr);
}
static int klp_write_object_relocations(struct module *pmod,
struct klp_object *obj)
{
int ret;
int ret = 0;
unsigned long val;
struct klp_reloc *reloc;
if (WARN_ON(!klp_is_object_loaded(obj)))
......@@ -292,41 +243,38 @@ static int klp_write_object_relocations(struct module *pmod,
if (WARN_ON(!obj->relocs))
return -EINVAL;
module_disable_ro(pmod);
for (reloc = obj->relocs; reloc->name; reloc++) {
if (!klp_is_module(obj)) {
#if defined(CONFIG_RANDOMIZE_BASE)
/* If KASLR has been enabled, adjust old value accordingly */
if (kaslr_enabled())
reloc->val += kaslr_offset();
#endif
ret = klp_verify_vmlinux_symbol(reloc->name,
reloc->val);
if (ret)
return ret;
} else {
/* module, reloc->val needs to be discovered */
if (reloc->external)
ret = klp_find_external_symbol(pmod,
reloc->name,
&reloc->val);
else
ret = klp_find_object_symbol(obj->mod->name,
/* discover the address of the referenced symbol */
if (reloc->external) {
if (reloc->sympos > 0) {
pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n",
reloc->name);
ret = -EINVAL;
goto out;
}
ret = klp_find_external_symbol(pmod, reloc->name, &val);
} else
ret = klp_find_object_symbol(obj->name,
reloc->name,
&reloc->val);
reloc->sympos,
&val);
if (ret)
return ret;
}
goto out;
ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc,
reloc->val + reloc->addend);
val + reloc->addend);
if (ret) {
pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n",
reloc->name, reloc->val, ret);
return ret;
reloc->name, val, ret);
goto out;
}
}
return 0;
out:
module_enable_ro(pmod);
return ret;
}
static void notrace klp_ftrace_handler(unsigned long ip,
......@@ -593,7 +541,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
* /sys/kernel/livepatch/<patch>
* /sys/kernel/livepatch/<patch>/enabled
* /sys/kernel/livepatch/<patch>/<object>
* /sys/kernel/livepatch/<patch>/<object>/<func>
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
*/
static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
......@@ -738,8 +686,14 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
INIT_LIST_HEAD(&func->stack_node);
func->state = KLP_DISABLED;
/* The format for the sysfs directory is <function,sympos> where sympos
* is the nth occurrence of this symbol in kallsyms for the patched
* object. If the user selects 0 for old_sympos, then 1 will be used
* since a unique symbol will be the first occurrence.
*/
return kobject_init_and_add(&func->kobj, &klp_ktype_func,
&obj->kobj, "%s", func->old_name);
&obj->kobj, "%s,%lu", func->old_name,
func->old_sympos ? func->old_sympos : 1);
}
/* parts of the initialization that is done only when the object is loaded */
......@@ -756,7 +710,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
}
klp_for_each_func(obj, func) {
ret = klp_find_verify_func_addr(obj, func);
ret = klp_find_object_symbol(obj->name, func->old_name,
func->old_sympos,
&func->old_addr);
if (ret)
return ret;
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment