Skip to content

Commit ae88a5d

Browse files
mrprePaolo Abeni
authored andcommitted
net: atm: fix crash due to unvalidated vcc pointer in sigd_send()
Reproducer available at [1]. The ATM send path (sendmsg -> vcc_sendmsg -> sigd_send) reads the vcc pointer from msg->vcc and uses it directly without any validation. This pointer comes from userspace via sendmsg() and can be arbitrarily forged: int fd = socket(AF_ATMSVC, SOCK_DGRAM, 0); ioctl(fd, ATMSIGD_CTRL); // become ATM signaling daemon struct msghdr msg = { .msg_iov = &iov, ... }; *(unsigned long *)(buf + 4) = 0xdeadbeef; // fake vcc pointer sendmsg(fd, &msg, 0); // kernel dereferences 0xdeadbeef In normal operation, the kernel sends the vcc pointer to the signaling daemon via sigd_enq() when processing operations like connect(), bind(), or listen(). The daemon is expected to return the same pointer when responding. However, a malicious daemon can send arbitrary pointer values. Fix this by introducing find_get_vcc() which validates the pointer by searching through vcc_hash (similar to how sigd_close() iterates over all VCCs), and acquires a reference via sock_hold() if found. Since struct atm_vcc embeds struct sock as its first member, they share the same lifetime. Therefore using sock_hold/sock_put is sufficient to keep the vcc alive while it is being used. Note that there may be a race with sigd_close() which could mark the vcc with various flags (e.g., ATM_VF_RELEASED) after find_get_vcc() returns. However, sock_hold() guarantees the memory remains valid, so this race only affects the logical state, not memory safety. [1]: https://gist.github.com/mrpre/1ba5949c45529c511152e2f4c755b0f3 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+1f22cb1769f249df9fa0@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69039850.a70a0220.5b2ed.005d.GAE@google.com/T/ Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com> Link: https://patch.msgid.link/20260205095501.131890-1-jiayuan.chen@linux.dev Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 6d2f142 commit ae88a5d

1 file changed

Lines changed: 54 additions & 2 deletions

File tree

net/atm/signaling.c

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,36 @@
2222

2323
struct atm_vcc *sigd = NULL;
2424

25+
/*
26+
* find_get_vcc - validate and get a reference to a vcc pointer
27+
* @vcc: the vcc pointer to validate
28+
*
29+
* This function validates that @vcc points to a registered VCC in vcc_hash.
30+
* If found, it increments the socket reference count and returns the vcc.
31+
* The caller must call sock_put(sk_atm(vcc)) when done.
32+
*
33+
* Returns the vcc pointer if valid, NULL otherwise.
34+
*/
35+
static struct atm_vcc *find_get_vcc(struct atm_vcc *vcc)
36+
{
37+
int i;
38+
39+
read_lock(&vcc_sklist_lock);
40+
for (i = 0; i < VCC_HTABLE_SIZE; i++) {
41+
struct sock *s;
42+
43+
sk_for_each(s, &vcc_hash[i]) {
44+
if (atm_sk(s) == vcc) {
45+
sock_hold(s);
46+
read_unlock(&vcc_sklist_lock);
47+
return vcc;
48+
}
49+
}
50+
}
51+
read_unlock(&vcc_sklist_lock);
52+
return NULL;
53+
}
54+
2555
static void sigd_put_skb(struct sk_buff *skb)
2656
{
2757
if (!sigd) {
@@ -69,7 +99,14 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
6999

70100
msg = (struct atmsvc_msg *) skb->data;
71101
WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc));
72-
vcc = *(struct atm_vcc **) &msg->vcc;
102+
103+
vcc = find_get_vcc(*(struct atm_vcc **)&msg->vcc);
104+
if (!vcc) {
105+
pr_debug("invalid vcc pointer in msg\n");
106+
dev_kfree_skb(skb);
107+
return -EINVAL;
108+
}
109+
73110
pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc);
74111
sk = sk_atm(vcc);
75112

@@ -100,7 +137,16 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
100137
clear_bit(ATM_VF_WAITING, &vcc->flags);
101138
break;
102139
case as_indicate:
103-
vcc = *(struct atm_vcc **)&msg->listen_vcc;
140+
/* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */
141+
sock_put(sk);
142+
143+
vcc = find_get_vcc(*(struct atm_vcc **)&msg->listen_vcc);
144+
if (!vcc) {
145+
pr_debug("invalid listen_vcc pointer in msg\n");
146+
dev_kfree_skb(skb);
147+
return -EINVAL;
148+
}
149+
104150
sk = sk_atm(vcc);
105151
pr_debug("as_indicate!!!\n");
106152
lock_sock(sk);
@@ -115,6 +161,8 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
115161
sk->sk_state_change(sk);
116162
as_indicate_complete:
117163
release_sock(sk);
164+
/* Paired with find_get_vcc(msg->listen_vcc) above */
165+
sock_put(sk);
118166
return 0;
119167
case as_close:
120168
set_bit(ATM_VF_RELEASED, &vcc->flags);
@@ -131,11 +179,15 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
131179
break;
132180
default:
133181
pr_alert("bad message type %d\n", (int)msg->type);
182+
/* Paired with find_get_vcc(msg->vcc) above */
183+
sock_put(sk);
134184
return -EINVAL;
135185
}
136186
sk->sk_state_change(sk);
137187
out:
138188
dev_kfree_skb(skb);
189+
/* Paired with find_get_vcc(msg->vcc) above */
190+
sock_put(sk);
139191
return 0;
140192
}
141193

0 commit comments

Comments
 (0)