-
-
[原创]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函数中的他也是这个作用。
追踪一下:
首先是一个宏定义,看起来是获取了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(¤t->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(¤t
-
>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(¤t
-
>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直播授课