ATM device drivers such as usbatm initialize ci_range.vpi_bits and ci_range.vci_bits to ATM_CI_MAX (-1), which denotes the maximum standard bit widths (8 bits for VPI and 16 bits for VCI). When binding or connecting a PVC socket, __vcc_connect() validates the requested VPI and VCI values by shifting them right by dev->ci_range.vpi_bits and dev->ci_range.vci_bits. When these values are -1, shifting by a negative exponent invokes undefined behavior and triggers a UBSAN shift-out-of-bounds warning: UBSAN: shift-out-of-bounds in net/atm/common.c:382:32 shift exponent -1 is negative CPU: 0 UID: 0 PID: 6100 Comm: a.out Not tainted 6.13.0 Call Trace: __ubsan_handle_shift_out_of_bounds+0x36d/0x400 lib/ubsan.c:494 __vcc_connect+0x14b4/0x19c0 net/atm/common.c:382 vcc_connect+0x328/0x8f0 net/atm/common.c:498 pvc_bind+0x272/0x380 net/atm/pvc.c:52 __sys_bind+0x2e3/0x410 net/socket.c:1951 __x64_sys_bind+0x7a/0x90 net/socket.c:1954 do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Furthermore, dynamic VPI/VCI allocation in find_ci() checks loop bounds using `1 << vcc->dev->ci_range.vpi_bits` and `1 << vcc->dev->ci_range.vci_bits`. When these bit widths are -1, signed integer shifts evaluate to negative values (e.g. INT_MIN on x86 due to shift count masking), causing boundary comparisons like `p >= 1 << -1` to always evaluate to true. This repeatedly resets the allocated indices and breaks dynamic CI allocation, prematurely returning -EADDRINUSE. Fix this by resolving ATM_CI_MAX to the standard maximum bit widths (8 for VPI, 16 for VCI) in local variables within find_ci() and __vcc_connect() before performing bit shifts and range checks. This eliminates the negative shifts and restores dynamic allocation while leaving dev->ci_range intact for ATM_GETCIRANGE ioctl queries. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Gemini:gemini-3.7-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+f6ac161ee9699270b8dd@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f6ac161ee9699270b8dd Link: https://syzkaller.appspot.com/ai_job?id=85f31c7a-29ad-45c0-a4e9-c74384abe250 To: "Chas Williams" <3chas3@gmail.com> To: "David S. Miller" To: "Eric Dumazet" To: "Jakub Kicinski" To: To: To: "Paolo Abeni" To: "Linus Torvalds" Cc: "Simon Horman" Cc: --- diff --git a/net/atm/common.c b/net/atm/common.c index 81195727f..a21439d69 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -334,6 +334,10 @@ static int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci) short old_p; int old_c; int err; + int vpi_bits = vcc->dev->ci_range.vpi_bits == ATM_CI_MAX ? 8 : + vcc->dev->ci_range.vpi_bits; + int vci_bits = vcc->dev->ci_range.vci_bits == ATM_CI_MAX ? 16 : + vcc->dev->ci_range.vci_bits; if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) { err = check_ci(vcc, *vpi, *vci); @@ -342,12 +346,12 @@ static int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci) /* last scan may have left values out of bounds for current device */ if (*vpi != ATM_VPI_ANY) p = *vpi; - else if (p >= 1 << vcc->dev->ci_range.vpi_bits) + else if (p >= 1 << vpi_bits) p = 0; if (*vci != ATM_VCI_ANY) c = *vci; - else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits) - c = ATM_NOT_RSV_VCI; + else if (c < ATM_NOT_RSV_VCI || c >= 1 << vci_bits) + c = ATM_NOT_RSV_VCI; old_p = p; old_c = c; do { @@ -358,13 +362,13 @@ static int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci) } if (*vci == ATM_VCI_ANY) { c++; - if (c >= 1 << vcc->dev->ci_range.vci_bits) + if (c >= 1 << vci_bits) c = ATM_NOT_RSV_VCI; } if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && *vpi == ATM_VPI_ANY) { p++; - if (p >= 1 << vcc->dev->ci_range.vpi_bits) + if (p >= 1 << vpi_bits) p = 0; } } while (old_p != p || old_c != c); @@ -376,10 +380,14 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, { struct sock *sk = sk_atm(vcc); int error; + int vpi_bits = dev->ci_range.vpi_bits == ATM_CI_MAX ? 8 : + dev->ci_range.vpi_bits; + int vci_bits = dev->ci_range.vci_bits == ATM_CI_MAX ? 16 : + dev->ci_range.vci_bits; if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY && - vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC && - vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits)) + vpi >> vpi_bits) || (vci != ATM_VCI_UNSPEC && + vci != ATM_VCI_ANY && vci >> vci_bits)) return -EINVAL; if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) return -EPERM; base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f -- This is an AI-generated patch subject to moderation. Reply with '#syz upstream' to Sign-off the patch as a human author and send it to the upstream kernel mailing lists. Reply with '#syz reject' to reject it ('#syz unreject' to undo). See https://goo.gle/syzbot-ai-patches for information about AI-generated patches. You can comment on the patch as usual, syzbot will try to address the comments and send a new version of the patch if necessary. syzbot engineers can be reached at syzkaller@googlegroups.com.