]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: netpoll: ensure skb_pool list is always initialized
authorJohn Sperbeck <jsperbeck@google.com>
Tue, 14 Jan 2025 01:13:54 +0000 (17:13 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Nov 2025 09:36:08 +0000 (10:36 +0100)
commit f0d0277796db613c124206544b6dbe95b520ab6c upstream.

When __netpoll_setup() is called directly, instead of through
netpoll_setup(), the np->skb_pool list head isn't initialized.
If skb_pool_flush() is later called, then we hit a NULL pointer
in skb_queue_purge_reason().  This can be seen with this repro,
when CONFIG_NETCONSOLE is enabled as a module:

    ip tuntap add mode tap tap0
    ip link add name br0 type bridge
    ip link set dev tap0 master br0
    modprobe netconsole netconsole=4444@10.0.0.1/br0,9353@10.0.0.2/
    rmmod netconsole

The backtrace is:

    BUG: kernel NULL pointer dereference, address: 0000000000000008
    #PF: supervisor write access in kernel mode
    #PF: error_code(0x0002) - not-present page
    ... ... ...
    Call Trace:
     <TASK>
     __netpoll_free+0xa5/0xf0
     br_netpoll_cleanup+0x43/0x50 [bridge]
     do_netpoll_cleanup+0x43/0xc0
     netconsole_netdev_event+0x1e3/0x300 [netconsole]
     unregister_netdevice_notifier+0xd9/0x150
     cleanup_module+0x45/0x920 [netconsole]
     __se_sys_delete_module+0x205/0x290
     do_syscall_64+0x70/0x150
     entry_SYSCALL_64_after_hwframe+0x76/0x7e

Move the skb_pool list setup and initial skb fill into __netpoll_setup().

Fixes: 221a9c1df790 ("net: netpoll: Individualize the skb pool")
Signed-off-by: John Sperbeck <jsperbeck@google.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20250114011354.2096812-1-jsperbeck@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/core/netpoll.c

index 47e6fb660f03ead1b672e08c22c3249f4b33fb2a..11b2a841b74886a5e8db68f7f590b4de1ad002fe 100644 (file)
@@ -632,6 +632,8 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
        const struct net_device_ops *ops;
        int err;
 
+       skb_queue_head_init(&np->skb_pool);
+
        if (ndev->priv_flags & IFF_DISABLE_NETPOLL) {
                np_err(np, "%s doesn't support polling, aborting\n",
                       ndev->name);
@@ -667,6 +669,9 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
        strscpy(np->dev_name, ndev->name, IFNAMSIZ);
        npinfo->netpoll = np;
 
+       /* fill up the skb queue */
+       refill_skbs(np);
+
        /* last thing to do is link it to the net device structure */
        rcu_assign_pointer(ndev->npinfo, npinfo);
 
@@ -686,8 +691,6 @@ int netpoll_setup(struct netpoll *np)
        struct in_device *in_dev;
        int err;
 
-       skb_queue_head_init(&np->skb_pool);
-
        rtnl_lock();
        if (np->dev_name[0]) {
                struct net *net = current->nsproxy->net_ns;
@@ -787,9 +790,6 @@ put_noaddr:
                }
        }
 
-       /* fill up the skb queue */
-       refill_skbs(np);
-
        err = __netpoll_setup(np, ndev);
        if (err)
                goto flush;