Skip to content

Commit 0c05e7b

Browse files
liu-song-6pmladek
authored andcommitted
livepatch,x86: Clear relocation targets on a module removal
Josh reported a bug: When the object to be patched is a module, and that module is rmmod'ed and reloaded, it fails to load with: module: x86/modules: Skipping invalid relocation target, existing value is nonzero for type 2, loc 00000000ba0302e9, val ffffffffa03e293c livepatch: failed to initialize patch 'livepatch_nfsd' for module 'nfsd' (-8) livepatch: patch 'livepatch_nfsd' failed for module 'nfsd', refusing to load module 'nfsd' The livepatch module has a relocation which references a symbol in the _previous_ loading of nfsd. When apply_relocate_add() tries to replace the old relocation with a new one, it sees that the previous one is nonzero and it errors out. He also proposed three different solutions. We could remove the error check in apply_relocate_add() introduced by commit eda9cec ("x86/module: Detect and skip invalid relocations"). However the check is useful for detecting corrupted modules. We could also deny the patched modules to be removed. If it proved to be a major drawback for users, we could still implement a different approach. The solution would also complicate the existing code a lot. We thus decided to reverse the relocation patching (clear all relocation targets on x86_64). The solution is not universal and is too much arch-specific, but it may prove to be simpler in the end. Reported-by: Josh Poimboeuf <jpoimboe@redhat.com> Originally-by: Miroslav Benes <mbenes@suse.cz> Signed-off-by: Song Liu <song@kernel.org> Acked-by: Miroslav Benes <mbenes@suse.cz> Reviewed-by: Petr Mladek <pmladek@suse.com> Acked-by: Josh Poimboeuf <jpoimboe@kernel.org> Reviewed-by: Joe Lawrence <joe.lawrence@redhat.com> Tested-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20230125185401.279042-2-song@kernel.org
1 parent bbb9336 commit 0c05e7b

File tree

3 files changed

+126
-46
lines changed

3 files changed

+126
-46
lines changed

