我可以将指向 linux 内核 space 的指针传递给 __user 参数吗?
Can I pass a pointer to linux kernel space for a __user parameter?
我读到 Linux 内核中的 __user
说明符用于标记 Sparse 的函数参数以检查函数参数。
特别是给定
int foo1( char * buf );
不得为 buf
.
传递指向用户 space 的指针
反过来呢?在
int foo2( char __user * buf );
可以 或 必须 我传递一个用户提供的指针?我猜是前者,因为用户提供的指针实际上可以是任何东西,但我还没有在任何地方找到 __user 的正式规范。
一个 __user
指针可能会传递给 copy_from_user
、copy_to_user
或类似的东西。
在 x86-64 中,这些在 copy_user_64.S
中实现:
/* Standard copy_to_user with segment limit checking */
ENTRY(_copy_to_user)
CFI_STARTPROC
GET_THREAD_INFO(%rax)
movq %rdi,%rcx
addq %rdx,%rcx
jc bad_to_user
cmpq TI_addr_limit(%rax),%rcx
ja bad_to_user
ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
copy_user_generic_unrolled,copy_user_generic_string, \
copy_user_enhanced_fast_string
CFI_ENDPROC
ENDPROC(_copy_to_user)
/* Standard copy_from_user with segment limit checking */
ENTRY(_copy_from_user)
CFI_STARTPROC
GET_THREAD_INFO(%rax)
movq %rsi,%rcx
addq %rdx,%rcx
jc bad_from_user
cmpq TI_addr_limit(%rax),%rcx
ja bad_from_user
ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
copy_user_generic_unrolled,copy_user_generic_string, \
copy_user_enhanced_fast_string
CFI_ENDPROC
ENDPROC(_copy_from_user)
注意 cmpq TI_addr_limit(%rax),%rcx
,这将确保您的指针小于或等于 current_thread_info()->addr_limit
。
此限制由 set_fs()
宏设置,该宏在多处调用。特别是,它可以用 USER_DS
调用,定义为:
#define USER_DS MAKE_MM_SEG(TASK_SIZE_MAX)
TASK_SIZE_MAX
定义为:
/*
* User space process size. 47bits minus one guard page.
* ...
*/
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
在 x86-64 中,内核映射到高(或负)虚拟地址 space。所以内核指针将无法通过此检查。
总而言之,我认为 __user
指针实际上不能由用户 提供 ,但保证是有效的用户-space地址。我认为唯一的限制是你必须确保你传递的指针在你当前的上下文中是 valid (这取决于我们谈论的地方,可以随时改变时间)。
请记住,__user
的根本意思是指针 指向用户地址 space。它不一定必须由用户提供,但这确实意味着它不能是指向内核内存的指针。
在某些架构上,例如 x86 和 x86_64,内核和用户内存位于同一地址 space,并且仅通过边界区分(即 x86 中 3G 以上的内核) .在这些架构上,__user
主要用作注解,提醒开发者谨慎对待。然而,这并非总是如此!某些架构,例如 PowerPC,实际上完全为内核使用单独的地址 space;在这些体系结构上,__user
具有新的重要性,因为它表明必须使用特殊函数(如 copy_from_user
) 来访问指针。正常的内存访问根本不起作用,因为它会尝试取消引用内核地址 space 中的指针,它可能无效。
我读到 Linux 内核中的 __user
说明符用于标记 Sparse 的函数参数以检查函数参数。
特别是给定
int foo1( char * buf );
不得为 buf
.
反过来呢?在
int foo2( char __user * buf );
可以 或 必须 我传递一个用户提供的指针?我猜是前者,因为用户提供的指针实际上可以是任何东西,但我还没有在任何地方找到 __user 的正式规范。
一个 __user
指针可能会传递给 copy_from_user
、copy_to_user
或类似的东西。
在 x86-64 中,这些在 copy_user_64.S
中实现:
/* Standard copy_to_user with segment limit checking */
ENTRY(_copy_to_user)
CFI_STARTPROC
GET_THREAD_INFO(%rax)
movq %rdi,%rcx
addq %rdx,%rcx
jc bad_to_user
cmpq TI_addr_limit(%rax),%rcx
ja bad_to_user
ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
copy_user_generic_unrolled,copy_user_generic_string, \
copy_user_enhanced_fast_string
CFI_ENDPROC
ENDPROC(_copy_to_user)
/* Standard copy_from_user with segment limit checking */
ENTRY(_copy_from_user)
CFI_STARTPROC
GET_THREAD_INFO(%rax)
movq %rsi,%rcx
addq %rdx,%rcx
jc bad_from_user
cmpq TI_addr_limit(%rax),%rcx
ja bad_from_user
ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,X86_FEATURE_ERMS, \
copy_user_generic_unrolled,copy_user_generic_string, \
copy_user_enhanced_fast_string
CFI_ENDPROC
ENDPROC(_copy_from_user)
注意 cmpq TI_addr_limit(%rax),%rcx
,这将确保您的指针小于或等于 current_thread_info()->addr_limit
。
此限制由 set_fs()
宏设置,该宏在多处调用。特别是,它可以用 USER_DS
调用,定义为:
#define USER_DS MAKE_MM_SEG(TASK_SIZE_MAX)
TASK_SIZE_MAX
定义为:
/*
* User space process size. 47bits minus one guard page.
* ...
*/
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
在 x86-64 中,内核映射到高(或负)虚拟地址 space。所以内核指针将无法通过此检查。
总而言之,我认为 __user
指针实际上不能由用户 提供 ,但保证是有效的用户-space地址。我认为唯一的限制是你必须确保你传递的指针在你当前的上下文中是 valid (这取决于我们谈论的地方,可以随时改变时间)。
请记住,__user
的根本意思是指针 指向用户地址 space。它不一定必须由用户提供,但这确实意味着它不能是指向内核内存的指针。
在某些架构上,例如 x86 和 x86_64,内核和用户内存位于同一地址 space,并且仅通过边界区分(即 x86 中 3G 以上的内核) .在这些架构上,__user
主要用作注解,提醒开发者谨慎对待。然而,这并非总是如此!某些架构,例如 PowerPC,实际上完全为内核使用单独的地址 space;在这些体系结构上,__user
具有新的重要性,因为它表明必须使用特殊函数(如 copy_from_user
) 来访问指针。正常的内存访问根本不起作用,因为它会尝试取消引用内核地址 space 中的指针,它可能无效。