* [PATCH net 0/2] net/iucv: give afiucv_hs_rcv() the preamble a packet_type handler needs
@ 2026-08-15 16:07 ` Bryam Vargas
0 siblings, 0 replies; 15+ messages in thread
From: Bryam Vargas via B4 Relay @ 2026-08-15 16:07 UTC (permalink / raw)
To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni,
David S. Miller, Alexandra Winter
Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan,
linux-s390
iucv_packet_type is registered with no .dev and no .af_packet_net, so
afiucv_hs_rcv() sits in the machine-global ptype_base[] and sees every frame of
its ethertype from every namespace, on any device. It then trusts the frame: no
namespace test, and no private or writable reference before it rewrites the
payload in place.
1/2 drop frames from other namespaces -- the only one that crosses a
privilege boundary, and the only one tagged for stable
2/2 unshare and cow the head before the in-place rewrite
The two siblings differ in how far they go, so I will not claim more than they
do: net/x25/x25_dev.c does the namespace test and then takes a full skb_copy()
of every frame, and net/ieee802154/socket.c does the namespace test but does not
unshare at all. 2/2 takes the cheaper of the two -- skb_share_check() plus
skb_cow_head() -- which costs a refcount test and a compare when the frame is
already private, and copies only when it is not.
2/2 also covers the headroom that afiucv_swap_src_dest() pushes without
checking. That started as a third patch; it collapsed into this one once it was
clear skb_cow_head() is needed for writability anyway and asking it for ETH_HLEN
rather than nothing is free.
2/2 was surfaced by the sashiko.dev review bot on the thread of a previous af_iucv
patch, which flagged both the missing unshare and the unchecked push; the Closes:
trailer on that patch points at its report.
By inspection; not reproduced. Compile-tested for s390x.
---
Bryam Vargas (2):
net/iucv: drop HiperSockets frames from other network namespaces
net/iucv: take a private, writable frame before rewriting it in place
net/iucv/af_iucv.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
---
base-commit: a59f57e2aa127c5354168d2ec4bac920df1be4f4
change-id: 20260815-b4-disp-dc82fde4-3cae5e52ae9f
Best regards,
--
Bryam Vargas <hexlabsecurity@proton.me>
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH net 0/2] net/iucv: give afiucv_hs_rcv() the preamble a packet_type handler needs @ 2026-08-15 16:07 ` Bryam Vargas 0 siblings, 0 replies; 15+ messages in thread From: Bryam Vargas @ 2026-08-15 16:07 UTC (permalink / raw) To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller, Alexandra Winter Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 iucv_packet_type is registered with no .dev and no .af_packet_net, so afiucv_hs_rcv() sits in the machine-global ptype_base[] and sees every frame of its ethertype from every namespace, on any device. It then trusts the frame: no namespace test, and no private or writable reference before it rewrites the payload in place. 1/2 drop frames from other namespaces -- the only one that crosses a privilege boundary, and the only one tagged for stable 2/2 unshare and cow the head before the in-place rewrite The two siblings differ in how far they go, so I will not claim more than they do: net/x25/x25_dev.c does the namespace test and then takes a full skb_copy() of every frame, and net/ieee802154/socket.c does the namespace test but does not unshare at all. 2/2 takes the cheaper of the two -- skb_share_check() plus skb_cow_head() -- which costs a refcount test and a compare when the frame is already private, and copies only when it is not. 2/2 also covers the headroom that afiucv_swap_src_dest() pushes without checking. That started as a third patch; it collapsed into this one once it was clear skb_cow_head() is needed for writability anyway and asking it for ETH_HLEN rather than nothing is free. 2/2 was surfaced by the sashiko.dev review bot on the thread of a previous af_iucv patch, which flagged both the missing unshare and the unchecked push; the Closes: trailer on that patch points at its report. By inspection; not reproduced. Compile-tested for s390x. --- Bryam Vargas (2): net/iucv: drop HiperSockets frames from other network namespaces net/iucv: take a private, writable frame before rewriting it in place net/iucv/af_iucv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) --- base-commit: a59f57e2aa127c5354168d2ec4bac920df1be4f4 change-id: 20260815-b4-disp-dc82fde4-3cae5e52ae9f Best regards, -- Bryam Vargas <hexlabsecurity@proton.me> ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces 2026-08-15 16:07 ` Bryam Vargas @ 2026-08-15 16:07 ` Bryam Vargas -1 siblings, 0 replies; 15+ messages in thread From: Bryam Vargas via B4 Relay @ 2026-08-15 16:07 UTC (permalink / raw) To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller, Alexandra Winter Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 From: Bryam Vargas <hexlabsecurity@proton.me> iucv_packet_type sets neither .dev nor .af_packet_net, so it lands in the machine-global ptype_base[] that __netif_receive_skb_core walks for every frame in every namespace, and afiucv_hs_rcv() ignores its dev argument. An ETH_P_AF_IUCV frame sent from any namespace holding CAP_NET_RAW is therefore matched against the global iucv_sk_list and can move a socket owned by the initial namespace: afiucv_hs_callback_synfin() and _fin() overwrite its sk_state, and _syn() builds an accept-queue child. Filter on the namespace. The core does not do it for ptype_base[] -- net/core/dev.c leaves namespace filtering to the ptype owner -- and net/x25/x25_dev.c and net/ieee802154/socket.c both test dev_net(dev) at exactly this point. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- net/iucv/af_iucv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ea047bab65e7..e3ec965d96ca 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2064,6 +2064,11 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, int err = NET_RX_SUCCESS; char nullstring[8]; + if (!net_eq(dev_net(dev), &init_net)) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { kfree_skb(skb); return NET_RX_SUCCESS; -- 2.55.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces @ 2026-08-15 16:07 ` Bryam Vargas 0 siblings, 0 replies; 15+ messages in thread From: Bryam Vargas @ 2026-08-15 16:07 UTC (permalink / raw) To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller, Alexandra Winter Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 iucv_packet_type sets neither .dev nor .af_packet_net, so it lands in the machine-global ptype_base[] that __netif_receive_skb_core walks for every frame in every namespace, and afiucv_hs_rcv() ignores its dev argument. An ETH_P_AF_IUCV frame sent from any namespace holding CAP_NET_RAW is therefore matched against the global iucv_sk_list and can move a socket owned by the initial namespace: afiucv_hs_callback_synfin() and _fin() overwrite its sk_state, and _syn() builds an accept-queue child. Filter on the namespace. The core does not do it for ptype_base[] -- net/core/dev.c leaves namespace filtering to the ptype owner -- and net/x25/x25_dev.c and net/ieee802154/socket.c both test dev_net(dev) at exactly this point. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- net/iucv/af_iucv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ea047bab65e7..e3ec965d96ca 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2064,6 +2064,11 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, int err = NET_RX_SUCCESS; char nullstring[8]; + if (!net_eq(dev_net(dev), &init_net)) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { kfree_skb(skb); return NET_RX_SUCCESS; -- 2.55.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces 2026-08-15 16:07 ` Bryam Vargas (?) @ 2026-08-16 16:07 ` sashiko-bot -1 siblings, 0 replies; 15+ messages in thread From: sashiko-bot @ 2026-08-16 16:07 UTC (permalink / raw) To: Bryam Vargas Cc: linux-s390, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger, Heiko Carstens Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: Pre-existing issues: - [Critical] AF_IUCV allows unprivileged users in non-initial network namespaces to create sockets in the initial network namespace, bypassing namespace isolation. - [High] Netdevice reference count leak during NETDEV_UNREGISTER leads to kernel hangs. - [High] Bypass of `lock_sock()` mutual exclusion in softirq receive path leads to data corruption. - [High] TOCTOU race in `afiucv_hs_callback_rx` leads to silent SKB memory leak. - [High] Newly allocated IUCV sockets leak memory and references when an error occurs during connection request processing. - [High] Incoming packets bypass `sk_rcvbuf` limits via unbounded queuing to `backlog_skb_q`, leading to OOM. -- --- Patch [1]: [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces --- commit f9050fb5eddf9fc6d107045a06d53a731b334db3 Author: Bryam Vargas <hexlabsecurity@proton.me> net/iucv: drop HiperSockets frames from other network namespaces This commit adds a namespace check to afiucv_hs_rcv() to drop HiperSockets frames from other network namespaces. Because iucv_packet_type lacks specific namespace bindings, it previously landed in the global ptype_base[], allowing frames from namespaces holding CAP_NET_RAW to interact with sockets owned by the initial namespace. > diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c > index ea047bab65e7c..e3ec965d96ca7 100644 > --- a/net/iucv/af_iucv.c > +++ b/net/iucv/af_iucv.c > @@ -2064,6 +2064,11 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, > int err = NET_RX_SUCCESS; > char nullstring[8]; > > + if (!net_eq(dev_net(dev), &init_net)) { > + kfree_skb(skb); > + return NET_RX_SUCCESS; > + } > + > if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { > kfree_skb(skb); > return NET_RX_SUCCESS; [Severity: Critical] This is a pre-existing issue, but does iucv_sock_create() bypass namespace isolation? A process in a non-initial network namespace calling socket(AF_IUCV, ...) ignores the passed network namespace and unconditionally allocates the socket in init_net: net/iucv/af_iucv.c:iucv_sock_create() { ... sk = iucv_sock_alloc(sock, protocol, GFP_KERNEL, kern); // inside iucv_sock_alloc: sk = sk_alloc(&init_net, PF_IUCV, prio, &iucv_proto, kern); ... } Subsequently, iucv_sock_bind() iterates through init_net devices and binds the socket to them. This appears to allow local users in containers with socket creation privileges to bypass namespace isolation and communicate over init_net devices. [Severity: High] This isn't a bug introduced by this patch, but is there a netdevice reference count leak during NETDEV_UNREGISTER? When an AF_IUCV socket binds to a HiperSockets device via iucv_sock_bind(), it takes a reference to the device using dev_hold(dev). The netdevice notifier explicitly ignores the NETDEV_UNREGISTER event: net/iucv/af_iucv.c:afiucv_netdev_event() { ... case NETDEV_DOWN: case NETDEV_UNREGISTER: default: break; ... } If a device is unregistered while a socket remains open, it seems the unreleased reference could prevent the device from being fully removed, causing the kernel to hang indefinitely in netdev_wait_allrefs(). [Severity: High] This is a pre-existing issue, but does afiucv_hs_callback_syn() bypass lock_sock() mutual exclusion? The HiperSockets receive path (afiucv_hs_rcv) executes in softirq context. Callbacks acquire bh_lock_sock(sk) but fail to check sock_owned_by_user(sk): net/iucv/af_iucv.c:afiucv_hs_callback_syn() { ... bh_lock_sock(sk); if ((sk->sk_state != IUCV_LISTEN) || ... ... } If user context is currently holding lock_sock() (e.g., executing close()), sk->sk_lock.owned is 1, but the softirq ignores this and modifies the socket state concurrently. Could this lead to data races and socket state corruption? [Severity: High] This isn't a bug introduced by this patch, but is there a TOCTOU race in afiucv_hs_callback_rx() leading to a silent SKB memory leak? The code locklessly checks sk->sk_state: net/iucv/af_iucv.c:afiucv_hs_callback_rx() { ... if (sk->sk_state != IUCV_CONNECTED) { kfree_skb(skb); return NET_RX_SUCCESS; } ... } Concurrently, iucv_sock_close() can execute, set the state to IUCV_CLOSED, and purge backlog_skb_q. If the softirq reads the state as IUCV_CONNECTED before it is updated, but queues the SKB after iucv_sock_close() has purged the queue, the SKB lands in backlog_skb_q of a closing socket. Since iucv_sock_destruct() fails to purge backlog_skb_q, is the SKB permanently leaked? [Severity: High] This is a pre-existing issue, but do newly allocated IUCV sockets leak memory and references when an error occurs during connection request processing? In afiucv_hs_callback_syn(), a new socket is allocated via iucv_sock_alloc(), which clears its SOCK_ZAPPED flag. On the error path, it calls iucv_sock_kill(nsk): net/iucv/af_iucv.c:afiucv_hs_callback_syn() { ... err = dev_queue_xmit(skb); if (nsk) iucv_sock_kill(nsk); ... } However, iucv_sock_kill() returns immediately if !sock_flag(sk, SOCK_ZAPPED): net/iucv/af_iucv.c:iucv_sock_kill() { ... if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; ... } Since the socket is neither unlinked nor freed, can a remote sender exploit this by spamming AF_IUCV SYN packets to exhaust kernel memory? [Severity: High] This isn't a bug introduced by this patch, but do incoming packets bypass sk_rcvbuf limits? Incoming data packets are appended to the socket's receive queue via __sock_queue_rcv_skb(). If this fails because the receive buffer limit is exceeded, the packet is queued to iucv->backlog_skb_q: net/iucv/af_iucv.c:afiucv_hs_callback_rx() { ... if (__sock_queue_rcv_skb(sk, skb)) /* handle rcv queue full */ skb_queue_tail(&iucv->backlog_skb_q, skb); ... } Since backlog_skb_q lacks length limits, could a remote attacker sending a continuous stream of packets force the kernel to continuously allocate SKBs and bypass flow control limits? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260815-b4-disp-dc82fde4-v1-0-e83b10b22ce9@proton.me?part=1 ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces 2026-08-15 16:07 ` Bryam Vargas (?) (?) @ 2026-08-19 15:06 ` Alexandra Winter 2026-08-21 11:42 ` Bryam Vargas -1 siblings, 1 reply; 15+ messages in thread From: Alexandra Winter @ 2026-08-19 15:06 UTC (permalink / raw) To: hexlabsecurity, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 On 15.08.26 18:07, Bryam Vargas via B4 Relay wrote: > From: Bryam Vargas <hexlabsecurity@proton.me> > > iucv_packet_type sets neither .dev nor .af_packet_net, so it lands in the > machine-global ptype_base[] that __netif_receive_skb_core walks for every > frame in every namespace, and afiucv_hs_rcv() ignores its dev argument. > An ETH_P_AF_IUCV frame sent from any namespace holding CAP_NET_RAW is > therefore matched against the global iucv_sk_list and can move a socket > owned by the initial namespace: afiucv_hs_callback_synfin() and _fin() > overwrite its sk_state, and _syn() builds an accept-queue child. > > Filter on the namespace. The core does not do it for ptype_base[] -- > net/core/dev.c leaves namespace filtering to the ptype owner -- and > net/x25/x25_dev.c and net/ieee802154/socket.c both test dev_net(dev) at > exactly this point. > > Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") > Cc: stable@vger.kernel.org > Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> > --- > net/iucv/af_iucv.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c > index ea047bab65e7..e3ec965d96ca 100644 > --- a/net/iucv/af_iucv.c > +++ b/net/iucv/af_iucv.c > @@ -2064,6 +2064,11 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, > int err = NET_RX_SUCCESS; > char nullstring[8]; > > + if (!net_eq(dev_net(dev), &init_net)) { > + kfree_skb(skb); > + return NET_RX_SUCCESS; > + } > + > if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { > kfree_skb(skb); > return NET_RX_SUCCESS; > I am wondering whether a check of + if (iucv_sk(sk)->hs_dev != dev) + continue; would cover a broader range of issues. I'll try to send a proper patch proposal. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces 2026-08-19 15:06 ` Alexandra Winter @ 2026-08-21 11:42 ` Bryam Vargas 2026-08-24 8:47 ` Alexandra Winter 0 siblings, 1 reply; 15+ messages in thread From: Bryam Vargas @ 2026-08-21 11:42 UTC (permalink / raw) To: Alexandra Winter, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S . Miller Cc: Hidayath Khan, Simon Horman, Ursula Braun, linux-s390, netdev, linux-kernel Alexandra, > I am wondering whether a check of > + if (iucv_sk(sk)->hs_dev != dev) > + continue; > > would cover a broader range of issues. It does, and I'd rather have yours than mine. It covers three things at once: the namespace case, since hs_dev can only come from the init_net scan in iucv_sock_bind(); the transport case the earlier patch went after, since classic sockets have hs_dev == NULL and drop out of the walk; and delivery to a socket bound to a different HiperSockets device. I went looking for the regression it could carry -- an accept-queue child left without hs_dev, which would break connection setup -- and it isn't there. The child inherits at af_iucv.c:1908. One thing it doesn't reach, and it's why I'm not dropping both: the check sits after EBCASC() has already rewritten the transport header in place at :2073-2076, and a SYN matching no socket still takes the !iucv branch at :1872-1877, which swaps the frame and hands it to dev_queue_xmit(). So it replaces 1/2 and stays complementary to 2/2. Send yours and I'll drop 1/2. Thanks, Bryam ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces 2026-08-21 11:42 ` Bryam Vargas @ 2026-08-24 8:47 ` Alexandra Winter 0 siblings, 0 replies; 15+ messages in thread From: Alexandra Winter @ 2026-08-24 8:47 UTC (permalink / raw) To: Bryam Vargas, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S . Miller Cc: Hidayath Khan, Simon Horman, linux-s390, netdev, linux-kernel On 21.08.26 13:42, Bryam Vargas wrote: > Alexandra, > >> I am wondering whether a check of >> + if (iucv_sk(sk)->hs_dev != dev) >> + continue; >> >> would cover a broader range of issues. > > It does, and I'd rather have yours than mine. It covers three things at > once: the namespace case, since hs_dev can only come from the init_net scan > in iucv_sock_bind(); the transport case the earlier patch went after, since > classic sockets have hs_dev == NULL and drop out of the walk; and delivery > to a socket bound to a different HiperSockets device. > > I went looking for the regression it could carry -- an accept-queue child > left without hs_dev, which would break connection setup -- and it isn't > there. The child inherits at af_iucv.c:1908. > > One thing it doesn't reach, and it's why I'm not dropping both: the check > sits after EBCASC() has already rewritten the transport header in place at > :2073-2076, and a SYN matching no socket still takes the !iucv branch at > :1872-1877, which swaps the frame and hands it to dev_queue_xmit(). So it > replaces 1/2 and stays complementary to 2/2. > > Send yours and I'll drop 1/2. > > Thanks, > Bryam > Thank you very much Bryam, I fully agree. I've sent R-b for 2/2, and propose to take it as it is. See https://lore.kernel.org/netdev/20260821125501.3718748-1-wintera@linux.ibm.com/ for my proposal that should supersede 1/2. Your review would be highly appreciated. ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-15 16:07 ` Bryam Vargas @ 2026-08-15 16:07 ` Bryam Vargas -1 siblings, 0 replies; 15+ messages in thread From: Bryam Vargas via B4 Relay @ 2026-08-15 16:07 UTC (permalink / raw) To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller, Alexandra Winter Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 From: Bryam Vargas <hexlabsecurity@proton.me> afiucv_hs_rcv() rewrites the frame in place -- EBCASC() converts four name fields in the transport header, afiucv_swap_src_dest() swaps them and pushes an Ethernet header back on -- without taking a private, writable copy. It sits on the global ptype_base[], so a packet socket (tcpdump is enough) has packet_rcv() clone every frame first, and net/core/dev.c has warned since 1998 that such a handler "is not able to sense, that packet is cloned and should be copied-on-write". Unshare, then cow the head, in that order: skb_cow_head() can reach pskb_expand_head(), which has BUG_ON(skb_shared()). Asking for ETH_HLEN also covers the unchecked push in afiucv_swap_src_dest() -- eth_type_trans() has already pulled that much on the ordinary path, so the call compares and returns. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Closes: https://sashiko.dev/#/patchset/20260813-b4-disp-60433a46-v1-1-509e1200533e@proton.me?part=1 Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- net/iucv/af_iucv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index e3ec965d96ca..10cfc5e82f04 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2069,11 +2069,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, return NET_RX_SUCCESS; } + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_SUCCESS; + if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { kfree_skb(skb); return NET_RX_SUCCESS; } + if (skb_cow_head(skb, ETH_HLEN)) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + trans_hdr = iucv_trans_hdr(skb); EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName)); EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID)); -- 2.55.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place @ 2026-08-15 16:07 ` Bryam Vargas 0 siblings, 0 replies; 15+ messages in thread From: Bryam Vargas @ 2026-08-15 16:07 UTC (permalink / raw) To: Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller, Alexandra Winter Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 afiucv_hs_rcv() rewrites the frame in place -- EBCASC() converts four name fields in the transport header, afiucv_swap_src_dest() swaps them and pushes an Ethernet header back on -- without taking a private, writable copy. It sits on the global ptype_base[], so a packet socket (tcpdump is enough) has packet_rcv() clone every frame first, and net/core/dev.c has warned since 1998 that such a handler "is not able to sense, that packet is cloned and should be copied-on-write". Unshare, then cow the head, in that order: skb_cow_head() can reach pskb_expand_head(), which has BUG_ON(skb_shared()). Asking for ETH_HLEN also covers the unchecked push in afiucv_swap_src_dest() -- eth_type_trans() has already pulled that much on the ordinary path, so the call compares and returns. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Closes: https://sashiko.dev/#/patchset/20260813-b4-disp-60433a46-v1-1-509e1200533e@proton.me?part=1 Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- net/iucv/af_iucv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index e3ec965d96ca..10cfc5e82f04 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2069,11 +2069,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, return NET_RX_SUCCESS; } + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_SUCCESS; + if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { kfree_skb(skb); return NET_RX_SUCCESS; } + if (skb_cow_head(skb, ETH_HLEN)) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + trans_hdr = iucv_trans_hdr(skb); EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName)); EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID)); -- 2.55.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-15 16:07 ` Bryam Vargas (?) @ 2026-08-16 16:07 ` sashiko-bot -1 siblings, 0 replies; 15+ messages in thread From: sashiko-bot @ 2026-08-16 16:07 UTC (permalink / raw) To: Bryam Vargas Cc: Heiko Carstens, Alexander Gordeev, Christian Borntraeger, Vasily Gorbik, linux-s390 Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] Unprotected socket state modification from softirq context -- --- Patch [2]: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place --- commit 656ba922e59efdf102e7aa3950857e3c0061f9b8 Author: Bryam Vargas <hexlabsecurity@proton.me> net/iucv: take a private, writable frame before rewriting it in place This commit modifies afiucv_hs_rcv() to take a private, writable copy of the socket buffer before rewriting the frame in place. It unshares the skb and uses skb_cow_head() to ensure enough headroom. Closes: https://sashiko.dev/#/patchset/20260813-b4-disp-60433a46-v1-1-509e1200533e@proton.me?part=1 > diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c > index e3ec965d96ca7..10cfc5e82f047 100644 > --- a/net/iucv/af_iucv.c > +++ b/net/iucv/af_iucv.c > @@ -2069,11 +2069,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, > return NET_RX_SUCCESS; > } > [ ... ] [Severity: High] This is a pre-existing issue, but while reviewing this file I noticed an unprotected socket state modification: net/iucv/af_iucv.c:afiucv_hs_callback_txnotify() { ... if (sk->sk_state == IUCV_CONNECTED) { sk->sk_state = IUCV_DISCONN; sk->sk_state_change(sk); } ... } Does this modification risk corrupting the socket state? When qeth hardware issues TX completions, qeth_notify_skbs() is called from softirq (NAPI) context and invokes afiucv_hs_callback_txnotify(). This modifies sk->sk_state directly without acquiring the socket lock or checking sock_owned_by_user(). Could this lead to missed state transitions or concurrent execution with process-context operations that assume exclusive access under lock_sock()? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260815-b4-disp-dc82fde4-v1-0-e83b10b22ce9@proton.me?part=2 ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-15 16:07 ` Bryam Vargas (?) (?) @ 2026-08-18 11:55 ` Alexandra Winter 2026-08-21 11:42 ` Bryam Vargas -1 siblings, 1 reply; 15+ messages in thread From: Alexandra Winter @ 2026-08-18 11:55 UTC (permalink / raw) To: hexlabsecurity, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S. Miller Cc: netdev, linux-kernel, Ursula Braun, Simon Horman, Hidayath Khan, linux-s390 On 15.08.26 18:07, Bryam Vargas via B4 Relay wrote: > From: Bryam Vargas <hexlabsecurity@proton.me> > > afiucv_hs_rcv() rewrites the frame in place -- EBCASC() converts four name > fields in the transport header, afiucv_swap_src_dest() swaps them and > pushes an Ethernet header back on -- without taking a private, writable > copy. It sits on the global ptype_base[], so a packet socket (tcpdump is > enough) has packet_rcv() clone every frame first, and net/core/dev.c has > warned since 1998 that such a handler "is not able to sense, that packet > is cloned and should be copied-on-write". > > Unshare, then cow the head, in that order: skb_cow_head() can reach > pskb_expand_head(), which has BUG_ON(skb_shared()). Asking for ETH_HLEN > also covers the unchecked push in afiucv_swap_src_dest() -- > eth_type_trans() has already pulled that much on the ordinary path, so the > call compares and returns. > > Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") > Closes: https://sashiko.dev/#/patchset/20260813-b4-disp-60433a46-v1-1-509e1200533e@proton.me?part=1 > Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> > --- Thank you for this patch Bryam. Actually Hidayath proposed the same fix to me for the same Sashiko finding and I asked him to clarify in the description what the consequences of today's problem are. "My current understanding: You use an AF_PACKET ring reader to analyze the skbs received by HS L3 interface and complain, that the skb is changed afterwards when af_iucv processes it. Is that correct? What could be the bad consequences? (I cannot think of any)" Excuse my ignorance, if it is obvious to other readers, but is the worst thing that the output of tcpdump is not correct? Is this really a problem fix then? Or should it go to net-next? > net/iucv/af_iucv.c | 9 +++++++++ > 1 file changed, 9 insertions(+) > > diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c > index e3ec965d96ca..10cfc5e82f04 100644 > --- a/net/iucv/af_iucv.c > +++ b/net/iucv/af_iucv.c > @@ -2069,11 +2069,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, > return NET_RX_SUCCESS; > } > > + skb = skb_share_check(skb, GFP_ATOMIC); > + if (!skb) > + return NET_RX_SUCCESS; > + > if (!pskb_may_pull(skb, sizeof(*trans_hdr))) { > kfree_skb(skb); > return NET_RX_SUCCESS; > } > > + if (skb_cow_head(skb, ETH_HLEN)) { > + kfree_skb(skb); > + return NET_RX_SUCCESS; > + } > + > trans_hdr = iucv_trans_hdr(skb); > EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName)); > EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID)); > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-18 11:55 ` Alexandra Winter @ 2026-08-21 11:42 ` Bryam Vargas 2026-08-21 14:55 ` Hidayath Khan 0 siblings, 1 reply; 15+ messages in thread From: Bryam Vargas @ 2026-08-21 11:42 UTC (permalink / raw) To: Alexandra Winter, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S . Miller Cc: Hidayath Khan, Simon Horman, Ursula Braun, linux-s390, netdev, linux-kernel Alexandra, > Excuse my ignorance, if it is obvious to other readers, but is the worst > thing that the output of tcpdump is not correct? Not obvious, and my description is why: it led with tcpdump, which is the mildest end of this. The order is the other way round. __netif_receive_skb_core() walks ptype_base[] at net/core/dev.c:6160, before net->ptype_specific (:6169) and orig_dev->ptype_specific (:6173). iucv_packet_type sets no .dev and no .af_packet_net, so it sits in ptype_base[] while a packet socket for ETH_P_AF_IUCV lands in one of the later lists. af_iucv runs first, and the AF_PACKET reader gets the frame after EBCASC() has rewritten the four name fields. The capture is wrong, but it was already wrong before the reader was reached. That isn't what I'd defend the patch on. Because af_iucv isn't the last matching handler in that configuration, deliver_ptype_list_skb() hands it over through deliver_skb(), which does refcount_inc(&skb->users) before calling us (dev.c:2492, :2507). We run with users == 2, and on that skb we rewrite the header in place, skb_push() 14 bytes in afiucv_swap_src_dest() and pass the same skb to dev_queue_xmit() (af_iucv.c:1876, :1888, :1914) -- including for a frame that matched no socket (:1872). What hides it in review is a guard asymmetry. deliver_skb() leaves users == 2 with skb->cloned == 0, so skb_shared() is true while skb_cloned() is false, and the copy-on-write guards all test skb_cloned() -- __pskb_pull_tail() at skbuff.c:2886 among them -- so they read the skb as already writable. The one that does test it is BUG_ON(skb_shared(skb)) at the top of pskb_expand_head() (skbuff.c:2305); skb_expand_head() carries "/* pskb_expand_head() might crash, if skb is shared. */" (:2456) for the same reason. What I don't have is a panic. On the qeth geometry the first pskb_may_pull() finds enough tailroom in the napi_get_frags() head and copies out of the frags without expanding, so it doesn't reach pskb_expand_head that way. By inspection; not reproduced. > Is this really a problem fix then? Or should it go to net-next? If the bar is a failure I can show you, net-next is right. I sent it to net because a handler that writes a shared skb and then gives it to the transmit path is a rule violation with a BUG_ON behind it, not because I can fire that BUG_ON. Your call either way, and net-next is fine by me. Worth having in the record: reaching the shared state costs one syscall -- socket(AF_PACKET, SOCK_RAW, htons(0xFBFB)), no bind, no ETH_P_ALL -- since ptype_base[] is walked before the per-namespace list. If Hidayath's version is further along, take his. I'd rather the check land than land mine. Thanks, Bryam ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-21 11:42 ` Bryam Vargas @ 2026-08-21 14:55 ` Hidayath Khan 2026-08-24 8:33 ` Alexandra Winter 0 siblings, 1 reply; 15+ messages in thread From: Hidayath Khan @ 2026-08-21 14:55 UTC (permalink / raw) To: Bryam Vargas, Alexandra Winter, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S . Miller Cc: Simon Horman, Ursula Braun, linux-s390, netdev, linux-kernel On 21/08/26 5:12 pm, Bryam Vargas wrote: > Alexandra, > >> Excuse my ignorance, if it is obvious to other readers, but is the worst >> thing that the output of tcpdump is not correct? > Not obvious, and my description is why: it led with tcpdump, which is the > mildest end of this. > > The order is the other way round. __netif_receive_skb_core() walks > ptype_base[] at net/core/dev.c:6160, before net->ptype_specific (:6169) > and orig_dev->ptype_specific (:6173). iucv_packet_type sets no .dev and no > .af_packet_net, so it sits in ptype_base[] while a packet socket for > ETH_P_AF_IUCV lands in one of the later lists. af_iucv runs first, and the > AF_PACKET reader gets the frame after EBCASC() has rewritten the four name > fields. The capture is wrong, but it was already wrong before the reader > was reached. > > That isn't what I'd defend the patch on. Because af_iucv isn't the last > matching handler in that configuration, deliver_ptype_list_skb() hands it > over through deliver_skb(), which does refcount_inc(&skb->users) before > calling us (dev.c:2492, :2507). We run with users == 2, and on that skb we > rewrite the header in place, skb_push() 14 bytes in afiucv_swap_src_dest() > and pass the same skb to dev_queue_xmit() (af_iucv.c:1876, :1888, :1914) -- > including for a frame that matched no socket (:1872). > > What hides it in review is a guard asymmetry. deliver_skb() leaves > users == 2 with skb->cloned == 0, so skb_shared() is true while > skb_cloned() is false, and the copy-on-write guards all test skb_cloned() > -- __pskb_pull_tail() at skbuff.c:2886 among them -- so they read the skb > as already writable. The one that does test it is BUG_ON(skb_shared(skb)) > at the top of pskb_expand_head() (skbuff.c:2305); skb_expand_head() carries > "/* pskb_expand_head() might crash, if skb is shared. */" (:2456) for the > same reason. > > What I don't have is a panic. On the qeth geometry the first > pskb_may_pull() finds enough tailroom in the napi_get_frags() head and > copies out of the frags without expanding, so it doesn't reach > pskb_expand_head that way. By inspection; not reproduced. > >> Is this really a problem fix then? Or should it go to net-next? > If the bar is a failure I can show you, net-next is right. I sent it to net > because a handler that writes a shared skb and then gives it to the > transmit path is a rule violation with a BUG_ON behind it, not because I > can fire that BUG_ON. Your call either way, and net-next is fine by me. > > Worth having in the record: reaching the shared state costs one syscall -- > socket(AF_PACKET, SOCK_RAW, htons(0xFBFB)), no bind, no ETH_P_ALL -- since > ptype_base[] is walked before the per-namespace list. > > If Hidayath's version is further along, take his. I'd rather the check land > than land mine. Hi Bryam, Please go ahead with your patch. I had dropped my patch and am not pursuing it. Thanks, Hidayath > > Thanks, > Bryam > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place 2026-08-21 14:55 ` Hidayath Khan @ 2026-08-24 8:33 ` Alexandra Winter 0 siblings, 0 replies; 15+ messages in thread From: Alexandra Winter @ 2026-08-24 8:33 UTC (permalink / raw) To: Hidayath Khan, Bryam Vargas, Thorsten Winkler, Jakub Kicinski, Eric Dumazet, Paolo Abeni, David S . Miller Cc: Simon Horman, Ursula Braun, linux-s390, netdev, linux-kernel On 21.08.26 16:55, Hidayath Khan wrote: > > On 21/08/26 5:12 pm, Bryam Vargas wrote: >> Alexandra, >> >>> Excuse my ignorance, if it is obvious to other readers, but is the worst >>> thing that the output of tcpdump is not correct? >> Not obvious, and my description is why: it led with tcpdump, which is the >> mildest end of this. >> >> The order is the other way round. __netif_receive_skb_core() walks >> ptype_base[] at net/core/dev.c:6160, before net->ptype_specific (:6169) >> and orig_dev->ptype_specific (:6173). iucv_packet_type sets no .dev and no >> .af_packet_net, so it sits in ptype_base[] while a packet socket for >> ETH_P_AF_IUCV lands in one of the later lists. af_iucv runs first, and the >> AF_PACKET reader gets the frame after EBCASC() has rewritten the four name >> fields. The capture is wrong, but it was already wrong before the reader >> was reached. >> >> That isn't what I'd defend the patch on. Because af_iucv isn't the last >> matching handler in that configuration, deliver_ptype_list_skb() hands it >> over through deliver_skb(), which does refcount_inc(&skb->users) before >> calling us (dev.c:2492, :2507). We run with users == 2, and on that skb we >> rewrite the header in place, skb_push() 14 bytes in afiucv_swap_src_dest() >> and pass the same skb to dev_queue_xmit() (af_iucv.c:1876, :1888, :1914) -- >> including for a frame that matched no socket (:1872). >> >> What hides it in review is a guard asymmetry. deliver_skb() leaves >> users == 2 with skb->cloned == 0, so skb_shared() is true while >> skb_cloned() is false, and the copy-on-write guards all test skb_cloned() >> -- __pskb_pull_tail() at skbuff.c:2886 among them -- so they read the skb >> as already writable. The one that does test it is BUG_ON(skb_shared(skb)) >> at the top of pskb_expand_head() (skbuff.c:2305); skb_expand_head() carries >> "/* pskb_expand_head() might crash, if skb is shared. */" (:2456) for the >> same reason. >> >> What I don't have is a panic. On the qeth geometry the first >> pskb_may_pull() finds enough tailroom in the napi_get_frags() head and >> copies out of the frags without expanding, so it doesn't reach >> pskb_expand_head that way. By inspection; not reproduced. >> >>> Is this really a problem fix then? Or should it go to net-next? >> If the bar is a failure I can show you, net-next is right. I sent it to net >> because a handler that writes a shared skb and then gives it to the >> transmit path is a rule violation with a BUG_ON behind it, not because I >> can fire that BUG_ON. Your call either way, and net-next is fine by me. >> >> Worth having in the record: reaching the shared state costs one syscall -- >> socket(AF_PACKET, SOCK_RAW, htons(0xFBFB)), no bind, no ETH_P_ALL -- since >> ptype_base[] is walked before the per-namespace list. >> >> If Hidayath's version is further along, take his. I'd rather the check land >> than land mine. > Hi Bryam, > > Please go ahead with your patch. I had dropped my patch and am not pursuing it. > > Thanks, > Hidayath >> >> Thanks, >> Bryam Thank you for your explanations, Bryam. I agree it makes sense to treat this as a fix. Reviewed-by: Alexandra Winter <wintera@linux.ibm.com> ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-08-24 8:48 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-15 16:07 [PATCH net 0/2] net/iucv: give afiucv_hs_rcv() the preamble a packet_type handler needs Bryam Vargas via B4 Relay 2026-08-15 16:07 ` Bryam Vargas 2026-08-15 16:07 ` [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces Bryam Vargas via B4 Relay 2026-08-15 16:07 ` Bryam Vargas 2026-08-16 16:07 ` sashiko-bot 2026-08-19 15:06 ` Alexandra Winter 2026-08-21 11:42 ` Bryam Vargas 2026-08-24 8:47 ` Alexandra Winter 2026-08-15 16:07 ` [PATCH net 2/2] net/iucv: take a private, writable frame before rewriting it in place Bryam Vargas via B4 Relay 2026-08-15 16:07 ` Bryam Vargas 2026-08-16 16:07 ` sashiko-bot 2026-08-18 11:55 ` Alexandra Winter 2026-08-21 11:42 ` Bryam Vargas 2026-08-21 14:55 ` Hidayath Khan 2026-08-24 8:33 ` Alexandra Winter
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.