
commit f8c2602733c953ed7a16e060640b8e96f9d94b9b upstream.
s390 enforces DYNAMIC_FTRACE if FUNCTION_TRACER is selected.
At the same time implementation of ftrace_caller is not compliant with
HAVE_DYNAMIC_FTRACE since it doesn't provide implementation of
ftrace_update_ftrace_func() and calls ftrace_trace_function() directly.
The subtle difference is that during ftrace code patching ftrace
replaces function tracer via ftrace_update_ftrace_func() and activates
it back afterwards. Unexpected direct calls to ftrace_trace_function()
during ftrace code patching leads to nullptr-dereferences when tracing
is activated for one of functions which are used during code patching.
Those function currently are:
copy_from_kernel_nofault()
copy_from_kernel_nofault_allowed()
preempt_count_sub() [with debug_defconfig]
preempt_count_add() [with debug_defconfig]
Corresponding KASAN report:
BUG: KASAN: nullptr-dereference in function_trace_call+0x316/0x3b0
Read of size 4 at addr 0000000000001e08 by task migration/0/15
CPU: 0 PID: 15 Comm: migration/0 Tainted: G B 5.13.0-41423-g08316af3644d
Hardware name: IBM 3906 M04 704 (LPAR)
Stopper: multi_cpu_stop+0x0/0x3e0 <- stop_machine_cpuslocked+0x1e4/0x218
Call Trace:
[<0000000001f77caa>] show_stack+0x16a/0x1d0
[<0000000001f8de42>] dump_stack+0x15a/0x1b0
[<0000000001f81d56>] print_address_description.constprop.0+0x66/0x2e0
[<000000000082b0ca>] kasan_report+0x152/0x1c0
[<00000000004cfd8e>] function_trace_call+0x316/0x3b0
[<0000000001fb7082>] ftrace_caller+0x7a/0x7e
[<00000000006bb3e6>] copy_from_kernel_nofault_allowed+0x6/0x10
[<00000000006bb42e>] copy_from_kernel_nofault+0x3e/0xd0
[<000000000014605c>] ftrace_make_call+0xb4/0x1f8
[<000000000047a1b4>] ftrace_replace_code+0x134/0x1d8
[<000000000047a6e0>] ftrace_modify_all_code+0x120/0x1d0
[<000000000047a7ec>] __ftrace_modify_code+0x5c/0x78
[<000000000042395c>] multi_cpu_stop+0x224/0x3e0
[<0000000000423212>] cpu_stopper_thread+0x33a/0x5a0
[<0000000000243ff2>] smpboot_thread_fn+0x302/0x708
[<00000000002329ea>] kthread+0x342/0x408
[<00000000001066b2>] __ret_from_fork+0x92/0xf0
[<0000000001fb57fa>] ret_from_fork+0xa/0x30
The buggy address belongs to the page:
page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1
flags: 0x1ffff00000001000(reserved|node=0|zone=0|lastcpupid=0x1ffff)
raw: 1ffff00000001000 0000040000000048 0000040000000048 0000000000000000
raw: 0000000000000000 0000000000000000 ffffffff00000001 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
0000000000001d00: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
0000000000001d80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
>0000000000001e00: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
^
0000000000001e80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
0000000000001f00: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
==================================================================
To fix that introduce ftrace_func callback to be called from
ftrace_caller and update it in ftrace_update_ftrace_func().
Fixes: 4cc9bed034
("[S390] cleanup ftrace backend functions")
Cc: stable@vger.kernel.org
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
119 lines
2.8 KiB
C
119 lines
2.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_S390_FTRACE_H
|
|
#define _ASM_S390_FTRACE_H
|
|
|
|
#define ARCH_SUPPORTS_FTRACE_OPS 1
|
|
|
|
#if defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT)
|
|
#define MCOUNT_INSN_SIZE 6
|
|
#else
|
|
#define MCOUNT_INSN_SIZE 24
|
|
#define MCOUNT_RETURN_FIXUP 18
|
|
#endif
|
|
|
|
#define HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
#ifdef CONFIG_CC_IS_CLANG
|
|
/* https://bugs.llvm.org/show_bug.cgi?id=41424 */
|
|
#define ftrace_return_address(n) 0UL
|
|
#else
|
|
#define ftrace_return_address(n) __builtin_return_address(n)
|
|
#endif
|
|
|
|
void _mcount(void);
|
|
void ftrace_caller(void);
|
|
|
|
extern char ftrace_graph_caller_end;
|
|
extern unsigned long ftrace_plt;
|
|
extern void *ftrace_func;
|
|
|
|
struct dyn_arch_ftrace { };
|
|
|
|
#define MCOUNT_ADDR ((unsigned long)_mcount)
|
|
#define FTRACE_ADDR ((unsigned long)ftrace_caller)
|
|
|
|
#define KPROBE_ON_FTRACE_NOP 0
|
|
#define KPROBE_ON_FTRACE_CALL 1
|
|
|
|
static inline unsigned long ftrace_call_adjust(unsigned long addr)
|
|
{
|
|
return addr;
|
|
}
|
|
|
|
struct ftrace_insn {
|
|
u16 opc;
|
|
s32 disp;
|
|
} __packed;
|
|
|
|
static inline void ftrace_generate_nop_insn(struct ftrace_insn *insn)
|
|
{
|
|
#ifdef CONFIG_FUNCTION_TRACER
|
|
#if defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT)
|
|
/* brcl 0,0 */
|
|
insn->opc = 0xc004;
|
|
insn->disp = 0;
|
|
#else
|
|
/* jg .+24 */
|
|
insn->opc = 0xc0f4;
|
|
insn->disp = MCOUNT_INSN_SIZE / 2;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static inline int is_ftrace_nop(struct ftrace_insn *insn)
|
|
{
|
|
#ifdef CONFIG_FUNCTION_TRACER
|
|
#if defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT)
|
|
if (insn->disp == 0)
|
|
return 1;
|
|
#else
|
|
if (insn->disp == MCOUNT_INSN_SIZE / 2)
|
|
return 1;
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static inline void ftrace_generate_call_insn(struct ftrace_insn *insn,
|
|
unsigned long ip)
|
|
{
|
|
#ifdef CONFIG_FUNCTION_TRACER
|
|
unsigned long target;
|
|
|
|
/* brasl r0,ftrace_caller */
|
|
target = is_module_addr((void *) ip) ? ftrace_plt : FTRACE_ADDR;
|
|
insn->opc = 0xc005;
|
|
insn->disp = (target - ip) / 2;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Even though the system call numbers are identical for s390/s390x a
|
|
* different system call table is used for compat tasks. This may lead
|
|
* to e.g. incorrect or missing trace event sysfs files.
|
|
* Therefore simply do not trace compat system calls at all.
|
|
* See kernel/trace/trace_syscalls.c.
|
|
*/
|
|
#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
|
|
static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
|
|
{
|
|
return is_compat_task();
|
|
}
|
|
|
|
#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
|
|
static inline bool arch_syscall_match_sym_name(const char *sym,
|
|
const char *name)
|
|
{
|
|
/*
|
|
* Skip __s390_ and __s390x_ prefix - due to compat wrappers
|
|
* and aliasing some symbols of 64 bit system call functions
|
|
* may get the __s390_ prefix instead of the __s390x_ prefix.
|
|
*/
|
|
return !strcmp(sym + 7, name) || !strcmp(sym + 8, name);
|
|
}
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
#endif /* _ASM_S390_FTRACE_H */
|