Web lists-archives.com

[uio] panic in name_show()




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");