]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ax25: Remove broken autobind
authorMurad Masimov <m.masimov@mt-integration.ru>
Mon, 17 Mar 2025 10:53:52 +0000 (13:53 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 24 Mar 2025 10:26:53 +0000 (10:26 +0000)
Binding AX25 socket by using the autobind feature leads to memory leaks
in ax25_connect() and also refcount leaks in ax25_release(). Memory
leak was detected with kmemleak:

================================================================
unreferenced object 0xffff8880253cd680 (size 96):
backtrace:
__kmalloc_node_track_caller_noprof (./include/linux/kmemleak.h:43)
kmemdup_noprof (mm/util.c:136)
ax25_rt_autobind (net/ax25/ax25_route.c:428)
ax25_connect (net/ax25/af_ax25.c:1282)
__sys_connect_file (net/socket.c:2045)
__sys_connect (net/socket.c:2064)
__x64_sys_connect (net/socket.c:2067)
do_syscall_64 (arch/x86/entry/common.c:52 arch/x86/entry/common.c:83)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
================================================================

When socket is bound, refcounts must be incremented the way it is done
in ax25_bind() and ax25_setsockopt() (SO_BINDTODEVICE). In case of
autobind, the refcounts are not incremented.

This bug leads to the following issue reported by Syzkaller:

================================================================
ax25_connect(): syz-executor318 uses autobind, please contact jreuter@yaina.de
------------[ cut here ]------------
refcount_t: decrement hit 0; leaking memory.
WARNING: CPU: 0 PID: 5317 at lib/refcount.c:31 refcount_warn_saturate+0xfa/0x1d0 lib/refcount.c:31
Modules linked in:
CPU: 0 UID: 0 PID: 5317 Comm: syz-executor318 Not tainted 6.14.0-rc4-syzkaller-00278-gece144f151ac #0
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
RIP: 0010:refcount_warn_saturate+0xfa/0x1d0 lib/refcount.c:31
...
Call Trace:
 <TASK>
 __refcount_dec include/linux/refcount.h:336 [inline]
 refcount_dec include/linux/refcount.h:351 [inline]
 ref_tracker_free+0x6af/0x7e0 lib/ref_tracker.c:236
 netdev_tracker_free include/linux/netdevice.h:4302 [inline]
 netdev_put include/linux/netdevice.h:4319 [inline]
 ax25_release+0x368/0x960 net/ax25/af_ax25.c:1080
 __sock_release net/socket.c:647 [inline]
 sock_close+0xbc/0x240 net/socket.c:1398
 __fput+0x3e9/0x9f0 fs/file_table.c:464
 __do_sys_close fs/open.c:1580 [inline]
 __se_sys_close fs/open.c:1565 [inline]
 __x64_sys_close+0x7f/0x110 fs/open.c:1565
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 ...
 </TASK>
================================================================

Considering the issues above and the comments left in the code that say:
"check if we can remove this feature. It is broken."; "autobinding in this
may or may not work"; - it is better to completely remove this feature than
to fix it because it is broken and leads to various kinds of memory bugs.

Now calling connect() without first binding socket will result in an
error (-EINVAL). Userspace software that relies on the autobind feature
might get broken. However, this feature does not seem widely used with
this specific driver as it was not reliable at any point of time, and it
is already broken anyway. E.g. ax25-tools and ax25-apps packages for
popular distributions do not use the autobind feature for AF_AX25.

Found by Linux Verification Center (linuxtesting.org) with Syzkaller.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+33841dc6aa3e1d86b78a@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=33841dc6aa3e1d86b78a
Signed-off-by: Murad Masimov <m.masimov@mt-integration.ru>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ax25.h
net/ax25/af_ax25.c
net/ax25/ax25_route.c

index 4ee141aae0a29de4655c9f72be1f21910b89d4a3..a7bba42dde153a2aeaf010a7ef8b48d39d15a835 100644 (file)
@@ -418,7 +418,6 @@ void ax25_rt_device_down(struct net_device *);
 int ax25_rt_ioctl(unsigned int, void __user *);
 extern const struct seq_operations ax25_rt_seqops;
 ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev);
