-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Attempting to patch a syscall results in an error due to a missing fentry hook in the inner __do_sys##name() function. The fentry hook is missing because of the 'inline' annotation, which invokes 'notrace'. Add some kpatch-specific syscall definition macros which can be used for patching a syscall. These macros are copied almost verbatim from the kernel, the main difference being a 'kpatch' prefix added to the __do_sys##name() function name. This causes kpatch-build to treat it as a new function (due to its new name), and its caller __se_sys##name() function is inlined by its own caller __x64_sys##name() function, which has an fentry hook. To patch a syscall, just use replace the use of the SYSCALL_DEFINE1 (or similar) macro with the "KPATCH_" prefixed version. Fixes: #1171 Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
- Loading branch information
Showing
2 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
#ifndef __KPATCH_SYSCALL_H_ | ||
#define __KPATCH_SYSCALL_H_ | ||
|
||
#include "kpatch-macros.h" | ||
|
||
/* | ||
* These kpatch-specific syscall definition macros can be used for patching a | ||
* syscall. | ||
* | ||
* Attempting to patch a syscall typically results in an error, due to a | ||
* missing fentry hook in the inner __do_sys##name() function. The fentry hook | ||
* is missing because of the 'inline' annotation, which invokes 'notrace'. | ||
* | ||
* These macros are copied almost verbatim from the kernel, the main difference | ||
* being a 'kpatch' prefix added to the __do_sys##name() function name. This | ||
* causes kpatch-build to treat it as a new function (due to | ||
* its new name), and its caller __se_sys##name() function is inlined by its own | ||
* caller __x64_sys##name() function, which has an fentry hook. | ||
* To patch a syscall, just replace the use of the SYSCALL_DEFINE1 (or similar) | ||
* macro with the "KPATCH_" prefixed version. | ||
*/ | ||
|
||
#define KPATCH_IGNORE_SYSCALL_SECTIONS \ | ||
KPATCH_IGNORE_SECTION("__syscalls_metadata"); \ | ||
KPATCH_IGNORE_SECTION("_ftrace_events") | ||
|
||
#define KPATCH_SYSCALL_DEFINE1(name, ...) KPATCH_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) | ||
#define KPATCH_SYSCALL_DEFINE2(name, ...) KPATCH_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) | ||
#define KPATCH_SYSCALL_DEFINE3(name, ...) KPATCH_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) | ||
#define KPATCH_SYSCALL_DEFINE4(name, ...) KPATCH_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) | ||
#define KPATCH_SYSCALL_DEFINE5(name, ...) KPATCH_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) | ||
#define KPATCH_SYSCALL_DEFINE6(name, ...) KPATCH_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) | ||
|
||
#define KPATCH_SYSCALL_DEFINEx(x, sname, ...) \ | ||
KPATCH_IGNORE_SYSCALL_SECTIONS; \ | ||
__KPATCH_SYSCALL_DEFINEx(x, sname, __VA_ARGS__) | ||
|
||
#ifdef CONFIG_X86_64 | ||
|
||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) | ||
|
||
#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ | ||
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ | ||
__X64_SYS_STUBx(x, name, __VA_ARGS__) \ | ||
__IA32_SYS_STUBx(x, name, __VA_ARGS__) \ | ||
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ | ||
{ \ | ||
long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ | ||
return ret; \ | ||
} \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) | ||
|
||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) | ||
|
||
#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ | ||
asmlinkage long __x64_sys##name(const struct pt_regs *regs); \ | ||
ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO); \ | ||
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ | ||
asmlinkage long __x64_sys##name(const struct pt_regs *regs) \ | ||
{ \ | ||
return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\ | ||
} \ | ||
__IA32_SYS_STUBx(x, name, __VA_ARGS__) \ | ||
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ | ||
{ \ | ||
long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ | ||
return ret; \ | ||
} \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) | ||
|
||
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) */ | ||
|
||
#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ | ||
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ | ||
static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ | ||
asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ | ||
{ \ | ||
long ret = __kpatch_SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ | ||
return ret; \ | ||
} \ | ||
SYSCALL_ALIAS(sys##name, SyS##name); \ | ||
static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) | ||
|
||
#endif /* LINUX_VERSION_CODE */ | ||
|
||
|
||
#elif defined(CONFIG_PPC64) | ||
|
||
#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ | ||
__diag_push(); \ | ||
__diag_ignore(GCC, 8, "-Wattribute-alias", \ | ||
"Type aliasing is used to sanitize syscall arguments");\ | ||
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ | ||
__attribute__((alias(__stringify(__se_sys##name)))); \ | ||
ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ | ||
asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ | ||
asmlinkage long __attribute__((optimize("-fno-optimize-sibling-calls")))\ | ||
__se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ | ||
{ \ | ||
long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ | ||
return ret; \ | ||
} \ | ||
__diag_pop(); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) | ||
|
||
|
||
#elif defined(CONFIG_S390) | ||
|
||
#define __KPATCH_S390_SYS_STUBx(x, name, ...) \ | ||
long __s390_sys##name(struct pt_regs *regs); \ | ||
ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ | ||
long __s390_sys##name(struct pt_regs *regs) \ | ||
{ \ | ||
long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs, \ | ||
__SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
return ret; \ | ||
} | ||
|
||
#define __KPATCH_SYSCALL_DEFINEx(x, name, ...) \ | ||
__diag_push(); \ | ||
__diag_ignore(GCC, 8, "-Wattribute-alias", \ | ||
"Type aliasing is used to sanitize syscall arguments"); \ | ||
long __s390x_sys##name(struct pt_regs *regs) \ | ||
__attribute__((alias(__stringify(__se_sys##name)))); \ | ||
ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ | ||
long __se_sys##name(struct pt_regs *regs); \ | ||
__KPATCH_S390_SYS_STUBx(x, name, __VA_ARGS__) \ | ||
long __se_sys##name(struct pt_regs *regs) \ | ||
{ \ | ||
long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs, \ | ||
__SC_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ | ||
__MAP(x,__SC_TEST,__VA_ARGS__); \ | ||
return ret; \ | ||
} \ | ||
__diag_pop(); \ | ||
static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) | ||
|
||
|
||
#endif /* CONFIG_X86_64 */ | ||
|
||
#endif /* __KPATCH_SYSCALL_H_ */ |