summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-06-03 18:52:28 -0700
committerJakub Kicinski <kuba@kernel.org>2026-06-03 18:52:29 -0700
commitc8e14cc9ccf999336d65bd3f638329e8bb7800ef (patch)
tree9dbbd2b8dd8e1dad1675e68f5aa0fef199fe5896
parent9a85ec3dc28b6df246801c19e4d9bae6297a25b0 (diff)
parentdd8975ad710ea1f3d7c7a36295072fd5ee59ca0a (diff)
downloadlinux-c8e14cc9ccf999336d65bd3f638329e8bb7800ef.tar.gz
linux-c8e14cc9ccf999336d65bd3f638329e8bb7800ef.zip
Merge branch 'af_unix-fix-inq_len-update-issue'
Jianyu Li says: ==================== af_unix: Fix inq_len update issue From: Jianyu Li <jianyu.li@mediatek.com> This series fix the problem that inq_len is inconsistent with actual remaining byte count when only part of a skb is consumed. ==================== Link: https://patch.msgid.link/20260601113640.231897-1-jianyu.li@mediatek.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/unix/af_unix.c11
-rw-r--r--tools/testing/selftests/net/af_unix/scm_inq.c54
2 files changed, 58 insertions, 7 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index dc71ed79be4a..0d9cd977c7b7 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2886,7 +2886,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
return -EAGAIN;
}
- WRITE_ONCE(u->inq_len, u->inq_len - skb->len);
+ WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb));
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
if (skb == u->oob_skb) {
@@ -3063,11 +3063,12 @@ unlock:
unix_detach_fds(&scm, skb);
}
- if (unix_skb_len(skb))
- break;
-
spin_lock(&sk->sk_receive_queue.lock);
- WRITE_ONCE(u->inq_len, u->inq_len - skb->len);
+ WRITE_ONCE(u->inq_len, u->inq_len - chunk);
+ if (unix_skb_len(skb)) {
+ spin_unlock(&sk->sk_receive_queue.lock);
+ break;
+ }
__skb_unlink(skb, &sk->sk_receive_queue);
spin_unlock(&sk->sk_receive_queue.lock);
diff --git a/tools/testing/selftests/net/af_unix/scm_inq.c b/tools/testing/selftests/net/af_unix/scm_inq.c
index 3a86be9bda17..6268b5bf50be 100644
--- a/tools/testing/selftests/net/af_unix/scm_inq.c
+++ b/tools/testing/selftests/net/af_unix/scm_inq.c
@@ -8,8 +8,9 @@
#include "kselftest_harness.h"
-#define NR_CHUNKS 100
-#define MSG_LEN 256
+#define NR_CHUNKS 100
+#define MSG_LEN 256
+#define NR_PARTIAL_READS 3
FIXTURE(scm_inq)
{
@@ -120,4 +121,53 @@ TEST_F(scm_inq, basic)
recv_chunks(_metadata, self);
}
+TEST_F(scm_inq, partial_read)
+{
+ char buf[MSG_LEN * NR_PARTIAL_READS] = {};
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+ struct msghdr msg = {};
+ struct iovec iov = {};
+ struct cmsghdr *cmsg;
+ int err, inq, ret, i;
+ int remain;
+
+ err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int));
+ if (variant->type != SOCK_STREAM) {
+ ASSERT_EQ(-ENOPROTOOPT, -errno);
+ return;
+ }
+ ASSERT_EQ(0, err);
+
+ ret = send(self->fd[0], buf, sizeof(buf), 0);
+ ASSERT_EQ(sizeof(buf), ret);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ iov.iov_base = buf;
+ iov.iov_len = MSG_LEN;
+
+ for (i = 0; i < NR_PARTIAL_READS; i++) {
+ remain = MSG_LEN * (NR_PARTIAL_READS - 1 - i);
+
+ memset(buf, 0, MSG_LEN);
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ ret = recvmsg(self->fd[1], &msg, 0);
+ ASSERT_EQ(MSG_LEN, ret);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT_NE(NULL, cmsg);
+ ASSERT_EQ(CMSG_LEN(sizeof(int)), cmsg->cmsg_len);
+ ASSERT_EQ(SOL_SOCKET, cmsg->cmsg_level);
+ ASSERT_EQ(SCM_INQ, cmsg->cmsg_type);
+ ASSERT_EQ(remain, *(int *)CMSG_DATA(cmsg));
+
+ ret = ioctl(self->fd[1], SIOCINQ, &inq);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(remain, inq);
+ }
+}
+
TEST_HARNESS_MAIN