Linux的系统调用在内核中的入口函数都是 sys_xxx ,但是我们在内核源码去搜索时,无法找到 sys_xxx 的函数定义,这是因为Linux的系统调用对应的函数全部都是由 SYSCALL_DEFINE 相关的宏来定义的。
send 系统调⽤使用了SYACALL_DEFINE4 宏定义,4 表示系统调用了4个参数,分别为fd,buff,len,flags。函数内部返回了sys_sendto函数,即send系统调用真正使用的是sendto系统调用。
//file: net/socket.c
SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, unsigned int, flags)
{
return sys_sendto(fd, buff, len, flags, NULL, 0);
}
SYACALL_DEFINE4 使用了 SYSCALL_DEFINEx 宏定义,将参数数量作为一个新的参数。
- ##表示将左右的符号拼接在一起。
- __VA_ARGS__表示前面...里面的可变参数。
- SYSCALL_METADATA 是调试时跟踪系统调用的,无需关注。
- asmlinkage 表示将函数参数存放在局部栈中(x86常用),FASTCALL表示将函数参数存放在R0~R4通用寄存器中(arm常用)。通常是系统调用的函数需要加armlinkage。
//file: include/linux/syscalls.h
#define SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \
asmlinkage long sys_##sname(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
//代码展开
SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
SYSCALL_DEFINEx(4, _send, int, fd, void __user *, buff, size_t, len, unsigned int, flags)
SYSCALL_DEFINEx 为主要的实现过程,包含三个函数,sys,SyS,SYSC。其中,sys为SyS的别名,SyS调用了SYSC,SYSC后面没有分号,其定义在调用时给出。因此,sys/SyS->SYSC,前面的封装并不是调用过程。
//file: include/linux/syscalls.h
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ // 声明sys_send()
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ // 声明SYSC_send()
asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ // 定义SyS_send()
{ \
long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ // 调用SYSC_send()
__MAP(x,__SC_TEST,__VA_ARGS__); \
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
return ret; \
} \
SYSCALL_ALIAS(sys##name, SyS##name); \ // 设置sys_send是SyS_send的别名
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) // 定义SYSC_send()
//代码展开
SYSCALL_DEFINEx(4, _send, int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
asmlinkage long sys_send(__MAP(4, __SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags));
static inline long SYSC_send(__MAP(4, __SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags));
asmlinkage long SyS_send(__MAP(4, __SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags))
{
long ret = SYSC_send(__MAP(4,__SC_CAST,int, fd, void __user *, buff, size_t, len, unsigned int, flags));
__MAP(4,__SC_TEST,int, fd, void __user *, buff, size_t, len, unsigned int, flags);
return ret;
}
SYSCALL_ALIAS(sys_send, SyS_send);
static inline long SYSC_send(__MAP(4, __SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags))
还有一些其他的宏定义,主要是用来对宏参数进行变换。
//file: include/linux/syscalls.h
#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
#define __SC_DECL(t, a) t a
#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
#define __SC_CAST(t, a) (t) a
#define __SC_ARGS(t, a) a
#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
#ifdef CONFIG_FTRACE_SYSCALLS
#define __SC_STR_ADECL(t, a) #a
#define __SC_STR_TDECL(t, a) #t
//代码展开
__MAP(4, __SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
__MAP4(__SC_DECL, int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
__SC_DECL(int, fd), __MAP3(__SC_DECL, void __user *, buff, size_t, len, unsigned int, flags) ->
__SC_DECL(int, fd), __SC_DECL(void __user *, buff), __SC_DECL(size_t, len), __SC_DECL(unsigned int, flags) ->
int fd, void __user * buff, size_t len, unsigned int flags
__MAP(4,__SC_CAST,int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
(int)fd, (void __user *)buff, (size_t)len, (unsigned int)flags
__MAP(4,__SC_TEST,int, fd, void __user *, buff, size_t, len, unsigned int, flags) ->
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(int) && sizeof(int) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(void __user *) && sizeof(void __user *) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(size_t) && sizeof(size_t) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(unsigned int) && sizeof(unsigned int) > sizeof(long));
最终得到的代码如下。使用多次宏替换,是为了将系统调用的参数统一变为了使用long类型来接收,然后再强转为本来参数类型。这样做的目的是解决Linux的CVE-2009-0029漏洞,该漏洞的大概意思是说:在Linux 2.6.28及以前版本的内核中,IBM/S390、PowerPC、Sparc64以及MIPS 架构64位平台的ABI要求在系统调用时,用户空间程序将系统调用中32位的参数存放在64位的寄存器中要做到正确的符号扩展,但是用户空间程序却不能保证做到这点,这样就会可以通过向有漏洞的系统调用传送特制参数便可以导致系统崩溃或获得权限提升。
asmlinkage long sys_send(int fd, void __user * buff, size_t len, unsigned int flags); // 声明sys_send()
static inline long SYSC_send(int fd, void __user * buff, size_t len, unsigned int flags); // 声明SYSC_send()
asmlinkage long SyS_send(long fd, long buff, long len, long flags) // 定义SyS_send()
{
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(int) && sizeof(int) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(void __user *) && sizeof(void __user *) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(size_t) && sizeof(size_t) > sizeof(long));
(void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(unsigned int) && sizeof(unsigned int) > sizeof(long));
return (long) SYSC_send((int) family, (int) type, (int) protocol); // 调用SYSC_send()
}
SYSCALL_ALIAS(sys_send, SyS_send); // 设置sys_send是SyS_send的别名
static inline long SYSC_send(int fd, void __user * buff, size_t len, unsigned int flags) // 定义SYSC_send()
标签:__,调用,SYACALL,int,void,send,long,SC,DEFINE
From: https://www.cnblogs.com/jpdeng/p/16753970.html