]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
batman-adv: Release references to inactive interfaces
authorSven Eckelmann <sven@narfation.org>
Sat, 27 Sep 2025 17:39:08 +0000 (19:39 +0200)
committerSimon Wunderlich <sw@simonwunderlich.de>
Sat, 27 Sep 2025 17:59:49 +0000 (19:59 +0200)
Trying to dump the originators or the neighbors via netlink for a meshif
with an inactive primary interface is not allowed. The dump functions were
checking this correctly but they didn't handle non-existing primary
interfaces and existing _inactive_ interfaces differently.

(Primary) batadv_hard_ifaces hold a references to a net_device. And
accessing them is only allowed when either being in a RCU/spinlock
protected section or when holding a valid reference to them. The netlink
dump functions use the latter.

But because the missing specific error handling for inactive primary
interfaces, the reference was never dropped. This reference counting error
was only detected when the interface should have been removed from the
system:

  unregister_netdevice: waiting for batadv_slave_0 to become free. Usage count = 2

Cc: stable@vger.kernel.org
Fixes: 6ecc4fd6c2f4 ("batman-adv: netlink: reduce duplicate code by returning interfaces")
Reported-by: syzbot+881d65229ca4f9ae8c84@syzkaller.appspotmail.com
Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
net/batman-adv/originator.c

index a464ff96b92917c3e9122b850d948a0108602518..ed89d7fd1e7f4be389fb1c6a7d9879b270979135 100644 (file)
@@ -764,11 +764,16 @@ int batadv_hardif_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb)
        bat_priv = netdev_priv(mesh_iface);
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
+       if (!primary_if) {
                ret = -ENOENT;
                goto out_put_mesh_iface;
        }
 
+       if (primary_if->if_status != BATADV_IF_ACTIVE) {
+               ret = -ENOENT;
+               goto out_put_primary_if;
+       }
+
        hard_iface = batadv_netlink_get_hardif(bat_priv, cb);
        if (IS_ERR(hard_iface) && PTR_ERR(hard_iface) != -ENONET) {
                ret = PTR_ERR(hard_iface);
@@ -1333,11 +1338,16 @@ int batadv_orig_dump(struct sk_buff *msg, struct netlink_callback *cb)
        bat_priv = netdev_priv(mesh_iface);
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
+       if (!primary_if) {
                ret = -ENOENT;
                goto out_put_mesh_iface;
        }
 
+       if (primary_if->if_status != BATADV_IF_ACTIVE) {
+               ret = -ENOENT;
+               goto out_put_primary_if;
+       }
+
        hard_iface = batadv_netlink_get_hardif(bat_priv, cb);
        if (IS_ERR(hard_iface) && PTR_ERR(hard_iface) != -ENONET) {
                ret = PTR_ERR(hard_iface);