macros: add syscall patching macros

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>
This commit is contained in:
Josh Poimboeuf 2021-04-16 15:18:47 -05:00
parent e4c0bb9999
commit e9c0b67862
2 changed files with 156 additions and 0 deletions

View File

@ -4,6 +4,7 @@
#include <linux/compiler.h>
#include <linux/jiffies.h>
#include <linux/version.h>
#include "kpatch-syscall.h"
/* upstream 33def8498fdd "treewide: Convert macro and uses of __section(foo) to __section("foo")" */
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)

155
kmod/patch/kpatch-syscall.h Normal file
View File

@ -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_ */