在看linux-5.0 sendto 函数实现时,__sys_sendto函数中调用了import_single_range函数.
[net/socket.c]
int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags,
struct sockaddr __user *addr, int addr_len)
{
struct socket *sock;
struct sockaddr_storage address;
int err;
struct msghdr msg;
struct iovec iov;
int fput_needed;
err = import_single_range(WRITE, buff, len, &iov, &msg.msg_iter);
if (unlikely(err))
return err;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
msg.msg_name = NULL;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_namelen = 0;
if (addr) {
err = move_addr_to_kernel(addr, addr_len, &address);
if (err < 0)
goto out_put;
msg.msg_name = (struct sockaddr *)&address;
msg.msg_namelen = addr_len;
}
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;
msg.msg_flags = flags;
err = sock_sendmsg(sock, &msg);
out_put:
fput_light(sock->file, fput_needed);
out:
return err;
}
查看import_single_range实现:
[lib/iov_iter.c]
int import_single_range(int rw, void __user *buf, size_t len,
struct iovec *iov, struct iov_iter *i)
{
if (len > MAX_RW_COUNT)
len = MAX_RW_COUNT;
if (unlikely(!access_ok(buf, len)))
return -EFAULT;
iov->iov_base = buf;
iov->iov_len = len;
iov_iter_init(i, rw, iov, 1, len);
return 0;
}
void iov_iter_init(struct iov_iter *i, unsigned int direction,
const struct iovec *iov, unsigned long nr_segs,
size_t count)
{
WARN_ON(direction & ~(READ | WRITE));
direction &= READ | WRITE;
/* It will get better. Eventually... */
if (uaccess_kernel()) {
i->type = ITER_KVEC | direction;
i->kvec = (struct kvec *)iov;
} else { //跑到这里
i->type = ITER_IOVEC | direction;
i->iov = iov;
}
i->nr_segs = nr_segs;
i->iov_offset = 0;
i->count = count;
}
-----------------------------------------------------
uaccess_kernel()实现:以arm64为例
[include/linux/uaccess.h]
#define uaccess_kernel() segment_eq(get_fs(), KERNEL_DS)
[arch/arm64/include/asm/uaccess.h]
#define get_fs() (current_thread_info()->addr_limit)
[arch/arm64/include/asm/uaccess.h]
#define segment_eq(a, b) ((a) == (b))
[arch/arm64/include/asm/processor.h]
#define KERNEL_DS UL(-1)
#define USER_DS ((UL(1) << MAX_USER_VA_BITS) - 1)
[include/linux/thread_info.h]
#define current_thread_info() ((struct thread_info *)current)
[arch/arm64/include/asm/thread_info.h]
struct thread_info {
unsigned long flags; /* low level flags */
mm_segment_t addr_limit; /* address limit */
......
}
谁赋值给addr_limit?
查找到两处给addr_limit赋值:
1.[arch/arm64/include/asm/uaccess.h]
static inline void set_fs(mm_segment_t fs)
{
current_thread_info()->addr_limit = fs;
....
}
2.[arch/arm64/include/asm/thread_info.h]
#define INIT_THREAD_INFO(tsk) \
{ \
.flags = _TIF_FOREIGN_FPSTATE, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
}
调用栈:
el0_svc
el0_svc_handler
el0_svc_common
invoke_syscall
__invoke_syscall
__arm64_sys_sendto
__do_sys_sendto
__sys_sendto
import_single_range
iov_iter_init
uaccess_kernel返回值是false
初始化后的结构体如下: