access_ok和get_user和put_user简介

本文详细介绍了Linux内核中access_ok函数的作用及使用方法,该函数用于检查用户空间指针的有效性。此外,还深入分析了get_user和put_user函数的工作原理,并通过示例展示了如何在内核空间与用户空间之间安全地交换数据。

access_ok

access_ok()
用来代替老版本的 verify_area()
检查用户空间指针是否可用

函数原型:

access_ok (type, addr, size);

变量说明:
type : 访问类型,其值可为 VERIFY_READ 或 VERIFY_WRITE 。
addr : 用户空间的指针变量,指向一个要检查的内存块开始处。
size : 要检查内存块大小

注意:
VERIFY_WRITE 是 VERIFY_READ 的超集
如果可以安全写内存块,那么肯定能读到内存块。

返回值:
此函数检查用户空间中的内存块是否可用。
如果可用,则返回真(非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;

参考:https://blog.csdn.net/dfz87292/article/details/102394781?utm_term=accessok%E5%87%BD%E6%95%B0&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-0-102394781&spm=3001.4430
//=======================================================================

kernel get_user 示例

在内核空间和用户空间交换数据时, get_user 和 put_user 是两个两用的函数。
相对于 copy_to_user 和 copy_from_user ,主要用于完成一些简单类型变量(char、int、long等)的拷贝任务,
对于一些复合类型的变量,比如数据结构或者数组类型,get_user 和 put_user 函数还是无法胜任,
这两个函数内部将对指针指向的对象长度进行检查,在 arm 平台上只支持长度为1,2,4,8的变量。

struct Demo {
	int index;
	int count;
};

// arg1 是用户空间传递下来的地址
struct Demo __user *demo1 = (struct Demo __user *)arg1;
// index 用来承接用户空间的对应值
int index;

if (get_user(index, &demo1->index))
	return -EFAULT;

// arg2 是用户空间传递下来的地址
int count = 2;
struct Demo __user *demo2 = (struct Demo __user *)arg2;

if (put_user(count, &demo2->count))
	return -EFAULT;

//=======================================================================

put_user 和 get_user 具体分析

#define put_user(x, ptr)					\
({								\
	void __user *__p = (ptr);				\
	might_fault();						\
	access_ok(VERIFY_WRITE, __p, sizeof(*ptr)) ?		\
		__put_user((x), ((__typeof__(*(ptr)) __user *)__p)) :	\
		-EFAULT;					\
})

#define __put_user(x, ptr) \
({								\
	__typeof__(*(ptr)) __x = (x);				\
	int __pu_err = -EFAULT;					\
        __chk_user_ptr(ptr);                                    \
	switch (sizeof (*(ptr))) {				\
	case 1:							\
	case 2:							\
	case 4:							\
	case 8:							\
		__pu_err = __put_user_fn(sizeof (*(ptr)),	\
					 ptr, &__x);		\
		break;						\
	default:						\
		__put_user_bad();				\
		break;						\
	 }							\
	__pu_err;						\
})

static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
{
	return unlikely(raw_copy_to_user(ptr, x, size)) ? -EFAULT : 0;
}
#define get_user(x, ptr)					\
({								\
	const void __user *__p = (ptr);				\
	might_fault();						\
	access_ok(VERIFY_READ, __p, sizeof(*ptr)) ?		\
		__get_user((x), (__typeof__(*(ptr)) __user *)__p) :\
		((x) = (__typeof__(*(ptr)))0,-EFAULT);		\
})

static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
{
	return unlikely(raw_copy_from_user(x, ptr, size)) ? -EFAULT : 0;
}

#define __get_user(x, ptr)					\
({								\
	int __gu_err = -EFAULT;					\
	__chk_user_ptr(ptr);					\
	switch (sizeof(*(ptr))) {				\
	case 1: {						\
		unsigned char __x = 0;				\
		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
					 ptr, &__x);		\
		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
		break;						\
	};							\
	case 2: {						\
		unsigned short __x = 0;				\
		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
					 ptr, &__x);		\
		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
		break;						\
	};							\
	case 4: {						\
		unsigned int __x = 0;				\
		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
					 ptr, &__x);		\
		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
		break;						\
	};							\
	case 8: {						\
		unsigned long long __x = 0;			\
		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
					 ptr, &__x);		\
		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
		break;						\
	};							\
	default:						\
		__get_user_bad();				\
		break;						\
	}							\
	__gu_err;						\
})

小彩蛋

static inline void uaccess_disable(void)
{
	__uaccess_disable(ARM64_HAS_PAN);
}

static inline void uaccess_enable(void)
{
	__uaccess_enable(ARM64_HAS_PAN);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值