arch/x86/kernel/module.c

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,27 @@ int apply_relocate(Elf32_Shdr *sechdrs,
128128
return 0;
129129
}
130130
#else /*X86_64*/
131-
static int __apply_relocate_add(Elf64_Shdr *sechdrs,
131+
static int __write_relocate_add(Elf64_Shdr *sechdrs,
132132
const char *strtab,
133133
unsigned int symindex,
134134
unsigned int relsec,
135135
struct module *me,
136-
void *(*write)(void *dest, const void *src, size_t len))
136+
void *(*write)(void *dest, const void *src, size_t len),
137+
bool apply)
137138
{
138139
unsigned int i;
139140
Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
140141
Elf64_Sym *sym;
141142
void *loc;
142143
u64 val;
144+
u64 zero = 0ULL;
143145

144-
DEBUGP("Applying relocate section %u to %u\n",
146+
DEBUGP("%s relocate section %u to %u\n",
147+
apply ? "Applying" : "Clearing",
145148
relsec, sechdrs[relsec].sh_info);
146149
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
150+
size_t size;
151+
147152
/* This is where to make the change */
148153
loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
149154
+ rel[i].r_offset;
@@ -161,52 +166,53 @@ static int __apply_relocate_add(Elf64_Shdr *sechdrs,
161166

162167
switch (ELF64_R_TYPE(rel[i].r_info)) {
163168
case R_X86_64_NONE:
164-
break;
169+
continue; /* nothing to write */
165170
case R_X86_64_64:
166-
if (*(u64 *)loc != 0)
167-
goto invalid_relocation;
168-
write(loc, &val, 8);
171+
size = 8;
169172
break;
170173
case R_X86_64_32:
171-
if (*(u32 *)loc != 0)
172-
goto invalid_relocation;
173-
write(loc, &val, 4);
174-
if (val != *(u32 *)loc)
174+
if (val != *(u32 *)&val)
175175
goto overflow;
176+
size = 4;
176177
break;
177178
case R_X86_64_32S:
178-
if (*(s32 *)loc != 0)
179-
goto invalid_relocation;
180-
write(loc, &val, 4);
181-
if ((s64)val != *(s32 *)loc)
179+
if ((s64)val != *(s32 *)&val)
182180
goto overflow;
181+
size = 4;
183182
break;
184183
case R_X86_64_PC32:
185184
case R_X86_64_PLT32:
186-
if (*(u32 *)loc != 0)
187-
goto invalid_relocation;
188185
val -= (u64)loc;
189-
write(loc, &val, 4);
186+
size = 4;
190187
break;
191188
case R_X86_64_PC64:
192-
if (*(u64 *)loc != 0)
193-
goto invalid_relocation;
194189
val -= (u64)loc;
195-
write(loc, &val, 8);
190+
size = 8;
196191
break;
197192
default:
198193
pr_err("%s: Unknown rela relocation: %llu\n",
199194
me->name, ELF64_R_TYPE(rel[i].r_info));
200195
return -ENOEXEC;
201196
}
197+
198+
if (apply) {
199+
if (memcmp(loc, &zero, size)) {
200+
pr_err("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
201+
(int)ELF64_R_TYPE(rel[i].r_info), loc, val);
202+
return -ENOEXEC;
203+
}
204+
write(loc, &val, size);
205+
} else {
206+
if (memcmp(loc, &val, size)) {
207+
pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n",
208+
(int)ELF64_R_TYPE(rel[i].r_info), loc, val);
209+
return -ENOEXEC;
210+
}
211+
write(loc, &zero, size);
212+
}
202213
}
203214
return 0;
204215

205-
invalid_relocation:
206-
pr_err("x86/modules: Skipping invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
207-
(int)ELF64_R_TYPE(rel[i].r_info), loc, val);
208-
return -ENOEXEC;
209-
210216
overflow:
211217
pr_err("overflow in relocation type %d val %Lx\n",
212218
(int)ELF64_R_TYPE(rel[i].r_info), val);
@@ -215,11 +221,12 @@ static int __apply_relocate_add(Elf64_Shdr *sechdrs,
215221
return -ENOEXEC;
216222
}
217223

218-
int apply_relocate_add(Elf64_Shdr *sechdrs,
219-
const char *strtab,
220-
unsigned int symindex,
221-
unsigned int relsec,
222-
struct module *me)
224+
static int write_relocate_add(Elf64_Shdr *sechdrs,
225+
const char *strtab,
226+
unsigned int symindex,
227+
unsigned int relsec,
228+
struct module *me,
229+
bool apply)
223230
{
224231
int ret;
225232
bool early = me->state == MODULE_STATE_UNFORMED;
@@ -230,8 +237,8 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
230237
mutex_lock(&text_mutex);
231238
}
232239

233-
ret = __apply_relocate_add(sechdrs, strtab, symindex, relsec, me,
234-
write);
240+
ret = __write_relocate_add(sechdrs, strtab, symindex, relsec, me,
241+
write, apply);
235242

236243
if (!early) {
237244
text_poke_sync();
@@ -241,6 +248,26 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
241248
return ret;
242249
}
243250

251+
int apply_relocate_add(Elf64_Shdr *sechdrs,
252+
const char *strtab,
253+
unsigned int symindex,
254+
unsigned int relsec,
255+
struct module *me)
256+
{
257+
return write_relocate_add(sechdrs, strtab, symindex, relsec, me, true);
258+
}
259+
260+
#ifdef CONFIG_LIVEPATCH
261+
void clear_relocate_add(Elf64_Shdr *sechdrs,
262+
const char *strtab,
263+
unsigned int symindex,
264+
unsigned int relsec,
265+
struct module *me)
266+
{
267+
write_relocate_add(sechdrs, strtab, symindex, relsec, me, false);
268+
}
269+
#endif
270+
244271
#endif
245272

246273
int module_finalize(const Elf_Ehdr *hdr,

include/linux/moduleloader.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
7272
unsigned int symindex,
7373
unsigned int relsec,
7474
struct module *mod);
75+
#ifdef CONFIG_LIVEPATCH
76+
/*
77+
* Some architectures (namely x86_64 and ppc64) perform sanity checks when
78+
* applying relocations. If a patched module gets unloaded and then later
79+
* reloaded (and re-patched), klp re-applies relocations to the replacement
80+
* function(s). Any leftover relocations from the previous loading of the
81+
* patched module might trigger the sanity checks.
82+
*
83+
* To prevent that, when unloading a patched module, clear out any relocations
84+
* that might trigger arch-specific sanity checks on a future module reload.
85+
*/
86+
void clear_relocate_add(Elf_Shdr *sechdrs,
87+
const char *strtab,
88+
unsigned int symindex,
89+
unsigned int relsec,
90+
struct module *me);
91+
#endif
7592
#else
7693
static inline int apply_relocate_add(Elf_Shdr *sechdrs,
7794
const char *strtab,

kernel/livepatch/core.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
261261
return 0;
262262
}
263263

264+
void __weak clear_relocate_add(Elf_Shdr *sechdrs,
265+
const char *strtab,
266+
unsigned int symindex,
267+
unsigned int relsec,
268+
struct module *me)
269+
{
270+
}
271+
264272
/*
265273
* At a high-level, there are two types of klp relocation sections: those which
266274
* reference symbols which live in vmlinux; and those which reference symbols
@@ -284,10 +292,10 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
284292
* the to-be-patched module to be loaded and patched sometime *after* the
285293
* klp module is loaded.
286294
*/
287-
int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
288-
const char *shstrtab, const char *strtab,
289-
unsigned int symndx, unsigned int secndx,
290-
const char *objname)
295+
static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
296+
const char *shstrtab, const char *strtab,
297+
unsigned int symndx, unsigned int secndx,
298+
const char *objname, bool apply)
291299
{
292300
int cnt, ret;
293301
char sec_objname[MODULE_NAME_LEN];
@@ -309,11 +317,26 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
309317
if (strcmp(objname ? objname : "vmlinux", sec_objname))
310318
return 0;
311319

312-
ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, sec_objname);
313-
if (ret)
314-
return ret;
320+
if (apply) {
321+
ret = klp_resolve_symbols(sechdrs, strtab, symndx,
322+
sec, sec_objname);
323+
if (ret)
324+
return ret;
325+
326+
return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
327+
}
328+
329+
clear_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
330+
return 0;
331+
}
315332

