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.