aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
authorChris J Arges <chris.j.arges@canonical.com>2015-12-01 21:40:54 -0500
committerJiri Kosina <jkosina@suse.cz>2015-12-03 17:01:26 -0500
commitb2b018ef48675a9a524fa9791ea7d67fdac405f7 (patch)
treee683d922f21f8b88b2ae502e4d076fcaac27d898 /kernel/livepatch
parenta3d66b5a17f81ee84604f95b0e2c9ccf0434c6f0 (diff)
livepatch: add old_sympos as disambiguator field to klp_func
Currently, patching objects with duplicate symbol names fail because the creation of the sysfs function directory collides with the previous attempt. Appending old_addr to the function name is problematic as it reveals the address of the function being patch to a normal user. Using the symbol's occurrence in kallsyms to postfix the function name in the sysfs directory solves the issue of having consistent unique names and ensuring that the address is not exposed to a normal user. In addition, using the symbol position as the user's method to disambiguate symbols instead of addr allows for disambiguating symbols in modules as well for both function addresses and for relocs. This also simplifies much of the code. Special handling for kASLR is no longer needed and can be removed. The klp_find_verify_func_addr function can be replaced by klp_find_object_symbol, and klp_verify_vmlinux_symbol and its callback can be removed completely. In cases of duplicate symbols, old_sympos will be used to disambiguate instead of old_addr. By default old_sympos will be 0, and patching will only succeed if the symbol is unique. Specifying a positive value will ensure that occurrence of the symbol in kallsyms for the patched object will be used for patching if it is valid. In addition, make old_addr an internal structure field not to be specified by the user. Finally, remove klp_find_verify_func_addr as it can be replaced by klp_find_object_symbol directly. Support for symbol position disambiguation for relocations is added in the next patch in this series. Signed-off-by: Chris J Arges <chris.j.arges@canonical.com> Reviewed-by: Petr Mladek <pmladek@suse.com> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel/livepatch')
-rw-r--r--kernel/livepatch/core.c72
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
147static int klp_find_callback(void *data, const char *name, 142static 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
172static int klp_find_object_symbol(const char *objname, const char *name, 170static 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
239static 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
283static int klp_write_object_relocations(struct module *pmod, 271static 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 }