|
28 | 28 | #include <linux/list.h> |
29 | 29 | #include <linux/kallsyms.h> |
30 | 30 | #include <linux/livepatch.h> |
| 31 | +#include <linux/elf.h> |
| 32 | +#include <linux/moduleloader.h> |
31 | 33 | #include <asm/cacheflush.h> |
32 | 34 |
|
33 | 35 | /** |
@@ -204,75 +206,109 @@ static int klp_find_object_symbol(const char *objname, const char *name, |
204 | 206 | return -EINVAL; |
205 | 207 | } |
206 | 208 |
|
207 | | -/* |
208 | | - * external symbols are located outside the parent object (where the parent |
209 | | - * object is either vmlinux or the kmod being patched). |
210 | | - */ |
211 | | -static int klp_find_external_symbol(struct module *pmod, const char *name, |
212 | | - unsigned long *addr) |
| 209 | +static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod) |
213 | 210 | { |
214 | | - const struct kernel_symbol *sym; |
215 | | - |
216 | | - /* first, check if it's an exported symbol */ |
217 | | - preempt_disable(); |
218 | | - sym = find_symbol(name, NULL, NULL, true, true); |
219 | | - if (sym) { |
220 | | - *addr = sym->value; |
221 | | - preempt_enable(); |
222 | | - return 0; |
223 | | - } |
224 | | - preempt_enable(); |
| 211 | + int i, cnt, vmlinux, ret; |
| 212 | + char objname[MODULE_NAME_LEN]; |
| 213 | + char symname[KSYM_NAME_LEN]; |
| 214 | + char *strtab = pmod->core_kallsyms.strtab; |
| 215 | + Elf_Rela *relas; |
| 216 | + Elf_Sym *sym; |
| 217 | + unsigned long sympos, addr; |
225 | 218 |
|
226 | 219 | /* |
227 | | - * Check if it's in another .o within the patch module. This also |
228 | | - * checks that the external symbol is unique. |
| 220 | + * Since the field widths for objname and symname in the sscanf() |
| 221 | + * call are hard-coded and correspond to MODULE_NAME_LEN and |
| 222 | + * KSYM_NAME_LEN respectively, we must make sure that MODULE_NAME_LEN |
| 223 | + * and KSYM_NAME_LEN have the values we expect them to have. |
| 224 | + * |
| 225 | + * Because the value of MODULE_NAME_LEN can differ among architectures, |
| 226 | + * we use the smallest/strictest upper bound possible (56, based on |
| 227 | + * the current definition of MODULE_NAME_LEN) to prevent overflows. |
229 | 228 | */ |
230 | | - return klp_find_object_symbol(pmod->name, name, 0, addr); |
| 229 | + BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 128); |
| 230 | + |
| 231 | + relas = (Elf_Rela *) relasec->sh_addr; |
| 232 | + /* For each rela in this klp relocation section */ |
| 233 | + for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) { |
| 234 | + sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info); |
| 235 | + if (sym->st_shndx != SHN_LIVEPATCH) { |
| 236 | + pr_err("symbol %s is not marked as a livepatch symbol", |
| 237 | + strtab + sym->st_name); |
| 238 | + return -EINVAL; |
| 239 | + } |
| 240 | + |
| 241 | + /* Format: .klp.sym.objname.symname,sympos */ |
| 242 | + cnt = sscanf(strtab + sym->st_name, |
| 243 | + ".klp.sym.%55[^.].%127[^,],%lu", |
| 244 | + objname, symname, &sympos); |
| 245 | + if (cnt != 3) { |
| 246 | + pr_err("symbol %s has an incorrectly formatted name", |
| 247 | + strtab + sym->st_name); |
| 248 | + return -EINVAL; |
| 249 | + } |
| 250 | + |
| 251 | + /* klp_find_object_symbol() treats a NULL objname as vmlinux */ |
| 252 | + vmlinux = !strcmp(objname, "vmlinux"); |
| 253 | + ret = klp_find_object_symbol(vmlinux ? NULL : objname, |
| 254 | + symname, sympos, &addr); |
| 255 | + if (ret) |
| 256 | + return ret; |
| 257 | + |
| 258 | + sym->st_value = addr; |
| 259 | + } |
| 260 | + |
| 261 | + return 0; |
231 | 262 | } |
232 | 263 |
|
233 | 264 | static int klp_write_object_relocations(struct module *pmod, |
234 | 265 | struct klp_object *obj) |
235 | 266 | { |
236 | | - int ret = 0; |
237 | | - unsigned long val; |
238 | | - struct klp_reloc *reloc; |
| 267 | + int i, cnt, ret = 0; |
| 268 | + const char *objname, *secname; |
| 269 | + char sec_objname[MODULE_NAME_LEN]; |
| 270 | + Elf_Shdr *sec; |
239 | 271 |
|
240 | 272 | if (WARN_ON(!klp_is_object_loaded(obj))) |
241 | 273 | return -EINVAL; |
242 | 274 |
|
243 | | - if (WARN_ON(!obj->relocs)) |
244 | | - return -EINVAL; |
| 275 | + objname = klp_is_module(obj) ? obj->name : "vmlinux"; |
245 | 276 |
|
246 | 277 | module_disable_ro(pmod); |
| 278 | + /* For each klp relocation section */ |
| 279 | + for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) { |
| 280 | + sec = pmod->klp_info->sechdrs + i; |
| 281 | + secname = pmod->klp_info->secstrings + sec->sh_name; |
| 282 | + if (!(sec->sh_flags & SHF_RELA_LIVEPATCH)) |
| 283 | + continue; |
247 | 284 |
|
248 | | - for (reloc = obj->relocs; reloc->name; reloc++) { |
249 | | - /* discover the address of the referenced symbol */ |
250 | | - if (reloc->external) { |
251 | | - if (reloc->sympos > 0) { |
252 | | - pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n", |
253 | | - reloc->name); |
254 | | - ret = -EINVAL; |
255 | | - goto out; |
256 | | - } |
257 | | - ret = klp_find_external_symbol(pmod, reloc->name, &val); |
258 | | - } else |
259 | | - ret = klp_find_object_symbol(obj->name, |
260 | | - reloc->name, |
261 | | - reloc->sympos, |
262 | | - &val); |
| 285 | + /* |
| 286 | + * Format: .klp.rela.sec_objname.section_name |
| 287 | + * See comment in klp_resolve_symbols() for an explanation |
| 288 | + * of the selected field width value. |
| 289 | + */ |
| 290 | + cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname); |
| 291 | + if (cnt != 1) { |
| 292 | + pr_err("section %s has an incorrectly formatted name", |
| 293 | + secname); |
| 294 | + ret = -EINVAL; |
| 295 | + break; |
| 296 | + } |
| 297 | + |
| 298 | + if (strcmp(objname, sec_objname)) |
| 299 | + continue; |
| 300 | + |
| 301 | + ret = klp_resolve_symbols(sec, pmod); |
263 | 302 | if (ret) |
264 | | - goto out; |
| 303 | + break; |
265 | 304 |
|
266 | | - ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc, |
267 | | - val + reloc->addend); |
268 | | - if (ret) { |
269 | | - pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n", |
270 | | - reloc->name, val, ret); |
271 | | - goto out; |
272 | | - } |
| 305 | + ret = apply_relocate_add(pmod->klp_info->sechdrs, |
| 306 | + pmod->core_kallsyms.strtab, |
| 307 | + pmod->klp_info->symndx, i, pmod); |
| 308 | + if (ret) |
| 309 | + break; |
273 | 310 | } |
274 | 311 |
|
275 | | -out: |
276 | 312 | module_enable_ro(pmod); |
277 | 313 | return ret; |
278 | 314 | } |
@@ -703,11 +739,9 @@ static int klp_init_object_loaded(struct klp_patch *patch, |
703 | 739 | struct klp_func *func; |
704 | 740 | int ret; |
705 | 741 |
|
706 | | - if (obj->relocs) { |
707 | | - ret = klp_write_object_relocations(patch->mod, obj); |
708 | | - if (ret) |
709 | | - return ret; |
710 | | - } |
| 742 | + ret = klp_write_object_relocations(patch->mod, obj); |
| 743 | + if (ret) |
| 744 | + return ret; |
711 | 745 |
|
712 | 746 | klp_for_each_func(obj, func) { |
713 | 747 | ret = klp_find_object_symbol(obj->name, func->old_name, |
@@ -842,6 +876,12 @@ int klp_register_patch(struct klp_patch *patch) |
842 | 876 | { |
843 | 877 | int ret; |
844 | 878 |
|
| 879 | + if (!is_livepatch_module(patch->mod)) { |
| 880 | + pr_err("module %s is not marked as a livepatch module", |
| 881 | + patch->mod->name); |
| 882 | + return -EINVAL; |
| 883 | + } |
| 884 | + |
845 | 885 | if (!klp_initialized()) |
846 | 886 | return -ENODEV; |
847 | 887 |
|
|
0 commit comments