一、关于Linux内核非法地址(网上搜加自己总结)
1、对任何一个指针,必然有三种情况:一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针
2、32位CPU,内核空间占用虚拟地址(0xc0000000~0xffffffff)3~4G虚拟地址(线性地址?)
3、其中最后一个page(假设4K大小)的地址为(0xfffff000~0xffffffff)为预留地址,指向这个地址的指针都是非法指针
二、错误码跟非法指针啥关系
1、
2、插一段代码,看内核如何返回错误码。可以看出,在错误码前面加上一个负号就变成了返回值
/* Check type and command number */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
return -ENOTTY;
3、继续以-ENOTTY为例,ENOTTY的值为25,-ENOTTY的值是多少呢。负数在计算机中以二进制补码形式存储。
补码=~源码+1(源码按位取反后加1)
那么-25的补码就为 0xFFFFFFE7。仔细看一下这个值,在0xfffff000~0xffffffff之间,所以它就是个错误指针。
所以错误码取负值后在内存中存储的值就是内核的错误指针。
三、IS_ERR_VALUE宏定义分析
#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)
1、unlikely宏啥意思现还没深入研究,好像是高速编译器哪个不可能发生,用来优化代码,先不管。
2、(unsigned long)-MAX_ERRNO的值为 0xFFFFF001,也就是大于等于0xFFFFF001的指针为非法指针。
3、根据上面的分析,负的错误码与非法指针等价,IS_ERR_VALUE宏既可以用来判断返回错误码(负值)的函数,又可以用来判断返回错误指针的函数。
四、ERR_PTR宏定义分析
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;
}
1、字面上来看,把一个long型error转换为指针类型,猜测一下是把负的错误码转换为 void *
2、看别人怎么用。可以看出,ERR_PTR宏把负的错误码转换为 void *指针。在返回指针的函数中可以这样用,返回一个非法地址,这个跟用户态错误就返回空指针是不同的
static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev)
{
struct ac97c_platform_data *pdata;
struct device_node *node = dev->of_node;
if (!node) {
dev_err(dev, "Device does not have associated DT data\n");
return ERR_PTR(-EINVAL);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->reset_pin = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
return pdata;
}
五、PTR_ERR宏定义分析
static inline long __must_check PTR_ERR(__force const void *ptr)
{
return (long) ptr;
}
1、字面上来看把指针转换为整数类型,合理猜测是把非法指针转换为错误码
2、看别人怎么用。IS_ERR宏就是调用了IS_ERR_VALUE,后面分析。可以看出先判断一下指针是不是非法的,如果是非法的,则将错误指针通过PTR_ERR转换为错误码,并返回一个整形数值。
static int act8945a_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &act8945a_regmap_config);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, regmap);
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
act8945a_devs, ARRAY_SIZE(act8945a_devs),
NULL, 0, NULL);
if (ret) {
dev_err(&i2c->dev, "Failed to add sub devices\n");
return ret;
}
return 0;
}
六、PTR_ERR宏定义分析
static inline bool __must_check IS_ERR(__force const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
这个宏就是调用了IS_ERR_VALUE,来判断指针是否非法。