-int ax25_rt_autobind(ax25_cb *, ax25_address *);
 struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *,
                                   ax25_address *, ax25_digi *);
 void ax25_rt_free(void);
index 9f3b8b682adb292628af07060416a04d84b0b3be..3ee7dba3431008b2f6b9c6d3f22f1af5fdb891a2 100644 (file)
@@ -1270,28 +1270,18 @@ static int __must_check ax25_connect(struct socket *sock,
                }
        }
 
-       /*
-        *      Must bind first - autobinding in this may or may not work. If
-        *      the socket is already bound, check to see if the device has
-        *      been filled in, error if it hasn't.
-        */
+       /* Must bind first - autobinding does not work. */
        if (sock_flag(sk, SOCK_ZAPPED)) {
-               /* check if we can remove this feature. It is broken. */
-               printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n",
-                       current->comm);
-               if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) {
-                       kfree(digi);
-                       goto out_release;
-               }
+               kfree(digi);
+               err = -EINVAL;
+               goto out_release;
+       }
 
-               ax25_fillin_cb(ax25, ax25->ax25_dev);
-               ax25_cb_add(ax25);
-       } else {
-               if (ax25->ax25_dev == NULL) {
-                       kfree(digi);
-                       err = -EHOSTUNREACH;
-                       goto out_release;
-               }
+       /* Check to see if the device has been filled in, error if it hasn't. */
+       if (ax25->ax25_dev == NULL) {
+               kfree(digi);
+               err = -EHOSTUNREACH;
+               goto out_release;
        }
 
        if (sk->sk_type == SOCK_SEQPACKET &&
index 69de75db0c9c2155f81a1161b456f4c3864bb246..10577434f40bf7a6ce2712bf4136ea9f228a904f 100644 (file)
@@ -373,80 +373,6 @@ ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
        return ax25_rt;
 }
 
-/*
- *     Adjust path: If you specify a default route and want to connect
- *      a target on the digipeater path but w/o having a special route
- *     set before, the path has to be truncated from your target on.
- */
-static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
-{
-       int k;
-
-       for (k = 0; k < digipeat->ndigi; k++) {
-               if (ax25cmp(addr, &digipeat->calls[k]) == 0)
-                       break;
-       }
-
-       digipeat->ndigi = k;
-}
-
-
-/*
- *     Find which interface to use.
- */
-int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
-{
-       ax25_uid_assoc *user;
-       ax25_route *ax25_rt;
-       int err = 0;
-
-       ax25_route_lock_use();
-       ax25_rt = ax25_get_route(addr, NULL);
-       if (!ax25_rt) {
-               ax25_route_lock_unuse();
-               return -EHOSTUNREACH;
-       }
-       rcu_read_lock();
-       if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) {
-               err = -EHOSTUNREACH;
-               goto put;
-       }
-
-       user = ax25_findbyuid(current_euid());
-       if (user) {
-               ax25->source_addr = user->call;
-               ax25_uid_put(user);
-       } else {
-               if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
-                       err = -EPERM;
-                       goto put;
-               }
-               ax25->source_addr = *(ax25_address *)ax25->ax25_dev->dev->dev_addr;
-       }
-
-       if (ax25_rt->digipeat != NULL) {
-               ax25->digipeat = kmemdup(ax25_rt->digipeat, sizeof(ax25_digi),
-                                        GFP_ATOMIC);
-               if (ax25->digipeat == NULL) {
-                       err = -ENOMEM;
-                       goto put;
-               }
-               ax25_adjust_path(addr, ax25->digipeat);
-       }
-
-       if (ax25->sk != NULL) {
-               local_bh_disable();
-               bh_lock_sock(ax25->sk);
-               sock_reset_flag(ax25->sk, SOCK_ZAPPED);
-               bh_unlock_sock(ax25->sk);
-               local_bh_enable();
-       }
-
-put:
-       rcu_read_unlock();
-       ax25_route_lock_unuse();
-       return err;
-}
 
 struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src,
        ax25_address *dest, ax25_digi *digi)