316-
return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
333+
int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
334+
const char *shstrtab, const char *strtab,
335+
unsigned int symndx, unsigned int secndx,
336+
const char *objname)
337+
{
338+
return klp_write_section_relocs(pmod, sechdrs, shstrtab, strtab, symndx,
339+
secndx, objname, true);
317340
}
318341

319342
/*
@@ -762,8 +785,9 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
762785
func->old_sympos ? func->old_sympos : 1);
763786
}
764787

765-
static int klp_apply_object_relocs(struct klp_patch *patch,
766-
struct klp_object *obj)
788+
static int klp_write_object_relocs(struct klp_patch *patch,
789+
struct klp_object *obj,
790+
bool apply)
767791
{
768792
int i, ret;
769793
struct klp_modinfo *info = patch->mod->klp_info;
@@ -774,17 +798,29 @@ static int klp_apply_object_relocs(struct klp_patch *patch,
774798
if (!(sec->sh_flags & SHF_RELA_LIVEPATCH))
775799
continue;
776800

777-
ret = klp_apply_section_relocs(patch->mod, info->sechdrs,
801+
ret = klp_write_section_relocs(patch->mod, info->sechdrs,
778802
info->secstrings,
779803
patch->mod->core_kallsyms.strtab,
780-
info->symndx, i, obj->name);
804+
info->symndx, i, obj->name, apply);
781805
if (ret)
782806
return ret;
783807
}
784808

785809
return 0;
786810
}
787811

812+
static int klp_apply_object_relocs(struct klp_patch *patch,
813+
struct klp_object *obj)
814+
{
815+
return klp_write_object_relocs(patch, obj, true);
816+
}
817+
818+
static void klp_clear_object_relocs(struct klp_patch *patch,
819+
struct klp_object *obj)
820+
{
821+
klp_write_object_relocs(patch, obj, false);
822+
}
823+
788824
/* parts of the initialization that is done only when the object is loaded */
789825
static int klp_init_object_loaded(struct klp_patch *patch,
790826
struct klp_object *obj)
@@ -1172,7 +1208,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
11721208
klp_unpatch_object(obj);
11731209

11741210
klp_post_unpatch_callback(obj);
1175-
1211+
klp_clear_object_relocs(patch, obj);
11761212
klp_free_object_loaded(obj);
11771213
break;
11781214
}

0 commit comments

Comments
 (0)