[PATCH net] net/iucv: filter frames in afiucv_hs_rcv() by ingress device

From: Alexandra Winter

Date: Fri Aug 21 2026 - 08:57:06 EST


afiucv_hs_rcv() selects a socket from iucv_sk_list by matching four 8-byte
name fields in the transport header alone. No check is made against the
net_device the frame arrived on.

This can cause a frame arriving on any netdev to be delivered to an AF_IUCV
socket. Three problems follow.

First, a frame arriving over HiperSockets can be delivered to a socket
bound to the classic z/VM IUCV transport, which has iucv->hs_dev == NULL.
iucv_sock_bind() takes the classic path whenever the requested userid
matches iucv_userid, even on a guest that also has a HiperSockets device
carrying the same identifier. The child socket created by
afiucv_hs_callback_syn() for such a match inherits hs_dev = NULL and
transport = AF_IUCV_TRANS_HIPER, so the first send() on it returns -ENODEV.
The socket delivered to accept() is unusable.

Second, a frame arriving on one netdev can be delivered to a socket bound
to a different IQD device. Which can lead to
- Accept-queue exhaustion (DoS)
- Attacker-controlled peer identity in the child socket
- Data injection into existing sockets
- Fabric noise on the IQD fabric, where bogus replies are sent
- killing established connections

Third, all AF_IUCV sockets live in init_net, as iucv_sock_alloc() calls
sk_alloc(&init_net, ...). But even frames arriving on netdev devices in a
namespace can be delivered to an IUCV socket. So a process in an
unprivileged user and network namespace holding only the CAP_NET_RAW
capability valid within that namespace can send a raw ETH_P_AF_IUCV frame
on its own lo device and have it matched against init_net sockets.

Fix all three by skipping any socket whose hs_dev does not match the
ingress device. A classic z/VM IUCV socket has hs_dev == NULL; the ingress
dev is never NULL, so classic sockets are skipped automatically. An unbound
HIPER socket also has hs_dev == NULL and is skipped. A bound HIPER socket
is only reachable from the exact IQD device it was bound to. Because hs_dev
is always a device in init_net (iucv_sock_bind() scans
for_each_netdev_rcu(&init_net, ...) exclusively), a frame whose ingress
device belongs to another namespace never matches any socket.

Note that AF_IUCV over HiperSockets provides no per-connection
authentication: no sequence numbers, no TLS, no nonce. The four name fields
identifying a connection are exchanged in plaintext on the shared
HiperSockets segment (VCHID). Any host on the same HiperSockets segment
could spoof any frame type against an existing connection. That is a
protocol-level property unchanged by this patch. The fix reduces the attack
surface to peers present on the same HiperSockets segment.

Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport")
Cc: stable@xxxxxxxxxxxxxxx
Co-developed-by: Bryam Vargas <hexlabsecurity@xxxxxxxxx>
Signed-off-by: Alexandra Winter <wintera@xxxxxxxxxxxxx>
---
I think this fix covers the issues adressed by [1] and [2], and
further reduces the attack surface.

Bryam, would you accept a Co-developed-by, as you did the analysis
and you proposed to add checks in afiucv_hs_rcv()?

[1] [PATCH net] net/iucv: only deliver HiperSockets frames to HiperSockets sockets
Link: https://lore.kernel.org/netdev/20260813-b4-disp-60433a46-v1-1-509e1200533e@xxxxxxxxx/ [1]
[2] [PATCH net 1/2] net/iucv: drop HiperSockets frames from other network namespaces
Link: https://lore.kernel.org/netdev/20260815-b4-disp-dc82fde4-v1-1-e83b10b22ce9@xxxxxxxxx/ [2]

---
net/iucv/af_iucv.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index ea047bab65e7..4e5cc9da6e06 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2079,6 +2079,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
sk = NULL;
read_lock(&iucv_sk_list.lock);
sk_for_each(sk, &iucv_sk_list.head) {
+ if (iucv_sk(sk)->hs_dev != dev)
+ continue;
if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
if ((!memcmp(&iucv_sk(sk)->src_name,
trans_hdr->destAppName, 8)) &&
--
2.53.0