From e804e3dbf7a407fec1cb621706ffcc48d6ba6ada Mon Sep 17 00:00:00 2001 From: 1chung <1chung.hu@proton.me> Date: Thu, 10 Sep 2026 10:47:33 +0800 Subject: [PATCH 1/2] net: UDP recv with MSG_TRUNC must return the datagram length Nrecv_no_select()/Nread() with MSG_TRUNC returned the number of bytes copied into the caller's buffer. For a datagram socket recv() reports the full datagram length, which is what the caller has to account; returning the copied length (12 bytes) made 'iperf3 -u --skip-rx-copy' report roughly 1/100 of the real throughput (0.07 Gbit/s for a 6.5 Gbit/s stream). Return the datagram length for SOCK_DGRAM; TCP behaviour is unchanged. Co-Authored-By: Claude Fable 5.1 --- src/net.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/net.c b/src/net.c index a8c327908..4f4283fc0 100644 --- a/src/net.c +++ b/src/net.c @@ -459,6 +459,14 @@ Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt) break; if (sock_opt & MSG_TRUNC) { + /* + * On a datagram socket MSG_TRUNC returns the full datagram length + * even though only `count` bytes were copied. That length is the + * value the caller must account (fastpath fix: upstream returned + * `count`, so UDP --skip-rx-copy under-reported throughput ~100x). + */ + if (prot == SOCK_DGRAM) /* Pudp; iperf_api.h is not included here */ + return r; size_t bytes_copied = (r > nleft)? nleft: r; nleft -= bytes_copied; buf += bytes_copied; @@ -538,6 +546,14 @@ Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt) break; if (sock_opt & MSG_TRUNC) { + /* + * On a datagram socket MSG_TRUNC returns the full datagram length + * even though only `count` bytes were copied. That length is the + * value the caller must account (fastpath fix: upstream returned + * `count`, so UDP --skip-rx-copy under-reported throughput ~100x). + */ + if (prot == SOCK_DGRAM) /* Pudp; iperf_api.h is not included here */ + return r; size_t bytes_copied = (r > nleft)? nleft: r; nleft -= bytes_copied; buf += bytes_copied; From 99993504a3683290a52f4322418141811e50bffa Mon Sep 17 00:00:00 2001 From: 1chung <1chung.hu@proton.me> Date: Thu, 10 Sep 2026 22:18:31 +0800 Subject: [PATCH 2/2] net: reject UDP datagrams shorter than the header under MSG_TRUNC With MSG_TRUNC the returned length is the full datagram size. If that is smaller than the requested header length the caller would parse a header that was never received, so treat it as a hard error instead (suggested by davidBar-On in review of #2075). MIN_UDP_BLOCKSIZE equals the header length, so a well-formed stream can never trip this check. Co-Authored-By: Claude Fable 5.1 --- src/net.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/net.c b/src/net.c index 4f4283fc0..8f28c03ad 100644 --- a/src/net.c +++ b/src/net.c @@ -465,8 +465,11 @@ Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt) * value the caller must account (fastpath fix: upstream returned * `count`, so UDP --skip-rx-copy under-reported throughput ~100x). */ - if (prot == SOCK_DGRAM) /* Pudp; iperf_api.h is not included here */ + if (prot == SOCK_DGRAM) { /* Pudp; iperf_api.h is not included here */ + if (r < count) + return NET_HARDERROR; /* at least the UDP message header must be present */ return r; + } size_t bytes_copied = (r > nleft)? nleft: r; nleft -= bytes_copied; buf += bytes_copied; @@ -552,8 +555,11 @@ Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt) * value the caller must account (fastpath fix: upstream returned * `count`, so UDP --skip-rx-copy under-reported throughput ~100x). */ - if (prot == SOCK_DGRAM) /* Pudp; iperf_api.h is not included here */ + if (prot == SOCK_DGRAM) { /* Pudp; iperf_api.h is not included here */ + if (r < count) + return NET_HARDERROR; /* at least the UDP message header must be present */ return r; + } size_t bytes_copied = (r > nleft)? nleft: r; nleft -= bytes_copied; buf += bytes_copied;