[uio] panic in name_show()

From: Francesco Ruggeri
Date: Fri Aug 11 2017 - 14:37:13 EST


I have run into this panic while some devices were being hotunplugged,
and I am able to easily reproduce it with the attached module, which
creates a dummy uio device (in my case /sys/class/uio/uio74).
The panic is the result of a race between uio_unregister_device(),
which sets idev->info to NULL, and name_show() which dereferences it.
Seen in 4.9, 3.18 and 3.4.

Thanks,
Francesco Ruggeri

-bash-4.3# insmod dummydev.ko
-bash-4.3# cat /sys/class/uio/uio74/name
uio_dummydev
-bash-4.3# rmmod dummydev
-bash-4.3#

Then on different bash shells on different cpus I run

while true ;do insmod dummydev.ko; rmmod dummydev ;done
while true ;do cat /sys/class/uio/uio74/name ;done

-bash-4.3# taskset 1 bash
bash-4.3# while true ;do insmod dummydev.ko; rmmod dummydev ;done
[ 448.104247] BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
[ 448.197773] IP: [<ffffffffa016f1ba>] name_show+0x24/0x31 [uio]
[ 448.267454] PGD 245c8d067 [ 448.297612] PUD 14b221067
PMD 0 [ 448.336088]
[ 448.353773] Oops: 0000 [#1] PREEMPT SMP
[ 448.399531] Modules linked in: dummydev(O-) l2mod_dma(PO) xt_u32 nfnetlink_log nfnetlink nf_log_ipv6 nf_conntrack_ipv6 nf_defrag_ipv6 ip6t_REJECT nf_reject_ipv6 ip6table_mangle nf_log_ipv4 nf_log_common nf_conntrack_ipv4 nf_defrag_ipv4 xt_LOG xt_limit xt_hl xt_multiport ipt_REJECT nf_reject_ipv4 xt_tcpudp iptable_mangle sch_prio msr strata_dma_drv(PO) arista_bde(PO) rbfd(PO) 8021q garp stp llc tun xt_conntrack xfrm_user nf_conntrack_tftp xfrm4_tunnel tunnel4 xt_CT nf_conntrack xt_mark ipcomp ip6table_raw iptable_raw xfrm_ipcomp iptable_filter ip6table_filter ip_tables ip6_tables esp4 x_tables ah4 af_key xfrm_algo x86_pkg_temp_thermal coretemp scd(O) kshim(PO) uio fan thermal tpm_tis tpm_tis_core tpm kvm_intel kvm irqbypass [last unloaded: dummydev]
[ 449.193038] CPU: 1 PID: 5278 Comm: cat Tainted: P O 4.9.20.Ar-5809926.eostrunkkernel49 #1
[ 449.304316] task: ffff880148e4ce00 task.stack: ffffc90004544000
[ 449.375031] RIP: 0010:[<ffffffffa016f1ba>] [<ffffffffa016f1ba>] name_show+0x24/0x31 [uio]
[ 449.473832] RSP: 0018:ffffc90004547da8 EFLAGS: 00010286
[ 449.537271] RAX: ffff88016a306000 RBX: ffffffffa01707c0 RCX: ffffffffa016f196
[ 449.622548] RDX: 0000000000000000 RSI: ffffffffa01701e4 RDI: ffff88016a306000
[ 449.707826] RBP: ffffc90004547da8 R08: ffff88014b16d810 R09: ffff88017d1df600
[ 449.793105] R10: ffffc90004547dc0 R11: 0000000000000246 R12: ffffffff81653b30
[ 449.878380] R13: ffffc90004547f08 R14: ffff88014b1b76c0 R15: ffff88014b212500
[ 449.963661] FS: 0000000000000000(0000) GS:ffff88024f880000(0063) knlGS:00000000f73b0940
[ 450.060376] CS: 0010 DS: 002b ES: 002b CR0: 0000000080050033
[ 450.129016] CR2: 0000000000000008 CR3: 0000000154571000 CR4: 00000000000406e0
[ 450.214293] Stack:
[ 450.238214] ffffc90004547dc8 ffffffff81382de9 ffff88014b16d810 ffff88014b1b76c0
[ 450.326613] ffffc90004547de8 ffffffff811d37f4 0000000000000000 0000000000010000
[ 450.415009] ffffc90004547df8 ffffffff811d242c ffffc90004547e60 ffffffff81187b60
[ 450.503409] Call Trace:
[ 450.532542] [<ffffffff81382de9>] dev_attr_show+0x25/0x49
[ 450.597019] [<ffffffff811d37f4>] sysfs_kf_seq_show+0x83/0xcf
[ 450.665655] [<ffffffff811d242c>] kernfs_seq_show+0x26/0x28
[ 450.732212] [<ffffffff81187b60>] seq_read+0x181/0x358
[ 450.793574] [<ffffffff81142639>] ? handle_mm_fault+0xc50/0xd5a
[ 450.864287] [<ffffffff811d2c68>] kernfs_fop_read+0x3a/0x166
[ 450.931887] [<ffffffff8116823a>] __vfs_read+0x18/0x2f
[ 450.993242] [<ffffffff81168c8c>] vfs_read+0xa6/0x10d
[ 451.053562] [<ffffffff81169d28>] SyS_read+0x51/0x8e
[ 451.112844] [<ffffffff81003b09>] do_fast_syscall_32+0xc9/0x150
[ 451.183559] [<ffffffff815e480c>] entry_SYSENTER_compat+0x4c/0x5b
[ 451.256356] Code: 08 16 e1 5d 48 98 c3 66 66 66 66 90 55 48 89 d0 48 8b 97 c0 00 00 00 48 c7 c6 e4 01 17 a0 48 89 c7 48 89 e5 48 8b 92 48 03 00 00 <48> 8b 52 08 e8 14 08 16 e1 5d 48 98 c3 66 66 66 66 90 55 48 89
[ 451.482028] RIP [<ffffffffa016f1ba>] name_show+0x24/0x31 [uio]
[ 451.552747] RSP <ffffc90004547da8>
[ 451.594346] CR2: 0000000000000008




#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kprobes.h>
#include <linux/kallsyms.h>
#include <linux/netdevice.h>
#include <net/net_namespace.h>
#include <net/dst.h>
#include <linux/uio_driver.h>

static struct device *dummydev = NULL;
static const char *devname = "dummydev";

static struct uio_info uio_info = {
.name = "uio_dummydev",
.version = "1.2.3",
.irq = UIO_IRQ_CUSTOM,
};

static void dummydev_release(struct device *dev) {}

int init_module(void)
{
int ret;

dummydev = kzalloc(sizeof(struct device), GFP_ATOMIC);
if (!dummydev) {
dev_info(dummydev, "INFO %s - kzalloc() failed\n", __func__);
goto out;
}

dev_set_name(dummydev, devname);

dummydev->release = dummydev_release;

ret = device_register(dummydev);
if (ret) {
dev_info(dummydev,
"INFO %s - device_register() failed\n", __func__);
goto out_free;
}

ret = uio_register_device(dummydev, &uio_info);
if (ret) {
dev_info(dummydev,
"INFO %s - uio_register_device() failed\n", __func__);
goto out_dev_unregister;
}

dev_info(dummydev, "INFO %s - created\n", __func__);
return 0;

out_dev_unregister:
device_unregister(dummydev);
out_free:
kfree(dummydev);
out:
return 1;
}

void cleanup_module(void)
{
if (!dummydev)
return;

dev_info(dummydev, "INFO %s - removing\n", __func__);
uio_unregister_device(&uio_info);
device_unregister(dummydev);
kfree(dummydev);
}

MODULE_LICENSE("GPL");