diff options
Diffstat (limited to 'kernel/livepatch')
| -rw-r--r-- | kernel/livepatch/core.c | 72 |
1 files changed, 31 insertions, 41 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index db545cbcdb89..e416f96e938d 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -135,13 +135,8 @@ struct klp_find_arg { | |||
| 135 | const char *objname; | 135 | const char *objname; |
| 136 | const char *name; | 136 | const char *name; |
| 137 | unsigned long addr; | 137 | unsigned long addr; |
| 138 | /* | ||
| 139 | * If count == 0, the symbol was not found. If count == 1, a unique | ||
| 140 | * match was found and addr is set. If count > 1, there is | ||
| 141 | * unresolvable ambiguity among "count" number of symbols with the same | ||
| 142 | * name in the same object. | ||
| 143 | */ | ||
| 144 | unsigned long count; | 138 | unsigned long count; |
| 139 | unsigned long pos; | ||
| 145 | }; | 140 | }; |
| 146 | 141 | ||
| 147 | static int klp_find_callback(void *data, const char *name, | 142 | static int klp_find_callback(void *data, const char *name, |
| @@ -158,37 +153,48 @@ static int klp_find_callback(void *data, const char *name, | |||
| 158 | if (args->objname && strcmp(args->objname, mod->name)) | 153 | if (args->objname && strcmp(args->objname, mod->name)) |
| 159 | return 0; | 154 | return 0; |
| 160 | 155 | ||
| 161 | /* | ||
| 162 | * args->addr might be overwritten if another match is found | ||
| 163 | * but klp_find_object_symbol() handles this and only returns the | ||
| 164 | * addr if count == 1. | ||
| 165 | */ | ||
| 166 | args->addr = addr; | 156 | args->addr = addr; |
| 167 | args->count++; | 157 | args->count++; |
| 168 | 158 | ||
| 159 | /* | ||
| 160 | * Finish the search when the symbol is found for the desired position | ||
| 161 | * or the position is not defined for a non-unique symbol. | ||
| 162 | */ | ||
| 163 | if ((args->pos && (args->count == args->pos)) || | ||
| 164 | (!args->pos && (args->count > 1))) | ||
| 165 | return 1; | ||
| 166 | |||
| 169 | return 0; | 167 | return 0; |
| 170 | } | 168 | } |
| 171 | 169 | ||
| 172 | static int klp_find_object_symbol(const char *objname, const char *name, | 170 | static int klp_find_object_symbol(const char *objname, const char *name, |
| 173 | unsigned long *addr) | 171 | unsigned long sympos, unsigned long *addr) |
| 174 | { | 172 | { |
| 175 | struct klp_find_arg args = { | 173 | struct klp_find_arg args = { |
| 176 | .objname = objname, | 174 | .objname = objname, |
| 177 | .name = name, | 175 | .name = name, |
| 178 | .addr = 0, | 176 | .addr = 0, |
| 179 | .count = 0 | 177 | .count = 0, |
| 178 | .pos = sympos, | ||
| 180 | }; | 179 | }; |
| 181 | 180 | ||
| 182 | mutex_lock(&module_mutex); | 181 | mutex_lock(&module_mutex); |
| 183 | kallsyms_on_each_symbol(klp_find_callback, &args); | 182 | kallsyms_on_each_symbol(klp_find_callback, &args); |
| 184 | mutex_unlock(&module_mutex); | 183 | mutex_unlock(&module_mutex); |
| 185 | 184 | ||
| 186 | if (args.count == 0) | 185 | /* |
| 186 | * Ensure an address was found. If sympos is 0, ensure symbol is unique; | ||
| 187 | * otherwise ensure the symbol position count matches sympos. | ||
| 188 | */ | ||
| 189 | if (args.addr == 0) | ||
| 187 | pr_err("symbol '%s' not found in symbol table\n", name); | 190 | pr_err("symbol '%s' not found in symbol table\n", name); |
| 188 | else if (args.count > 1) | 191 | else if (args.count > 1 && sympos == 0) { |
| 189 | pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", | 192 | pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", |
| 190 | args.count, name, objname); | 193 | args.count, name, objname); |
| 191 | else { | 194 | } else if (sympos != args.count && sympos > 0) { |
| 195 | pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n", | ||
| 196 | sympos, name, objname ? objname : "vmlinux"); | ||
| 197 | } else { | ||
| 192 | *addr = args.addr; | 198 | *addr = args.addr; |
| 193 | return 0; | 199 | return 0; |
| 194 | } | 200 | } |
| @@ -236,27 +242,6 @@ static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr) | |||
| 236 | return 0; | 242 | return 0; |
| 237 | } | 243 | } |
| 238 | 244 | ||
| 239 | static int klp_find_verify_func_addr(struct klp_object *obj, | ||
| 240 | struct klp_func *func) | ||
| 241 | { | ||
| 242 | int ret; | ||
| 243 | |||
| 244 | #if defined(CONFIG_RANDOMIZE_BASE) | ||
| 245 | /* If KASLR has been enabled, adjust old_addr accordingly */ | ||
| 246 | if (kaslr_enabled() && func->old_addr) | ||
| 247 | func->old_addr += kaslr_offset(); | ||
| 248 | #endif | ||
| 249 | |||
| 250 | if (!func->old_addr || klp_is_module(obj)) | ||
| 251 | ret = klp_find_object_symbol(obj->name, func->old_name, | ||
| 252 | &func->old_addr); | ||
| 253 | else | ||
| 254 | ret = klp_verify_vmlinux_symbol(func->old_name, | ||
| 255 | func->old_addr); | ||
| 256 | |||
| 257 | return ret; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* | 245 | /* |
| 261 | * external symbols are located outside the parent object (where the parent | 246 | * external symbols are located outside the parent object (where the parent |
| 262 | * object is either vmlinux or the kmod being patched). | 247 | * object is either vmlinux or the kmod being patched). |
| @@ -276,8 +261,11 @@ static int klp_find_external_symbol(struct module *pmod, const char *name, | |||
| 276 | } | 261 | } |
| 277 | preempt_enable(); | 262 | preempt_enable(); |
| 278 | 263 | ||
| 279 | /* otherwise check if it's in another .o within the patch module */ | 264 | /* |
| 280 | return klp_find_object_symbol(pmod->name, name, addr); | 265 | * Check if it's in another .o within the patch module. This also |
| 266 | * checks that the external symbol is unique. | ||
| 267 | */ | ||
| 268 | return klp_find_object_symbol(pmod->name, name, 0, addr); | ||
| 281 | } | 269 | } |
| 282 | 270 | ||
| 283 | static int klp_write_object_relocations(struct module *pmod, | 271 | static int klp_write_object_relocations(struct module *pmod, |
| @@ -313,7 +301,7 @@ static int klp_write_object_relocations(struct module *pmod, | |||
| 313 | else | 301 | else |
| 314 | ret = klp_find_object_symbol(obj->mod->name, | 302 | ret = klp_find_object_symbol(obj->mod->name, |
| 315 | reloc->name, | 303 | reloc->name, |
| 316 | &reloc->val); | 304 | 0, &reloc->val); |
| 317 | if (ret) | 305 | if (ret) |
| 318 | return ret; | 306 | return ret; |
| 319 | } | 307 | } |
| @@ -756,7 +744,9 @@ static int klp_init_object_loaded(struct klp_patch *patch, | |||
| 756 | } | 744 | } |
| 757 | 745 | ||
| 758 | klp_for_each_func(obj, func) { | 746 | klp_for_each_func(obj, func) { |
| 759 | ret = klp_find_verify_func_addr(obj, func); | 747 | ret = klp_find_object_symbol(obj->name, func->old_name, |
| 748 | func->old_sympos, | ||
| 749 | &func->old_addr); | ||
| 760 | if (ret) | 750 | if (ret) |
| 761 | return ret; | 751 | return ret; |
| 762 | } | 752 | } |
