From: Greg Kroah-Hartman Date: Mon, 4 Jul 2022 11:33:59 +0000 (+0200) Subject: 5.18-stable patches X-Git-Tag: v4.9.322~64 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=10d4432fbbc3a5547d0ae800da947df6ae8c0b81;p=thirdparty%2Fkernel%2Fstable-queue.git 5.18-stable patches added patches: net-rose-fix-uaf-bugs-caused-by-timer-handler.patch net-usb-ax88179_178a-fix-packet-receiving.patch sunrpc-fix-read_plus-crasher.patch virtio-net-fix-race-between-ndo_open-and-virtio_device_ready.patch --- diff --git a/queue-5.18/net-rose-fix-uaf-bugs-caused-by-timer-handler.patch b/queue-5.18/net-rose-fix-uaf-bugs-caused-by-timer-handler.patch new file mode 100644 index 00000000000..ceb411f2030 --- /dev/null +++ b/queue-5.18/net-rose-fix-uaf-bugs-caused-by-timer-handler.patch @@ -0,0 +1,251 @@ +From 9cc02ede696272c5271a401e4f27c262359bc2f6 Mon Sep 17 00:00:00 2001 +From: Duoming Zhou +Date: Wed, 29 Jun 2022 08:26:40 +0800 +Subject: net: rose: fix UAF bugs caused by timer handler + +From: Duoming Zhou + +commit 9cc02ede696272c5271a401e4f27c262359bc2f6 upstream. + +There are UAF bugs in rose_heartbeat_expiry(), rose_timer_expiry() +and rose_idletimer_expiry(). The root cause is that del_timer() +could not stop the timer handler that is running and the refcount +of sock is not managed properly. + +One of the UAF bugs is shown below: + + (thread 1) | (thread 2) + | rose_bind + | rose_connect + | rose_start_heartbeat +rose_release | (wait a time) + case ROSE_STATE_0 | + rose_destroy_socket | rose_heartbeat_expiry + rose_stop_heartbeat | + sock_put(sk) | ... + sock_put(sk) // FREE | + | bh_lock_sock(sk) // USE + +The sock is deallocated by sock_put() in rose_release() and +then used by bh_lock_sock() in rose_heartbeat_expiry(). + +Although rose_destroy_socket() calls rose_stop_heartbeat(), +it could not stop the timer that is running. + +The KASAN report triggered by POC is shown below: + +BUG: KASAN: use-after-free in _raw_spin_lock+0x5a/0x110 +Write of size 4 at addr ffff88800ae59098 by task swapper/3/0 +... +Call Trace: + + dump_stack_lvl+0xbf/0xee + print_address_description+0x7b/0x440 + print_report+0x101/0x230 + ? irq_work_single+0xbb/0x140 + ? _raw_spin_lock+0x5a/0x110 + kasan_report+0xed/0x120 + ? _raw_spin_lock+0x5a/0x110 + kasan_check_range+0x2bd/0x2e0 + _raw_spin_lock+0x5a/0x110 + rose_heartbeat_expiry+0x39/0x370 + ? rose_start_heartbeat+0xb0/0xb0 + call_timer_fn+0x2d/0x1c0 + ? rose_start_heartbeat+0xb0/0xb0 + expire_timers+0x1f3/0x320 + __run_timers+0x3ff/0x4d0 + run_timer_softirq+0x41/0x80 + __do_softirq+0x233/0x544 + irq_exit_rcu+0x41/0xa0 + sysvec_apic_timer_interrupt+0x8c/0xb0 + + + asm_sysvec_apic_timer_interrupt+0x1b/0x20 +RIP: 0010:default_idle+0xb/0x10 +RSP: 0018:ffffc9000012fea0 EFLAGS: 00000202 +RAX: 000000000000bcae RBX: ffff888006660f00 RCX: 000000000000bcae +RDX: 0000000000000001 RSI: ffffffff843a11c0 RDI: ffffffff843a1180 +RBP: dffffc0000000000 R08: dffffc0000000000 R09: ffffed100da36d46 +R10: dfffe9100da36d47 R11: ffffffff83cf0950 R12: 0000000000000000 +R13: 1ffff11000ccc1e0 R14: ffffffff8542af28 R15: dffffc0000000000 +... +Allocated by task 146: + __kasan_kmalloc+0xc4/0xf0 + sk_prot_alloc+0xdd/0x1a0 + sk_alloc+0x2d/0x4e0 + rose_create+0x7b/0x330 + __sock_create+0x2dd/0x640 + __sys_socket+0xc7/0x270 + __x64_sys_socket+0x71/0x80 + do_syscall_64+0x43/0x90 + entry_SYSCALL_64_after_hwframe+0x46/0xb0 + +Freed by task 152: + kasan_set_track+0x4c/0x70 + kasan_set_free_info+0x1f/0x40 + ____kasan_slab_free+0x124/0x190 + kfree+0xd3/0x270 + __sk_destruct+0x314/0x460 + rose_release+0x2fa/0x3b0 + sock_close+0xcb/0x230 + __fput+0x2d9/0x650 + task_work_run+0xd6/0x160 + exit_to_user_mode_loop+0xc7/0xd0 + exit_to_user_mode_prepare+0x4e/0x80 + syscall_exit_to_user_mode+0x20/0x40 + do_syscall_64+0x4f/0x90 + entry_SYSCALL_64_after_hwframe+0x46/0xb0 + +This patch adds refcount of sock when we use functions +such as rose_start_heartbeat() and so on to start timer, +and decreases the refcount of sock when timer is finished +or deleted by functions such as rose_stop_heartbeat() +and so on. As a result, the UAF bugs could be mitigated. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Duoming Zhou +Tested-by: Duoming Zhou +Link: https://lore.kernel.org/r/20220629002640.5693-1-duoming@zju.edu.cn +Signed-off-by: Paolo Abeni +Signed-off-by: Greg Kroah-Hartman +--- + net/rose/rose_timer.c | 34 +++++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 15 deletions(-) + +--- a/net/rose/rose_timer.c ++++ b/net/rose/rose_timer.c +@@ -31,89 +31,89 @@ static void rose_idletimer_expiry(struct + + void rose_start_heartbeat(struct sock *sk) + { +- del_timer(&sk->sk_timer); ++ sk_stop_timer(sk, &sk->sk_timer); + + sk->sk_timer.function = rose_heartbeat_expiry; + sk->sk_timer.expires = jiffies + 5 * HZ; + +- add_timer(&sk->sk_timer); ++ sk_reset_timer(sk, &sk->sk_timer, sk->sk_timer.expires); + } + + void rose_start_t1timer(struct sock *sk) + { + struct rose_sock *rose = rose_sk(sk); + +- del_timer(&rose->timer); ++ sk_stop_timer(sk, &rose->timer); + + rose->timer.function = rose_timer_expiry; + rose->timer.expires = jiffies + rose->t1; + +- add_timer(&rose->timer); ++ sk_reset_timer(sk, &rose->timer, rose->timer.expires); + } + + void rose_start_t2timer(struct sock *sk) + { + struct rose_sock *rose = rose_sk(sk); + +- del_timer(&rose->timer); ++ sk_stop_timer(sk, &rose->timer); + + rose->timer.function = rose_timer_expiry; + rose->timer.expires = jiffies + rose->t2; + +- add_timer(&rose->timer); ++ sk_reset_timer(sk, &rose->timer, rose->timer.expires); + } + + void rose_start_t3timer(struct sock *sk) + { + struct rose_sock *rose = rose_sk(sk); + +- del_timer(&rose->timer); ++ sk_stop_timer(sk, &rose->timer); + + rose->timer.function = rose_timer_expiry; + rose->timer.expires = jiffies + rose->t3; + +- add_timer(&rose->timer); ++ sk_reset_timer(sk, &rose->timer, rose->timer.expires); + } + + void rose_start_hbtimer(struct sock *sk) + { + struct rose_sock *rose = rose_sk(sk); + +- del_timer(&rose->timer); ++ sk_stop_timer(sk, &rose->timer); + + rose->timer.function = rose_timer_expiry; + rose->timer.expires = jiffies + rose->hb; + +- add_timer(&rose->timer); ++ sk_reset_timer(sk, &rose->timer, rose->timer.expires); + } + + void rose_start_idletimer(struct sock *sk) + { + struct rose_sock *rose = rose_sk(sk); + +- del_timer(&rose->idletimer); ++ sk_stop_timer(sk, &rose->idletimer); + + if (rose->idle > 0) { + rose->idletimer.function = rose_idletimer_expiry; + rose->idletimer.expires = jiffies + rose->idle; + +- add_timer(&rose->idletimer); ++ sk_reset_timer(sk, &rose->idletimer, rose->idletimer.expires); + } + } + + void rose_stop_heartbeat(struct sock *sk) + { +- del_timer(&sk->sk_timer); ++ sk_stop_timer(sk, &sk->sk_timer); + } + + void rose_stop_timer(struct sock *sk) + { +- del_timer(&rose_sk(sk)->timer); ++ sk_stop_timer(sk, &rose_sk(sk)->timer); + } + + void rose_stop_idletimer(struct sock *sk) + { +- del_timer(&rose_sk(sk)->idletimer); ++ sk_stop_timer(sk, &rose_sk(sk)->idletimer); + } + + static void rose_heartbeat_expiry(struct timer_list *t) +@@ -130,6 +130,7 @@ static void rose_heartbeat_expiry(struct + (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { + bh_unlock_sock(sk); + rose_destroy_socket(sk); ++ sock_put(sk); + return; + } + break; +@@ -152,6 +153,7 @@ static void rose_heartbeat_expiry(struct + + rose_start_heartbeat(sk); + bh_unlock_sock(sk); ++ sock_put(sk); + } + + static void rose_timer_expiry(struct timer_list *t) +@@ -181,6 +183,7 @@ static void rose_timer_expiry(struct tim + break; + } + bh_unlock_sock(sk); ++ sock_put(sk); + } + + static void rose_idletimer_expiry(struct timer_list *t) +@@ -205,4 +208,5 @@ static void rose_idletimer_expiry(struct + sock_set_flag(sk, SOCK_DEAD); + } + bh_unlock_sock(sk); ++ sock_put(sk); + } diff --git a/queue-5.18/net-usb-ax88179_178a-fix-packet-receiving.patch b/queue-5.18/net-usb-ax88179_178a-fix-packet-receiving.patch new file mode 100644 index 00000000000..0089cb145d0 --- /dev/null +++ b/queue-5.18/net-usb-ax88179_178a-fix-packet-receiving.patch @@ -0,0 +1,230 @@ +From f8ebb3ac881b17712e1d5967c97ab1806b16d3d6 Mon Sep 17 00:00:00 2001 +From: Jose Alonso +Date: Tue, 28 Jun 2022 12:13:02 -0300 +Subject: net: usb: ax88179_178a: Fix packet receiving + +From: Jose Alonso + +commit f8ebb3ac881b17712e1d5967c97ab1806b16d3d6 upstream. + +This patch corrects packet receiving in ax88179_rx_fixup. + +- problem observed: + ifconfig shows allways a lot of 'RX Errors' while packets + are received normally. + + This occurs because ax88179_rx_fixup does not recognise properly + the usb urb received. + The packets are normally processed and at the end, the code exits + with 'return 0', generating RX Errors. + (pkt_cnt==-2 and ptk_hdr over field rx_hdr trying to identify + another packet there) + + This is a usb urb received by "tcpdump -i usbmon2 -X" on a + little-endian CPU: + 0x0000: eeee f8e3 3b19 87a0 94de 80e3 daac 0800 + ^ packet 1 start (pkt_len = 0x05ec) + ^^^^ IP alignment pseudo header + ^ ethernet packet start + last byte ethernet packet v + padding (8-bytes aligned) vvvv vvvv + 0x05e0: c92d d444 1420 8a69 83dd 272f e82b 9811 + 0x05f0: eeee f8e3 3b19 87a0 94de 80e3 daac 0800 + ... ^ packet 2 + 0x0be0: eeee f8e3 3b19 87a0 94de 80e3 daac 0800 + ... + 0x1130: 9d41 9171 8a38 0ec5 eeee f8e3 3b19 87a0 + ... + 0x1720: 8cfc 15ff 5e4c e85c eeee f8e3 3b19 87a0 + ... + 0x1d10: ecfa 2a3a 19ab c78c eeee f8e3 3b19 87a0 + ... + 0x2070: eeee f8e3 3b19 87a0 94de 80e3 daac 0800 + ... ^ packet 7 + 0x2120: 7c88 4ca5 5c57 7dcc 0d34 7577 f778 7e0a + 0x2130: f032 e093 7489 0740 3008 ec05 0000 0080 + ====1==== ====2==== + hdr_off ^ + pkt_len = 0x05ec ^^^^ + AX_RXHDR_*=0x00830 ^^^^ ^ + pkt_len = 0 ^^^^ + AX_RXHDR_DROP_ERR=0x80000000 ^^^^ ^ + 0x2140: 3008 ec05 0000 0080 3008 5805 0000 0080 + 0x2150: 3008 ec05 0000 0080 3008 ec05 0000 0080 + 0x2160: 3008 5803 0000 0080 3008 c800 0000 0080 + ===11==== ===12==== ===13==== ===14==== + 0x2170: 0000 0000 0e00 3821 + ^^^^ ^^^^ rx_hdr + ^^^^ pkt_cnt=14 + ^^^^ hdr_off=0x2138 + ^^^^ ^^^^ padding + + The dump shows that pkt_cnt is the number of entrys in the + per-packet metadata. It is "2 * packet count". + Each packet have two entrys. The first have a valid + value (pkt_len and AX_RXHDR_*) and the second have a + dummy-header 0x80000000 (pkt_len=0 with AX_RXHDR_DROP_ERR). + Why exists dummy-header for each packet?!? + My guess is that this was done probably to align the + entry for each packet to 64-bits and maintain compatibility + with old firmware. + There is also a padding (0x00000000) before the rx_hdr to + align the end of rx_hdr to 64-bit. + Note that packets have a alignment of 64-bits (8-bytes). + + This patch assumes that the dummy-header and the last + padding are optional. So it preserves semantics and + recognises the same valid packets as the current code. + + This patch was made using only the dumpfile information and + tested with only one device: + 0b95:1790 ASIX Electronics Corp. AX88179 Gigabit Ethernet + +Fixes: 57bc3d3ae8c1 ("net: usb: ax88179_178a: Fix out-of-bounds accesses in RX fixup") +Fixes: e2ca90c276e1 ("ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver") +Signed-off-by: Jose Alonso +Acked-by: Paolo Abeni +Link: https://lore.kernel.org/r/d6970bb04bf67598af4d316eaeb1792040b18cfd.camel@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/usb/ax88179_178a.c | 101 ++++++++++++++++++++++++++++++----------- + 1 file changed, 76 insertions(+), 25 deletions(-) + +--- a/drivers/net/usb/ax88179_178a.c ++++ b/drivers/net/usb/ax88179_178a.c +@@ -1472,6 +1472,42 @@ static int ax88179_rx_fixup(struct usbne + * are bundled into this buffer and where we can find an array of + * per-packet metadata (which contains elements encoded into u16). + */ ++ ++ /* SKB contents for current firmware: ++ * ++ * ... ++ * ++ * ++ * ... ++ * ++ * ++ * ++ * where: ++ * contains pkt_len bytes: ++ * 2 bytes of IP alignment pseudo header ++ * packet received ++ * contains 4 bytes: ++ * pkt_len and fields AX_RXHDR_* ++ * 0-7 bytes to terminate at ++ * 8 bytes boundary (64-bit). ++ * 4 bytes to make rx_hdr terminate at ++ * 8 bytes boundary (64-bit) ++ * contains 4 bytes: ++ * pkt_len=0 and AX_RXHDR_DROP_ERR ++ * contains 4 bytes: ++ * pkt_cnt and hdr_off (offset of ++ * ) ++ * ++ * pkt_cnt is number of entrys in the per-packet metadata. ++ * In current firmware there is 2 entrys per packet. ++ * The first points to the packet and the ++ * second is a dummy header. ++ * This was done probably to align fields in 64-bit and ++ * maintain compatibility with old firmware. ++ * This code assumes that and are ++ * optional. ++ */ ++ + if (skb->len < 4) + return 0; + skb_trim(skb, skb->len - 4); +@@ -1485,51 +1521,66 @@ static int ax88179_rx_fixup(struct usbne + /* Make sure that the bounds of the metadata array are inside the SKB + * (and in front of the counter at the end). + */ +- if (pkt_cnt * 2 + hdr_off > skb->len) ++ if (pkt_cnt * 4 + hdr_off > skb->len) + return 0; + pkt_hdr = (u32 *)(skb->data + hdr_off); + + /* Packets must not overlap the metadata array */ + skb_trim(skb, hdr_off); + +- for (; ; pkt_cnt--, pkt_hdr++) { ++ for (; pkt_cnt > 0; pkt_cnt--, pkt_hdr++) { ++ u16 pkt_len_plus_padd; + u16 pkt_len; + + le32_to_cpus(pkt_hdr); + pkt_len = (*pkt_hdr >> 16) & 0x1fff; ++ pkt_len_plus_padd = (pkt_len + 7) & 0xfff8; + +- if (pkt_len > skb->len) ++ /* Skip dummy header used for alignment ++ */ ++ if (pkt_len == 0) ++ continue; ++ ++ if (pkt_len_plus_padd > skb->len) + return 0; + + /* Check CRC or runt packet */ +- if (((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) == 0) && +- pkt_len >= 2 + ETH_HLEN) { +- bool last = (pkt_cnt == 0); +- +- if (last) { +- ax_skb = skb; +- } else { +- ax_skb = skb_clone(skb, GFP_ATOMIC); +- if (!ax_skb) +- return 0; +- } +- ax_skb->len = pkt_len; +- /* Skip IP alignment pseudo header */ +- skb_pull(ax_skb, 2); +- skb_set_tail_pointer(ax_skb, ax_skb->len); +- ax_skb->truesize = pkt_len + sizeof(struct sk_buff); +- ax88179_rx_checksum(ax_skb, pkt_hdr); ++ if ((*pkt_hdr & (AX_RXHDR_CRC_ERR | AX_RXHDR_DROP_ERR)) || ++ pkt_len < 2 + ETH_HLEN) { ++ dev->net->stats.rx_errors++; ++ skb_pull(skb, pkt_len_plus_padd); ++ continue; ++ } + +- if (last) +- return 1; ++ /* last packet */ ++ if (pkt_len_plus_padd == skb->len) { ++ skb_trim(skb, pkt_len); + +- usbnet_skb_return(dev, ax_skb); ++ /* Skip IP alignment pseudo header */ ++ skb_pull(skb, 2); ++ ++ skb->truesize = SKB_TRUESIZE(pkt_len_plus_padd); ++ ax88179_rx_checksum(skb, pkt_hdr); ++ return 1; + } + +- /* Trim this packet away from the SKB */ +- if (!skb_pull(skb, (pkt_len + 7) & 0xFFF8)) ++ ax_skb = skb_clone(skb, GFP_ATOMIC); ++ if (!ax_skb) + return 0; ++ skb_trim(ax_skb, pkt_len); ++ ++ /* Skip IP alignment pseudo header */ ++ skb_pull(ax_skb, 2); ++ ++ skb->truesize = pkt_len_plus_padd + ++ SKB_DATA_ALIGN(sizeof(struct sk_buff)); ++ ax88179_rx_checksum(ax_skb, pkt_hdr); ++ usbnet_skb_return(dev, ax_skb); ++ ++ skb_pull(skb, pkt_len_plus_padd); + } ++ ++ return 0; + } + + static struct sk_buff * diff --git a/queue-5.18/series b/queue-5.18/series index d52eb614ccd..1f619d9675a 100644 --- a/queue-5.18/series +++ b/queue-5.18/series @@ -19,3 +19,7 @@ powerpc-bpf-fix-use-of-user_pt_regs-in-uapi.patch cpufreq-amd-pstate-add-resume-and-suspend-callbacks.patch dm-raid-fix-accesses-beyond-end-of-raid-member-array.patch dm-raid-fix-kasan-warning-in-raid5_add_disks.patch +sunrpc-fix-read_plus-crasher.patch +net-rose-fix-uaf-bugs-caused-by-timer-handler.patch +net-usb-ax88179_178a-fix-packet-receiving.patch +virtio-net-fix-race-between-ndo_open-and-virtio_device_ready.patch diff --git a/queue-5.18/sunrpc-fix-read_plus-crasher.patch b/queue-5.18/sunrpc-fix-read_plus-crasher.patch new file mode 100644 index 00000000000..9c347bbb059 --- /dev/null +++ b/queue-5.18/sunrpc-fix-read_plus-crasher.patch @@ -0,0 +1,34 @@ +From a23dd544debcda4ee4a549ec7de59e85c3c8345c Mon Sep 17 00:00:00 2001 +From: Chuck Lever +Date: Thu, 30 Jun 2022 16:48:18 -0400 +Subject: SUNRPC: Fix READ_PLUS crasher + +From: Chuck Lever + +commit a23dd544debcda4ee4a549ec7de59e85c3c8345c upstream. + +Looks like there are still cases when "space_left - frag1bytes" can +legitimately exceed PAGE_SIZE. Ensure that xdr->end always remains +within the current encode buffer. + +Reported-by: Bruce Fields +Reported-by: Zorro Lang +Link: https://bugzilla.kernel.org/show_bug.cgi?id=216151 +Fixes: 6c254bf3b637 ("SUNRPC: Fix the calculation of xdr->end in xdr_get_next_encode_buffer()") +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman +--- + net/sunrpc/xdr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/sunrpc/xdr.c ++++ b/net/sunrpc/xdr.c +@@ -979,7 +979,7 @@ static __be32 *xdr_get_next_encode_buffe + */ + xdr->p = (void *)p + frag2bytes; + space_left = xdr->buf->buflen - xdr->buf->len; +- if (space_left - nbytes >= PAGE_SIZE) ++ if (space_left - frag1bytes >= PAGE_SIZE) + xdr->end = (void *)p + PAGE_SIZE; + else + xdr->end = (void *)p + space_left - frag1bytes; diff --git a/queue-5.18/virtio-net-fix-race-between-ndo_open-and-virtio_device_ready.patch b/queue-5.18/virtio-net-fix-race-between-ndo_open-and-virtio_device_ready.patch new file mode 100644 index 00000000000..8348ed3e731 --- /dev/null +++ b/queue-5.18/virtio-net-fix-race-between-ndo_open-and-virtio_device_ready.patch @@ -0,0 +1,52 @@ +From 50c0ada627f56c92f5953a8bf9158b045ad026a1 Mon Sep 17 00:00:00 2001 +From: Jason Wang +Date: Fri, 17 Jun 2022 15:29:49 +0800 +Subject: virtio-net: fix race between ndo_open() and virtio_device_ready() + +From: Jason Wang + +commit 50c0ada627f56c92f5953a8bf9158b045ad026a1 upstream. + +We currently call virtio_device_ready() after netdev +registration. Since ndo_open() can be called immediately +after register_netdev, this means there exists a race between +ndo_open() and virtio_device_ready(): the driver may start to use the +device before DRIVER_OK which violates the spec. + +Fix this by switching to use register_netdevice() and protect the +virtio_device_ready() with rtnl_lock() to make sure ndo_open() can +only be called after virtio_device_ready(). + +Fixes: 4baf1e33d0842 ("virtio_net: enable VQs early") +Signed-off-by: Jason Wang +Message-Id: <20220617072949.30734-1-jasowang@redhat.com> +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/virtio_net.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/virtio_net.c ++++ b/drivers/net/virtio_net.c +@@ -3641,14 +3641,20 @@ static int virtnet_probe(struct virtio_d + if (vi->has_rss || vi->has_rss_hash_report) + virtnet_init_default_rss(vi); + +- err = register_netdev(dev); ++ /* serialize netdev register + virtio_device_ready() with ndo_open() */ ++ rtnl_lock(); ++ ++ err = register_netdevice(dev); + if (err) { + pr_debug("virtio_net: registering device failed\n"); ++ rtnl_unlock(); + goto free_failover; + } + + virtio_device_ready(vdev); + ++ rtnl_unlock(); ++ + err = virtnet_cpu_notif_add(vi); + if (err) { + pr_debug("virtio_net: registering cpu notifier failed\n");