KASAN 打电话时抱怨 copy_from/to_user
KASAN complains when calling copy_from/to_user
我们正在开发一个 linux 驱动程序,当我 read/write 创建设备文件时注意到 KASAN 抱怨。
下面列出了最小的例子(所以设计得不好)。
它创建文件 /dev/test_ctl
并启用 read
/write
/ioctl
.
我们编译了一个启用了 KASAN 的 4.6.2 内核,并且这个代码在更新的 Fedora 中。
在modprobe模块之后,我尝试读取(cat
)/写入(echo foo >
)并调用ioctl
,都得到了BUG: KASAN: user-memory-access on address ... ...
。我预计这些操作会在没有任何警告的情况下运行。
根据 pr_info,我注意到该行为是由 copy_to/from_user 函数引起的。
由于我们使用 KASAN 进行内存相关的运行时检查,我们如何才能消除这种噪音。
#define pr_fmt(fmt) "test dev : " fmt
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm-generic/uaccess.h>
#include <asm-generic/ioctl.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FOO");
MODULE_DESCRIPTION("BAR");
#define LEN 1024
#define IOCTL_READ _IOR('t', 0xD0, u8[LEN])
#define IOCTL_WRITE _IOW('t', 0xD1, u8[LEN])
static u8 buf[LEN];
static dev_t major;
static struct class *class;
static struct cdev cdev;
static struct device *cdevice;
static long test_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err;
void __user *ptr = (void __user *)arg;
pr_info("ioctl: cmd: %08x, arg: %p, local: %p\n", cmd, ptr, buf);
switch (cmd) {
case IOCTL_READ:
pr_info("ioctl/r: calling copy_to_user\n");
if (copy_to_user(ptr, buf, LEN)) {
pr_info("ioctl/r: failed to copy to user\n");
err = -EFAULT;
} else {
pr_info("ioctl/r: buffer copied, val: %8ph\n", buf);
err = 0;
}
break;
case IOCTL_WRITE:
pr_info("ioctl/r: calling copy_from_user\n");
if (copy_from_user(buf, ptr, LEN)) {
pr_info("ioctl/w: failed to copy from user\n");
err = -EFAULT;
} else {
pr_info("ioctl/w: buffer copied, val: %8ph\n", buf);
err = 0;
}
break;
default:
pr_info("ioctl: invalid command\n");
err = -EINVAL;
break;
}
return err;
}
static int test_open(struct inode *inode, struct file *file)
{
return 0;
}
static int test_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t test_read(struct file *filep,
char __user *ptr, size_t len, loff_t *offset)
{
ssize_t r;
if (*offset) {
return 0;
}
if (len > LEN) {
len = LEN;
}
*offset += len;
pr_info("calling copy_to_user\n");
r = copy_to_user(ptr, buf, len) ? -EFAULT : len;
pr_info("called copy_to_user\n");
return r;
}
static ssize_t test_write(struct file *filep,
const char __user *ptr, size_t len, loff_t *offset)
{
ssize_t r;
if (*offset) {
return -EINVAL;
}
if (len > LEN) {
len = LEN;
}
*offset += len;
pr_info("calling copy_from_user\n");
r = copy_from_user(buf, ptr, len) ? -EFAULT : len;
pr_info("called copy_from_user\n");
return r;
}
static const struct file_operations cdev_ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = test_ioctl,
.open = test_open,
.release = test_release,
.read = test_read,
.write = test_write,
};
static int test_init(void)
{
int rc;
pr_info("test device initing\n");
rc = alloc_chrdev_region(&major, 0, 1, "test");
if (rc) {
goto fail_alloc_chrdev_region;
}
pr_info("major assigned: %d\n", (int)MAJOR(major));
class = class_create(THIS_MODULE, "test_ctl");
if (IS_ERR(class)) {
pr_err("failed to create class\n");
rc = PTR_RET(class);
goto fail_create_class;
}
cdev_init(&cdev, &cdev_ops);
cdev.owner = THIS_MODULE;
rc = cdev_add(&cdev, MKDEV(MAJOR(major), 0), 1);
if (rc) {
pr_info("failed to add char dev\n");
goto fail_cdev_add;
}
cdevice = device_create(class, NULL, MKDEV(MAJOR(major), 0), NULL,
"test_ctl");
if (IS_ERR(cdevice)) {
pr_err("failed to create /dev/ file\n");
rc = PTR_RET(cdevice);
goto fail_device_create;
}
pr_info("driver initialized\n");
return 0;
device_destroy(class, MKDEV(major, 0));
fail_device_create:
cdev_del(&cdev);
fail_cdev_add:
class_destroy(class);
fail_create_class:
unregister_chrdev_region(major, 1);
fail_alloc_chrdev_region:
return rc;
}
module_init(test_init);
这是 od /dev/test_ctl
期间的内核消息
[18088.583185] test dev : called copy_to_user
[18117.378665] test dev : ioctl: cmd: 00005401, arg: 00007ffeb2303070, local: ffffffffa018aba0
[18117.380954] test dev : ioctl: invalid command
[18117.386294] test dev : calling copy_to_user
[18117.388772] ==================================================================
[18117.390903] BUG: KASAN: user-memory-access on address 00007f52831a8000
[18117.392150] Write of size 1024 by task od/2057
[18117.393305] CPU: 1 PID: 2057 Comm: od Tainted: G B O 4.6.2-kasan-outline #1
[18117.395448] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[18117.396655] ffff880009cf8000 00000000da76c32b ffff88000a2efbc8 ffffffff816e1398
[18117.399220] 0000000000000400 ffff88000a2efc60 ffff88000a2efc50 ffffffff8134fe46
[18117.401765] 0000000000000246 ffffffffa0189140 0000000000000292 ffff88000a2efc00
[18117.404304] Call Trace:
[18117.405375] [<ffffffff816e1398>] dump_stack+0x85/0xcd
[18117.406550] [<ffffffff8134fe46>] kasan_report_error+0x456/0x560
[18117.407777] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18117.409039] [<ffffffff813504e8>] kasan_report+0x58/0x60
[18117.410222] [<ffffffff8134f498>] ? memcpy+0x28/0x40
[18117.411394] [<ffffffff8134f08d>] __asan_storeN+0x12d/0x180
[18117.412592] [<ffffffff8134f498>] memcpy+0x28/0x40
[18117.413766] [<ffffffffa0188019>] __copy_to_user+0x9/0x10 [test_drv]
[18117.415010] [<ffffffffa0188132>] test_read+0x72/0xa0 [test_drv]
[18117.416233] [<ffffffff8138529d>] __vfs_read+0xdd/0x260
[18117.417419] [<ffffffff813851c0>] ? vfs_iter_write+0x190/0x190
[18117.418644] [<ffffffff813f7140>] ? __fsnotify_update_child_dentry_flags.part.1+0x160/0x160
[18117.442467] [<ffffffff812e0117>] ? vm_mmap_pgoff+0x167/0x1a0
[18117.443686] [<ffffffff8116ff18>] ? up_write+0x28/0x50
[18117.444874] [<ffffffff812e0117>] ? vm_mmap_pgoff+0x167/0x1a0
[18117.446093] [<ffffffff81636dd5>] ? security_file_permission+0xd5/0x100
[18117.447344] [<ffffffff81386bb7>] vfs_read+0xb7/0x1a0
[18117.448536] [<ffffffff81388dba>] SyS_read+0xba/0x150
[18117.449713] [<ffffffff81388d00>] ? vfs_copy_file_range+0x370/0x370
[18117.450945] [<ffffffff811776c6>] ? trace_hardirqs_on_caller+0x16/0x290
[18117.452193] [<ffffffff8100401b>] ? trace_hardirqs_on_thunk+0x1b/0x1d
[18117.453441] [<ffffffff81b386fc>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[18117.454689] [<ffffffff811717b6>] ? trace_hardirqs_off_caller+0x16/0x120
[18117.455943] ==================================================================
[18117.462008] test dev : called copy_to_user
调用 echo 1 > /dev/test_ctl
时,我收到以下内核消息:
[18212.023598] test dev : calling copy_from_user
[18212.024844] ==================================================================
[18212.027020] BUG: KASAN: user-memory-access on address 00007f31bf34f000
[18212.028272] Read of size 2 by task bash/1982
[18212.029425] CPU: 1 PID: 1982 Comm: bash Tainted: G B O 4.6.2-kasan-outline #1
[18212.031585] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[18212.032904] ffff8800306e0000 000000003803bec2 ffff88000a2dfbc0 ffffffff816e1398
[18212.035569] 0000000000000002 ffff88000a2dfc58 ffff88000a2dfc48 ffffffff8134fe46
[18212.038169] 0000000000000246 ffffffffa0189040 0000000000000286 ffff88000a2dfbf8
[18212.040760] Call Trace:
[18212.041835] [<ffffffff816e1398>] dump_stack+0x85/0xcd
[18212.043032] [<ffffffff8134fe46>] kasan_report_error+0x456/0x560
[18212.044276] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.045538] [<ffffffff813504e8>] kasan_report+0x58/0x60
[18212.046742] [<ffffffff8134f48d>] ? memcpy+0x1d/0x40
[18212.047944] [<ffffffff8134ef0a>] __asan_loadN+0x12a/0x180
[18212.049155] [<ffffffff8134f48d>] memcpy+0x1d/0x40
[18212.050340] [<ffffffffa0188019>] __copy_to_user+0x9/0x10 [test_drv]
[18212.051593] [<ffffffffa0188097>] test_write+0x77/0xa0 [test_drv]
[18212.052840] [<ffffffff813854fd>] __vfs_write+0xdd/0x260
[18212.054040] [<ffffffff81385420>] ? __vfs_read+0x260/0x260
[18212.055252] [<ffffffff81300b40>] ? __pmd_alloc+0x250/0x250
[18212.056464] [<ffffffff813b9595>] ? __fd_install+0x5/0x3f0
[18212.057736] [<ffffffff813b92ac>] ? __alloc_fd+0x3c/0x2b0
[18212.058952] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.060220] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.061488] [<ffffffff81636d68>] ? security_file_permission+0x68/0x100
[18212.062757] [<ffffffff81386d96>] vfs_write+0xf6/0x260
[18212.063959] [<ffffffff81388f0a>] SyS_write+0xba/0x150
[18212.065165] [<ffffffff81388e50>] ? SyS_read+0x150/0x150
[18212.066382] [<ffffffff811776c6>] ? trace_hardirqs_on_caller+0x16/0x290
[18212.067666] [<ffffffff8100401b>] ? trace_hardirqs_on_thunk+0x1b/0x1d
[18212.068933] [<ffffffff81b386fc>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[18212.070199] [<ffffffff811717b6>] ? trace_hardirqs_off_caller+0x16/0x120
[18212.071470] ==================================================================
[18212.073790] test dev : called copy_from_user
所以 KASAN 抱怨 user-space 访问。我不知道为什么。但是 #include <asm-generic/uaccess.h>
看起来 可疑 :外部代码(如模块)应该很少包含 asm-specific header。使用标准 #include <linux/uaccess.h>
。顺便说一句,对于 x86 asm-generic
版本的 uaccess.h
从未使用过:它的 asm/uaccess.h
header 手动定义了用户访问函数。这可能是您遇到问题的原因。 asm-generic/ioctl.h
包含也是如此。 –齐瓦列夫
感谢您的帮助。我使用 linux/uaccess.h
而不是 asm-generic/uaccess.h
并且此行为不会再次发生。 – OstCollector
我们正在开发一个 linux 驱动程序,当我 read/write 创建设备文件时注意到 KASAN 抱怨。
下面列出了最小的例子(所以设计得不好)。
它创建文件 /dev/test_ctl
并启用 read
/write
/ioctl
.
我们编译了一个启用了 KASAN 的 4.6.2 内核,并且这个代码在更新的 Fedora 中。
在modprobe模块之后,我尝试读取(cat
)/写入(echo foo >
)并调用ioctl
,都得到了BUG: KASAN: user-memory-access on address ... ...
。我预计这些操作会在没有任何警告的情况下运行。
根据 pr_info,我注意到该行为是由 copy_to/from_user 函数引起的。 由于我们使用 KASAN 进行内存相关的运行时检查,我们如何才能消除这种噪音。
#define pr_fmt(fmt) "test dev : " fmt
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm-generic/uaccess.h>
#include <asm-generic/ioctl.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FOO");
MODULE_DESCRIPTION("BAR");
#define LEN 1024
#define IOCTL_READ _IOR('t', 0xD0, u8[LEN])
#define IOCTL_WRITE _IOW('t', 0xD1, u8[LEN])
static u8 buf[LEN];
static dev_t major;
static struct class *class;
static struct cdev cdev;
static struct device *cdevice;
static long test_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err;
void __user *ptr = (void __user *)arg;
pr_info("ioctl: cmd: %08x, arg: %p, local: %p\n", cmd, ptr, buf);
switch (cmd) {
case IOCTL_READ:
pr_info("ioctl/r: calling copy_to_user\n");
if (copy_to_user(ptr, buf, LEN)) {
pr_info("ioctl/r: failed to copy to user\n");
err = -EFAULT;
} else {
pr_info("ioctl/r: buffer copied, val: %8ph\n", buf);
err = 0;
}
break;
case IOCTL_WRITE:
pr_info("ioctl/r: calling copy_from_user\n");
if (copy_from_user(buf, ptr, LEN)) {
pr_info("ioctl/w: failed to copy from user\n");
err = -EFAULT;
} else {
pr_info("ioctl/w: buffer copied, val: %8ph\n", buf);
err = 0;
}
break;
default:
pr_info("ioctl: invalid command\n");
err = -EINVAL;
break;
}
return err;
}
static int test_open(struct inode *inode, struct file *file)
{
return 0;
}
static int test_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t test_read(struct file *filep,
char __user *ptr, size_t len, loff_t *offset)
{
ssize_t r;
if (*offset) {
return 0;
}
if (len > LEN) {
len = LEN;
}
*offset += len;
pr_info("calling copy_to_user\n");
r = copy_to_user(ptr, buf, len) ? -EFAULT : len;
pr_info("called copy_to_user\n");
return r;
}
static ssize_t test_write(struct file *filep,
const char __user *ptr, size_t len, loff_t *offset)
{
ssize_t r;
if (*offset) {
return -EINVAL;
}
if (len > LEN) {
len = LEN;
}
*offset += len;
pr_info("calling copy_from_user\n");
r = copy_from_user(buf, ptr, len) ? -EFAULT : len;
pr_info("called copy_from_user\n");
return r;
}
static const struct file_operations cdev_ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = test_ioctl,
.open = test_open,
.release = test_release,
.read = test_read,
.write = test_write,
};
static int test_init(void)
{
int rc;
pr_info("test device initing\n");
rc = alloc_chrdev_region(&major, 0, 1, "test");
if (rc) {
goto fail_alloc_chrdev_region;
}
pr_info("major assigned: %d\n", (int)MAJOR(major));
class = class_create(THIS_MODULE, "test_ctl");
if (IS_ERR(class)) {
pr_err("failed to create class\n");
rc = PTR_RET(class);
goto fail_create_class;
}
cdev_init(&cdev, &cdev_ops);
cdev.owner = THIS_MODULE;
rc = cdev_add(&cdev, MKDEV(MAJOR(major), 0), 1);
if (rc) {
pr_info("failed to add char dev\n");
goto fail_cdev_add;
}
cdevice = device_create(class, NULL, MKDEV(MAJOR(major), 0), NULL,
"test_ctl");
if (IS_ERR(cdevice)) {
pr_err("failed to create /dev/ file\n");
rc = PTR_RET(cdevice);
goto fail_device_create;
}
pr_info("driver initialized\n");
return 0;
device_destroy(class, MKDEV(major, 0));
fail_device_create:
cdev_del(&cdev);
fail_cdev_add:
class_destroy(class);
fail_create_class:
unregister_chrdev_region(major, 1);
fail_alloc_chrdev_region:
return rc;
}
module_init(test_init);
这是 od /dev/test_ctl
[18088.583185] test dev : called copy_to_user
[18117.378665] test dev : ioctl: cmd: 00005401, arg: 00007ffeb2303070, local: ffffffffa018aba0
[18117.380954] test dev : ioctl: invalid command
[18117.386294] test dev : calling copy_to_user
[18117.388772] ==================================================================
[18117.390903] BUG: KASAN: user-memory-access on address 00007f52831a8000
[18117.392150] Write of size 1024 by task od/2057
[18117.393305] CPU: 1 PID: 2057 Comm: od Tainted: G B O 4.6.2-kasan-outline #1
[18117.395448] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[18117.396655] ffff880009cf8000 00000000da76c32b ffff88000a2efbc8 ffffffff816e1398
[18117.399220] 0000000000000400 ffff88000a2efc60 ffff88000a2efc50 ffffffff8134fe46
[18117.401765] 0000000000000246 ffffffffa0189140 0000000000000292 ffff88000a2efc00
[18117.404304] Call Trace:
[18117.405375] [<ffffffff816e1398>] dump_stack+0x85/0xcd
[18117.406550] [<ffffffff8134fe46>] kasan_report_error+0x456/0x560
[18117.407777] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18117.409039] [<ffffffff813504e8>] kasan_report+0x58/0x60
[18117.410222] [<ffffffff8134f498>] ? memcpy+0x28/0x40
[18117.411394] [<ffffffff8134f08d>] __asan_storeN+0x12d/0x180
[18117.412592] [<ffffffff8134f498>] memcpy+0x28/0x40
[18117.413766] [<ffffffffa0188019>] __copy_to_user+0x9/0x10 [test_drv]
[18117.415010] [<ffffffffa0188132>] test_read+0x72/0xa0 [test_drv]
[18117.416233] [<ffffffff8138529d>] __vfs_read+0xdd/0x260
[18117.417419] [<ffffffff813851c0>] ? vfs_iter_write+0x190/0x190
[18117.418644] [<ffffffff813f7140>] ? __fsnotify_update_child_dentry_flags.part.1+0x160/0x160
[18117.442467] [<ffffffff812e0117>] ? vm_mmap_pgoff+0x167/0x1a0
[18117.443686] [<ffffffff8116ff18>] ? up_write+0x28/0x50
[18117.444874] [<ffffffff812e0117>] ? vm_mmap_pgoff+0x167/0x1a0
[18117.446093] [<ffffffff81636dd5>] ? security_file_permission+0xd5/0x100
[18117.447344] [<ffffffff81386bb7>] vfs_read+0xb7/0x1a0
[18117.448536] [<ffffffff81388dba>] SyS_read+0xba/0x150
[18117.449713] [<ffffffff81388d00>] ? vfs_copy_file_range+0x370/0x370
[18117.450945] [<ffffffff811776c6>] ? trace_hardirqs_on_caller+0x16/0x290
[18117.452193] [<ffffffff8100401b>] ? trace_hardirqs_on_thunk+0x1b/0x1d
[18117.453441] [<ffffffff81b386fc>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[18117.454689] [<ffffffff811717b6>] ? trace_hardirqs_off_caller+0x16/0x120
[18117.455943] ==================================================================
[18117.462008] test dev : called copy_to_user
调用 echo 1 > /dev/test_ctl
时,我收到以下内核消息:
[18212.023598] test dev : calling copy_from_user
[18212.024844] ==================================================================
[18212.027020] BUG: KASAN: user-memory-access on address 00007f31bf34f000
[18212.028272] Read of size 2 by task bash/1982
[18212.029425] CPU: 1 PID: 1982 Comm: bash Tainted: G B O 4.6.2-kasan-outline #1
[18212.031585] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[18212.032904] ffff8800306e0000 000000003803bec2 ffff88000a2dfbc0 ffffffff816e1398
[18212.035569] 0000000000000002 ffff88000a2dfc58 ffff88000a2dfc48 ffffffff8134fe46
[18212.038169] 0000000000000246 ffffffffa0189040 0000000000000286 ffff88000a2dfbf8
[18212.040760] Call Trace:
[18212.041835] [<ffffffff816e1398>] dump_stack+0x85/0xcd
[18212.043032] [<ffffffff8134fe46>] kasan_report_error+0x456/0x560
[18212.044276] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.045538] [<ffffffff813504e8>] kasan_report+0x58/0x60
[18212.046742] [<ffffffff8134f48d>] ? memcpy+0x1d/0x40
[18212.047944] [<ffffffff8134ef0a>] __asan_loadN+0x12a/0x180
[18212.049155] [<ffffffff8134f48d>] memcpy+0x1d/0x40
[18212.050340] [<ffffffffa0188019>] __copy_to_user+0x9/0x10 [test_drv]
[18212.051593] [<ffffffffa0188097>] test_write+0x77/0xa0 [test_drv]
[18212.052840] [<ffffffff813854fd>] __vfs_write+0xdd/0x260
[18212.054040] [<ffffffff81385420>] ? __vfs_read+0x260/0x260
[18212.055252] [<ffffffff81300b40>] ? __pmd_alloc+0x250/0x250
[18212.056464] [<ffffffff813b9595>] ? __fd_install+0x5/0x3f0
[18212.057736] [<ffffffff813b92ac>] ? __alloc_fd+0x3c/0x2b0
[18212.058952] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.060220] [<ffffffff81197896>] ? debug_lockdep_rcu_enabled+0x26/0x40
[18212.061488] [<ffffffff81636d68>] ? security_file_permission+0x68/0x100
[18212.062757] [<ffffffff81386d96>] vfs_write+0xf6/0x260
[18212.063959] [<ffffffff81388f0a>] SyS_write+0xba/0x150
[18212.065165] [<ffffffff81388e50>] ? SyS_read+0x150/0x150
[18212.066382] [<ffffffff811776c6>] ? trace_hardirqs_on_caller+0x16/0x290
[18212.067666] [<ffffffff8100401b>] ? trace_hardirqs_on_thunk+0x1b/0x1d
[18212.068933] [<ffffffff81b386fc>] entry_SYSCALL_64_fastpath+0x1f/0xbd
[18212.070199] [<ffffffff811717b6>] ? trace_hardirqs_off_caller+0x16/0x120
[18212.071470] ==================================================================
[18212.073790] test dev : called copy_from_user
所以 KASAN 抱怨 user-space 访问。我不知道为什么。但是 #include <asm-generic/uaccess.h>
看起来 可疑 :外部代码(如模块)应该很少包含 asm-specific header。使用标准 #include <linux/uaccess.h>
。顺便说一句,对于 x86 asm-generic
版本的 uaccess.h
从未使用过:它的 asm/uaccess.h
header 手动定义了用户访问函数。这可能是您遇到问题的原因。 asm-generic/ioctl.h
包含也是如此。 –齐瓦列夫
感谢您的帮助。我使用 linux/uaccess.h
而不是 asm-generic/uaccess.h
并且此行为不会再次发生。 – OstCollector