aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
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 }