access_ok()

access_ok函数详解

转自:http://blog.sina.com.cn/s/blog_6c46cb8601013z31.html

access_ok() 函数是用来代替老版本的 verify_area() 函数的。它的作用也是检查用户空间指针是否可用。


函数原型
access_ok (type, addr, size);

变量说明
type   :   访问类型,其值可为 VERIFY_READ 或者 VERIFY_WRITE 。注意,VERIFY_WRITE 是 VERIFY_READ 的超集 -- 如果可以安全的写内存块,那么自然也总能读到内存块。

addr    用户空间的指针变量,其指向一个要检查的内存块开始处。

size   :   要检查内存块的大小。

返回值
此函数检查用户空间中的内存块是否可用。如果可用,则返回真(非0值),否则返回假 (0) 。

代码片段
if (_IOC_DIR(cmd& _IOC_READ)
    err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd& _IOC_WRITE)
    err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err)
    return -EFAULT;

<think> 我们遇到了一个问题:在包含`linux/uaccess.h`头文件后调用`access_ok`函数时,编译器报告该函数未声明(undeclared)。这通常意味着编译器在包含的头文件中没有找到该函数的声明。 ### 可能的原因和解决方法: 1. **内核版本差异**:`access_ok`函数在不同的内核版本中可能位于不同的头文件中。在较新的内核版本(例如4.xx之后)中,这个函数可能被移动到了另一个头文件(比如`linux/access_ok.h`)或者被改名了。 2. **函数名称变更**:在某些版本中,函数名可能有大小写变化或完全不同的命名。例如,在一些版本中,函数名为`access_ok`,而在另一些版本中可能是`access_ok_wrapper`或是其他形式。 3. **配置选项影响**:某些内核配置选项可能会影响该函数的可用性。例如,如果内核配置为不使用该功能,那么相关的函数可能不会被定义。 4. **头文件包含顺序**:可能由于头文件包含的顺序问题,导致某些必要的宏或类型定义未被正确包含,从而影响了`access_ok`的声明。 ### 解决步骤: 1. **检查内核版本**: 使用命令`uname -r`查看当前内核版本。 2. **查找函数定义**: 在Linux内核源代码中搜索`access_ok`函数,以确定它在哪个头文件中声明。可以使用如下命令(假设内核源代码在`/usr/src/linux`): ```bash grep -r "access_ok" /usr/src/linux/include/linux/ ``` 或者使用`cscope`等工具进行搜索。 3. **根据内核版本调整**: - 对于较旧的内核(如2.6.x),通常`access_ok`在`asm/uaccess.h`中声明。 - 对于较新的内核(如4.x及以上),可能需要包含`linux/uaccess.h`,但也要注意它可能被内联或者由体系结构特定的头文件提供。 4. **体系结构相关的头文件**: 由于`access_ok`函数是体系结构相关的,因此它通常定义在体系结构特定的头文件中。例如,在x86架构上,它可能在`arch/x86/include/asm/uaccess.h`中。但是,通用的`linux/uaccess.h`头文件通常会包含正确的体系结构头文件。 5. **函数名大小写**: 注意函数名的大小写,在Linux内核中,函数名通常是小写,但也要确认是否拼写正确(例如,是`access_ok`还是`access_ok`?实际上,函数名是`access_ok`,中间是下划线)。 6. **使用正确的函数参数**: 确保调用时参数正确。`access_ok`有两个参数:第一个是访问类型(如`VERIFY_READ`或`VERIFY_WRITE`),第二个是用户空间地址和大小。在较新内核中,访问类型可能被`VERIFY_READ`和`VERIFY_WRITE`取代,但注意这些宏也可能被移除,而直接使用`ACCESS_READ`和`ACCESS_WRITE`?实际上,在较新内核中(例如5.0+),`access_ok`的参数已经改变,不再使用`VERIFY_READ`等,而是直接传递一个指向用户空间的指针和大小,以及访问类型(如`READ`或`WRITE`)。 ### 最新变化(重要): 在Linux内核5.0及以上版本中,`access_ok`函数被重新设计。现在它只需要两个参数: ```c access_ok(void __user *addr, unsigned long size); ``` 并且,不再需要传递`VERIFY_READ`或`VERIFY_WRITE`。同时,`VERIFY_READ`和`VERIFY_WRITE`宏已经被移除。 因此,如果您使用的是较新的内核(>=5.0),那么: - 确保您使用的是新的两个参数的`access_ok`。 - 并且,注意头文件依然是`#include <linux/uaccess.h>`。 ### 示例代码(内核>=5.0): ```c #include <linux/uaccess.h> if (access_ok(ptr, size)) { // 可以访问 } ``` ### 总结: - 首先确认内核版本。 - 如果内核版本低于5.0,则使用旧版三个参数的形式:`access_ok(type, addr, size)`,并且确保包含正确的头文件(通常是`linux/uaccess.h`或`asm/uaccess.h`)。 - 如果内核版本>=5.0,则使用两个参数的形式:`access_ok(addr, size)`。 如果仍然找不到`access_ok`,可能是由于头文件包含路径问题或者内核配置问题。请检查内核配置中是否启用了相关的功能。 ### 实际应用中的建议: 在编写内核模块时,为了兼容不同版本的内核,可以使用条件编译: ```c #include <linux/version.h> #include <linux/uaccess.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) if (access_ok(ptr, size)) { // ... } #else if (access_ok(VERIFY_WRITE, ptr, size)) { // ... } #endif ``` 但要注意,`VERIFY_WRITE`等宏在老版本中也可能有不同的定义,因此需要仔细测试。 §§相关问题§§ 1. 在内核模块编程中,如何根据不同的内核版本进行条件编译? 2. 除了access_ok函数,Linux内核中还有哪些常用的用户空间内存访问函数? 3. 在内核模块中,为什么需要特别注意用户空间指针的访问? 4. 如何在内核模块中安全地从用户空间复制数据到内核空间? 5. 当内核函数或宏在不同版本中发生变化时,有哪些资源可以帮助开发者查找这些变更? </think> ### 问题分析 在 Linux 内核开发中,`access_ok()` 函数用于验证用户空间指针的有效性。当包含 `<linux/uaccess.h>` 后仍出现 `undeclared` 错误,通常有以下原因: 1. **内核版本差异**: - **5.0+ 版本**:`access_ok(type, addr, size)` 被弃用,改用两参数版本 `access_ok(addr, size)`。 - **旧版本**:需包含 `<asm/uaccess.h>` 而非 `<linux/uaccess.h>`。 2. **头文件包含问题**: - 某些体系结构(如 ARM)要求先包含 `<asm/current.h>`。 3. **宏冲突**: - `CONFIG_CC_HAS_ASM_GOTO` 配置未启用时可能导致函数未声明。 --- ### 解决方法 #### 方案 1:更新调用方式(内核 ≥5.0) ```c #include <linux/uaccess.h> // 弃用旧形式: // access_ok(VERIFY_READ, user_ptr, size); // 改用: if (access_ok(user_ptr, size)) { ... } ``` #### 方案 2:包含正确头文件(旧内核) ```c // 替换为体系结构专用头文件 #include <asm/uaccess.h> // x86/ARM 通用 ``` #### 方案 3:额外依赖(ARM 示例) ```c #include <asm/current.h> // 先行包含 #include <linux/uaccess.h> ``` #### 方案 4:配置检查 确保内核配置启用: ```kconfig CONFIG_CC_HAS_ASM_GOTO=y ``` --- ### 验证步骤 1. **确认内核版本**: ```bash uname -r ``` 2. **检查函数声明位置**: ```bash grep -rn "access_ok" /usr/src/linux-headers-$(uname -r)/include ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值