Skip to content

Commit f8af0b3

Browse files
Ard Biesheuvelwildea01
authored andcommitted
arm64: ftrace: don't validate branch via PLT in ftrace_make_nop()
When turning branch instructions into NOPs, we attempt to validate the action by comparing the old value at the call site with the opcode of a direct relative branch instruction pointing at the old target. However, these call sites are statically initialized to call _mcount(), and may be redirected via a PLT entry if the module is loaded far away from the kernel text, leading to false negatives and spurious errors. So skip the validation if CONFIG_ARM64_MODULE_PLTS is configured. Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent dbbb08f commit f8af0b3

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

arch/arm64/kernel/ftrace.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,52 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
8484
unsigned long addr)
8585
{
8686
unsigned long pc = rec->ip;
87-
u32 old, new;
87+
long offset = (long)pc - (long)addr;
88+
bool validate = true;
89+
u32 old = 0, new;
90+
91+
if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
92+
(offset < -SZ_128M || offset >= SZ_128M)) {
93+
u32 replaced;
94+
95+
/*
96+
* 'mod' is only set at module load time, but if we end up
97+
* dealing with an out-of-range condition, we can assume it
98+
* is due to a module being loaded far away from the kernel.
99+
*/
100+
if (!mod) {
101+
preempt_disable();
102+
mod = __module_text_address(pc);
103+
preempt_enable();
104+
105+
if (WARN_ON(!mod))
106+
return -EINVAL;
107+
}
108+
109+
/*
110+
* The instruction we are about to patch may be a branch and
111+
* link instruction that was redirected via a PLT entry. In
112+
* this case, the normal validation will fail, but we can at
113+
* least check that we are dealing with a branch and link
114+
* instruction that points into the right module.
115+
*/
116+
if (aarch64_insn_read((void *)pc, &replaced))
117+
return -EFAULT;
118+
119+
if (!aarch64_insn_is_bl(replaced) ||
120+
!within_module(pc + aarch64_get_branch_offset(replaced),
121+
mod))
122+
return -EINVAL;
123+
124+
validate = false;
125+
} else {
126+
old = aarch64_insn_gen_branch_imm(pc, addr,
127+
AARCH64_INSN_BRANCH_LINK);
128+
}
88129

89-
old = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
90130
new = aarch64_insn_gen_nop();
91131

92-
return ftrace_modify_code(pc, old, new, true);
132+
return ftrace_modify_code(pc, old, new, validate);
93133
}
94134

95135
void arch_ftrace_update_code(int command)

0 commit comments

Comments
 (0)