我可以将指针传递给Linux内核空间以获取__user参数吗?

时间:2015-04-27 16:02:52

标签: c pointers linux-kernel

我已经读过Linux内核中的__user说明符,用于标记Sparse的函数参数以检查函数参数。

特别是,给定

int foo1( char * buf );

不得将指针传递给buf的用户空间。

反过来怎么样?在

int foo2( char __user * buf );

可能必须我传递用户提供的指针?我猜测前者是因为用户提供的指针确实可以是任何东西,但我还没有找到__user的正式规范。

2 个答案:

答案 0 :(得分:5)

请记住,__user从根本上意味着指针指向用户地址空间。它不一定必须由用户提供,但它确实意味着它不能是指向内核内存的指针。

在某些体系结构上,例如x86和x86_64,内核和用户内存存在于同一地址空间中,并且仅通过边界(即x86中的3G以上内核)进行区分。在这些体系结构中,__user主要用作注释,以提醒开发人员应该小心对待它。然而,这并非总是如此!一些体系结构,例如PowerPC,实际上完全为内核使用单独的地址空间;在这些体系结构上,__user具有新的重要性,因为它表明必须使用特殊函数(如copy_from_user来访问指针。正常的内存访问根本不起作用,因为它会尝试取消引用内核地址空间中的指针,它可能无效。

答案 1 :(得分:3)

copy_from_user指针可能会传递给copy_to_user/* 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) 或类似内容。

在x86-64中,这些是在copy_user_64.S

中实现的
cmpq TI_addr_limit(%rax),%rcx

请注意current_thread_info()->addr_limit,以确保您的指针小于或等于USER_DS

此限制由set_fs()宏设置,该宏在许多地方都被调用。特别是,可以使用#define USER_DS MAKE_MM_SEG(TASK_SIZE_MAX) 调用它,它定义为:

/*
 * User space process size. 47bits minus one guard page.
 * ...
 */
#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

TASK_SIZE_MAX定义为:

__user

在x86-64中,内核映射到高(或负)虚拟地址空间。因此,内核指针将无法通过此检查。

总之,我认为{ "id": 1, "name": "MyTeam5", "players": [ { "name": "player5" }, { "name":"player10" } ] } 指针不能由用户实际提供,但保证是有效的用户空间地址。我认为唯一的限制是你必须确保你当前环境中你传递的指针有效(这取决于我们所说的,可能会改变的地方)任何时候)。