Skip to content

Commit 8e8ae78

Browse files
abarnas-usyswilldeacon
authored andcommitted
arm64: Reject modules with internal alternative callbacks
During module loading, check if a callback function used by the alternatives specified in the '.altinstruction' ELF section (if present) is located in core kernel .text. If not fail module loading before callback is called. Reported-by: Fanqin Cui <cuifq1@chinatelecom.cn> Closes: https://lore.kernel.org/all/20250807072700.348514-1-fanqincui@163.com/ Signed-off-by: Adrian Barnaś <abarnas@google.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org> [will: Folded in 'noinstr' tweak from Mark] Signed-off-by: Will Deacon <will@kernel.org>
1 parent 6d4a0fb commit 8e8ae78

File tree

3 files changed

+24
-11
lines changed

3 files changed

+24
-11
lines changed

arch/arm64/include/asm/alternative.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ void __init apply_alternatives_all(void);
2626
bool alternative_is_applied(u16 cpucap);
2727

2828
#ifdef CONFIG_MODULES
29-
void apply_alternatives_module(void *start, size_t length);
29+
int apply_alternatives_module(void *start, size_t length);
3030
#else
31-
static inline void apply_alternatives_module(void *start, size_t length) { }
31+
static inline int apply_alternatives_module(void *start, size_t length)
32+
{
33+
return 0;
34+
}
3235
#endif
3336

3437
void alt_cb_patch_nops(struct alt_instr *alt, __le32 *origptr,

arch/arm64/kernel/alternative.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ static noinstr void clean_dcache_range_nopatch(u64 start, u64 end)
139139
} while (cur += d_size, cur < end);
140140
}
141141

142-
static void __apply_alternatives(const struct alt_region *region,
143-
bool is_module,
144-
unsigned long *cpucap_mask)
142+
static int __apply_alternatives(const struct alt_region *region,
143+
bool is_module,
144+
unsigned long *cpucap_mask)
145145
{
146146
struct alt_instr *alt;
147147
__le32 *origptr, *updptr;
@@ -166,10 +166,13 @@ static void __apply_alternatives(const struct alt_region *region,
166166
updptr = is_module ? origptr : lm_alias(origptr);
167167
nr_inst = alt->orig_len / AARCH64_INSN_SIZE;
168168

169-
if (ALT_HAS_CB(alt))
169+
if (ALT_HAS_CB(alt)) {
170170
alt_cb = ALT_REPL_PTR(alt);
171-
else
171+
if (is_module && !core_kernel_text((unsigned long)alt_cb))
172+
return -ENOEXEC;
173+
} else {
172174
alt_cb = patch_alternative;
175+
}
173176

174177
alt_cb(alt, origptr, updptr, nr_inst);
175178

@@ -193,6 +196,8 @@ static void __apply_alternatives(const struct alt_region *region,
193196
bitmap_and(applied_alternatives, applied_alternatives,
194197
system_cpucaps, ARM64_NCAPS);
195198
}
199+
200+
return 0;
196201
}
197202

198203
static void __init apply_alternatives_vdso(void)
@@ -277,7 +282,7 @@ void __init apply_boot_alternatives(void)
277282
}
278283

279284
#ifdef CONFIG_MODULES
280-
void apply_alternatives_module(void *start, size_t length)
285+
int apply_alternatives_module(void *start, size_t length)
281286
{
282287
struct alt_region region = {
283288
.begin = start,
@@ -287,7 +292,7 @@ void apply_alternatives_module(void *start, size_t length)
287292

288293
bitmap_fill(all_capabilities, ARM64_NCAPS);
289294

290-
__apply_alternatives(&region, true, &all_capabilities[0]);
295+
return __apply_alternatives(&region, true, &all_capabilities[0]);
291296
}
292297
#endif
293298

arch/arm64/kernel/module.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,13 @@ int module_finalize(const Elf_Ehdr *hdr,
489489
int ret;
490490

491491
s = find_section(hdr, sechdrs, ".altinstructions");
492-
if (s)
493-
apply_alternatives_module((void *)s->sh_addr, s->sh_size);
492+
if (s) {
493+
ret = apply_alternatives_module((void *)s->sh_addr, s->sh_size);
494+
if (ret < 0) {
495+
pr_err("module %s: error occurred when applying alternatives\n", me->name);
496+
return ret;
497+
}
498+
}
494499

495500
if (scs_is_dynamic()) {
496501
s = find_section(hdr, sechdrs, ".init.eh_frame");

0 commit comments

Comments
 (0)