Skip to content

Commit 36dac9a

Browse files
gclementtsbogend
authored andcommitted
MIPS: ftrace: Fix memory corruption when kernel is located beyond 32 bits
Since commit e424054 ("MIPS: Tracing: Reduce the overhead of dynamic Function Tracer"), the macro UASM_i_LA_mostly has been used, and this macro can generate more than 2 instructions. At the same time, the code in ftrace assumes that no more than 2 instructions can be generated, which is why it stores them in an int[2] array. However, as previously noted, the macro UASM_i_LA_mostly (and now UASM_i_LA) causes a buffer overflow when _mcount is beyond 32 bits. This leads to corruption of the variables located in the __read_mostly section. This corruption was observed because the variable __cpu_primary_thread_mask was corrupted, causing a hang very early during boot. This fix prevents the corruption by avoiding the generation of instructions if they could exceed 2 instructions in length. Fortunately, insn_la_mcount is only used if the instrumented code is located outside the kernel code section, so dynamic ftrace can still be used, albeit in a more limited scope. This is still preferable to corrupting memory and/or crashing the kernel. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
1 parent 4f0b3cd commit 36dac9a

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

arch/mips/kernel/ftrace.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,20 @@ static inline void ftrace_dyn_arch_init_insns(void)
5454
u32 *buf;
5555
unsigned int v1;
5656

57-
/* la v1, _mcount */
58-
v1 = 3;
59-
buf = (u32 *)&insn_la_mcount[0];
60-
UASM_i_LA(&buf, v1, MCOUNT_ADDR);
57+
/* If we are not in compat space, the number of generated
58+
* instructions will exceed the maximum expected limit of 2.
59+
* To prevent buffer overflow, we avoid generating them.
60+
* insn_la_mcount will not be used later in ftrace_make_call.
61+
*/
62+
if (uasm_in_compat_space_p(MCOUNT_ADDR)) {
63+
/* la v1, _mcount */
64+
v1 = 3;
65+
buf = (u32 *)&insn_la_mcount[0];
66+
UASM_i_LA(&buf, v1, MCOUNT_ADDR);
67+
} else {
68+
pr_warn("ftrace: mcount address beyond 32 bits is not supported (%lX)\n",
69+
MCOUNT_ADDR);
70+
}
6171

6272
/* jal (ftrace_caller + 8), jump over the first two instruction */
6373
buf = (u32 *)&insn_jal_ftrace_caller;
@@ -189,6 +199,13 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
189199
unsigned int new;
190200
unsigned long ip = rec->ip;
191201

202+
/* When the code to patch does not belong to the kernel code
203+
* space, we must use insn_la_mcount. However, if MCOUNT_ADDR
204+
* is not in compat space, insn_la_mcount is not usable.
205+
*/
206+
if (!core_kernel_text(ip) && !uasm_in_compat_space_p(MCOUNT_ADDR))
207+
return -EFAULT;
208+
192209
new = core_kernel_text(ip) ? insn_jal_ftrace_caller : insn_la_mcount[0];
193210

194211
#ifdef CONFIG_64BIT

0 commit comments

Comments
 (0)