首页
社区
课程
招聘
[原创]copy_from_user与copy_to_user调用流程分析
发表于: 2020-10-4 16:29 9956

[原创]copy_from_user与copy_to_user调用流程分析

2020-10-4 16:29
9956

位置在:/include/linux/uaccess.h 中

这是copy_from_user函数底层真正干活的韩素:/include/linux/uaccess.h

然后首先还是调用might_fault()
底层实现如下:

其中的current_task是一个结构体指针:

指向:

this_cpu_read_stable在最底层时到达这里:

可以看到它实际上这个switch是通过sizeof(var)来做分支跳转的。
然而这里的var实际上就是:current_task 这一结构体指针,所以命中case 8.
我们单独看一下case 8:

将内联汇编展开:

这行代码的含义为将约束输入部分必须为有效的地址&(kernel_stack)(p约束), 将段寄存器gs加偏移量&(kernel_stack)通过寄存器(r约束)赋值给 pfo_ret__.
那么实际上就是在做地址约束

你会发现实际上get_current返回了一个struct task_struct *类型的指针or地址。而这个东西最终成为了我们的current指针。用来检测:current->pagefault_disabled != 0

task_struct结构体我早早就听过他的大名(众所周知,是用来做进程管理的一个非常重要的结构体),正巧借着这个机会来了解一下
由于这个结构体在源码中竟然长达600多行。我们这里只谈:pagefault_disabled 。
实际上pagefault_disabled这一成员。他代表着:是否缺页中断处理函数被禁用。回到might_fault函数中的他也是这个作用。

浅析Linux下的task_struct结构体

追踪一下:
首先是一个宏定义,看起来是获取了fs寄存器和kenel ds寄存器

最终到达如下位置:

在这里应该就是 判断fs和kernel ds是否相等。返沪bool。

might_sleep()用于调试在不预期睡眠的地方是否会正的睡眠。如果没有定义CONFIG_DEBUG_ATOMIC_SLEEP的话,might_sleep就是一个空函数,所以平常看code的时候可以忽略。
如果定义的CONFIG_DEBUG_ATOMIC_SLEEP的话,如果在包含might_sleep的函数中睡眠了,则会打印当前的callstack

看一下mm。是一个指向mm_struct的指针:Linux进程地址管理之mm_struct

无论是内核线程还是用户进程,对于内核来说,无非都是task_struct这个数据结构的一个实例而已,task_struct被称为进程描述符(process descriptor),因为它记录了这个进程所有的context。其中有一个被称为'内存描述符‘(memory descriptor)的数据结构mm_struct,抽象并描述了Linux视角下管理进程地址空间的所有信息。

当mm指针不为空时:
调用:might_lock_read(&current->mm->mmap_lock);
最终到达:

这个我没有再往下分析了,应该是检查一下然后请求/释放锁?可能是针对缓冲区的我猜测。

用来检查用户空间或内核空间的指针是否有效(取决于你传进去的addr是to还是from指针)。如果有效则进行如下操作:

主要是检查内存访问的有效性。

这里做了一件事情是:根据cpu的特性来选择调用哪个函数,也就是说:

关于_copy_to_user和_copy_from_user.实际他们的实现是极为相似和对称的。

只不过check的指针可能变了一下,然后参数的位置调换了一下。底层实现几乎可以说一模一样。

https://cloud.tencent.com/developer/article/1635127

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
    if (likely(check_copy_size(to, n, false)))
        n = _copy_from_user(to, from, n);
    return n;
}
static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
    if (likely(check_copy_size(to, n, false)))
        n = _copy_from_user(to, from, n);
    return n;
}
static inline __must_check unsigned long
_copy_from_user(void *to, const void __user *from, unsigned long n)
{
    unsigned long res = n;
    might_fault();
    if (likely(access_ok(from, n))) {
        instrument_copy_from_user(to, from, n);
        res = raw_copy_from_user(to, from, n);
    }
    if (unlikely(res))
        memset(to + (n - res), 0, res);
    return res;
}
static inline __must_check unsigned long
_copy_from_user(void *to, const void __user *from, unsigned long n)
{
    unsigned long res = n;
    might_fault();
    if (likely(access_ok(from, n))) {
        instrument_copy_from_user(to, from, n);
        res = raw_copy_from_user(to, from, n);
    }
    if (unlikely(res))
        memset(to + (n - res), 0, res);
    return res;
}
void __might_fault(const char *file, int line)
{
    /*
     * Some code (nfs/sunrpc) uses socket ops on kernel memory while
     * holding the mmap_lock, this is safe because kernel memory doesn't
     * get paged out, therefore we'll never actually fault, and the
     * below annotations will generate false positives.
     */
    if (uaccess_kernel())
        return;
    if (pagefault_disabled())
        return;
    __might_sleep(file, line, 0);
#if defined(CONFIG_DEBUG_ATOMIC_SLEEP)
    if (current->mm)
        might_lock_read(&current->mm->mmap_lock);
#endif
}
void __might_fault(const char *file, int line)
{
    /*
     * Some code (nfs/sunrpc) uses socket ops on kernel memory while
     * holding the mmap_lock, this is safe because kernel memory doesn't
     * get paged out, therefore we'll never actually fault, and the
     * below annotations will generate false positives.
     */
    if (uaccess_kernel())
        return;
    if (pagefault_disabled())
        return;
    __might_sleep(file, line, 0);
#if defined(CONFIG_DEBUG_ATOMIC_SLEEP)
    if (current->mm)
        might_lock_read(&current->mm->mmap_lock);
#endif
}
/*
 * Is the pagefault handler disabled? If so, user access methods will not sleep.
 */
