OP is probably using a kernel/architecture that uses "syscall wrappers" where the system call table contains a wrapper function that calls the real syscall function (possibly as an inline function call). The x86_64 architecture has used syscall wrappers since kernel version 4.17.
For x86_64 on kernel 4.17 or later, sys_call_table[__NR_open]
points to __x64_sys_open
(with prototype asmlinkage long __x64_sys_open(const struct pt_regs *regs)
), which calls static
function __se_sys_open
(with prototype static long __se_sys_open(const __user *filename, int flags, umode_t mode)
), which calls inline function __do_sys_open
(with prototype static inline long __do_sys_open(const __user *filename, int flags, umode_t mode)
. Those will all be defined by the SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
macro call in "fs/open.c" and the function body that follows the macro call.
SYSCALL_DEFINE3
is defined in "include/linux/syscalls.h" and uses the SYSCALL_DEFINEx
macro in the same file, which uses the __SYSCALL_DEFINEx
macro. Since x86_64 defines CONFIG_ARCH_HAS_SYSCALL_WRAPPER
, the __SYSCALL_DEFINEx
macro is defined by #include <asm/syscall_wrapper.h>
, which maps to "arch/x86/include/asm/syscall_wrapper.h".
For background on this change, see
It seems the motivation is to only pass a pointer to pt_regs
, instead of having a bunch of user-space values in registers down the call chain. (Perhaps to increase resistance to Spectre attacks by making gadgets less useful?)
Why open
still worked, even though the wrapper didn't:
If OP is indeed using x86_64 kernel 4.17 or later, and replacing the sys_call_table[__NR_open]
entry with a pointer to a function that uses a different prototype and calls the original function (pointed to by old_open
) with the same parameters, that explains why the call to strncpy_from_user(user_msg, filename, sizeof(user_msg))
failed. Although declared as const char * __user filename
, the filename
pointer is actually pointing to the original struct pt_regs
in kernel space.
In the subsequent call to old_open(filename, flags, mode)
, the first parameter filename
is still pointing to the original struct pt_regs
so the old function (which expects a single parameter of type struct pt_regs *
) still works as expected.
i.e. the function passed on its first pointer arg unchanged, despite calling it a different type.