static inline bool pagefault_disabled(void)
{
    return current->pagefault_disabled != 0;
}
/*
 * Is the pagefault handler disabled? If so, user access methods will not sleep.
 */
static inline bool pagefault_disabled(void)
{
    return current->pagefault_disabled != 0;
}
#define current get_current()
#define current get_current()
static __always_inline struct task_struct *get_current(void)
{
    return this_cpu_read_stable(current_task);
}
static __always_inline struct task_struct *get_current(void)
{
    return this_cpu_read_stable(current_task);
}
struct lima_sched_task *current_task;
struct lima_sched_task *current_task;
struct lima_sched_task {
    struct drm_sched_job base;
 
    struct lima_vm *vm;
    void *frame;
 
    struct xarray deps;
    unsigned long last_dep;
 
    struct lima_bo **bos;
    int num_bos;
 
    bool recoverable;
    struct lima_bo *heap;
 
    /* pipe fence */
    struct dma_fence *fence;
};
struct lima_sched_task {
    struct drm_sched_job base;
 
    struct lima_vm *vm;
    void *frame;
 
    struct xarray deps;
    unsigned long last_dep;
 
    struct lima_bo **bos;
    int num_bos;
 
    bool recoverable;
    struct lima_bo *heap;
 
    /* pipe fence */
    struct dma_fence *fence;
};
#define percpu_stable_op(op, var)            \
({                            \
    typeof(var) pfo_ret__;                \
    switch (sizeof(var)) {                \
    case 1:                        \
        asm(op "b "__percpu_arg(P1)",%0"    \
            : "=q" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 2:                        \
        asm(op "w "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 4:                        \
        asm(op "l "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 8:                        \
        asm(op "q "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    default: __bad_percpu_size();            \
    }                        \
    pfo_ret__;                    \
})
#define percpu_stable_op(op, var)            \
({                            \
    typeof(var) pfo_ret__;                \
    switch (sizeof(var)) {                \
    case 1:                        \
        asm(op "b "__percpu_arg(P1)",%0"    \
            : "=q" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 2:                        \
        asm(op "w "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 4:                        \
        asm(op "l "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    case 8:                        \
        asm(op "q "__percpu_arg(P1)",%0"    \
            : "=r" (pfo_ret__)            \
            : "p" (&(var)));            \
        break;                    \
    default: __bad_percpu_size();            \
    }                        \
    pfo_ret__;                    \
})
asm(op "q "__percpu_arg(P1)",%0"    \
    : "=r" (pfo_ret__)            \
    : "p" (&(var)));            \
break;
asm(op "q "__percpu_arg(P1)",%0"    \
    : "=r" (pfo_ret__)            \
    : "p" (&(var)));            \
break;
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(kernel_stack)));
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(kernel_stack)));
 
#define uaccess_kernel() segment_eq(get_fs(), KERNEL_DS)
#define uaccess_kernel() segment_eq(get_fs(), KERNEL_DS)
#define segment_eq(a, b)    ((a).seg == (b).seg)
#define segment_eq(a, b)    ((a).seg == (b).seg)
void __might_sleep(const char *file, int line, int preempt_offset)
{
    /*
     * Blocking primitives will set (and therefore destroy) current->state,
     * since we will exit with TASK_RUNNING make sure we enter with it,
     * otherwise we will destroy state.
     */
    WARN_ONCE(current->state != TASK_RUNNING && current->task_state_change,
            "do not call blocking ops when !TASK_RUNNING; "
            "state=%lx set at [<%p>] %pS\n",
            current->state,
            (void *)current->task_state_change,
            (void *)current->task_state_change);
 
    ___might_sleep(file, line, preempt_offset);
}
EXPORT_SYMBOL(__might_sleep);
void __might_sleep(const char *file, int line, int preempt_offset)
{
    /*
     * Blocking primitives will set (and therefore destroy) current->state,
     * since we will exit with TASK_RUNNING make sure we enter with it,
     * otherwise we will destroy state.
     */
    WARN_ONCE(current->state != TASK_RUNNING && current->task_state_change,
            "do not call blocking ops when !TASK_RUNNING; "
            "state=%lx set at [<%p>] %pS\n",
            current->state,
            (void *)current->task_state_change,
            (void *)current->task_state_change);
 
    ___might_sleep(file, line, preempt_offset);
}
EXPORT_SYMBOL(__might_sleep);
 
typedef struct {
    struct mm_struct *mm;
} temp_mm_state_t;
typedef struct {
    struct mm_struct *mm;
} temp_mm_state_t;
# define might_lock_read(lock)                         \
do {                                    \
    typecheck(struct lockdep_map *, &(lock)->dep_map);        \
    lock_acquire(&(lock)->dep_map, 0, 0, 1, 1, NULL, _THIS_IP_);    \
    lock_release(&(lock)->dep_map, _THIS_IP_);            \
} while (0)
# define might_lock_read(lock)                         \
do {                                    \
    typecheck(struct lockdep_map *, &(lock)->dep_map);        \

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//