]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net/handshake: Verify file-reference balance in submit paths
authorChuck Lever <chuck.lever@oracle.com>
Mon, 25 May 2026 16:51:21 +0000 (12:51 -0400)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 28 May 2026 11:35:31 +0000 (13:35 +0200)
The new file-reference contract on struct handshake_req is silently
breakable: a missing get_file() at submit or a missing fput() on an
error path leaves the file leaked but does not crash the test, so
the existing absence-of-crash checks pass either way.

Snapshot file_count(filp) before each handshake_req_submit() in
the submit-success, EAGAIN, EBUSY, and cancel tests, and assert
the expected balance after submit and again after cancel. The
already-completed cancel test also asserts the post-complete
balance, which pins down that handshake_complete() drops the
reference and that the subsequent cancel does not double-fput.
The destroy test gets the same treatment before __fput_sync(),
which double-checks that cancel's fput() ran and the only
remaining reference is the one sock_alloc_file() established.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-7-66c616906ead@oracle.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/handshake/handshake-test.c

index 9cc7a95f41207ea5e074edde74ffc38de395ea49..3dd507470d5feab50e7346c4441d9536ba717c88 100644 (file)
@@ -208,6 +208,7 @@ static void handshake_req_submit_test3(struct kunit *test)
 static void handshake_req_submit_test4(struct kunit *test)
 {
        struct handshake_req *req, *result;
+       unsigned long fcount_before;
        struct socket *sock;
        struct file *filp;
        int err;
@@ -224,8 +225,10 @@ static void handshake_req_submit_test4(struct kunit *test)
        KUNIT_ASSERT_NOT_NULL(test, sock->sk);
        sock->file = filp;
 
+       fcount_before = file_count(filp);
        err = handshake_req_submit(sock, req, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
 
        /* Act */
        result = handshake_req_hash_lookup(sock->sk);
@@ -235,11 +238,13 @@ static void handshake_req_submit_test4(struct kunit *test)
        KUNIT_EXPECT_PTR_EQ(test, req, result);
 
        handshake_req_cancel(sock->sk);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
        fput(filp);
 }
 
 static void handshake_req_submit_test5(struct kunit *test)
 {
+       unsigned long fcount_before;
        struct handshake_req *req;
        struct handshake_net *hn;
        struct socket *sock;
@@ -265,12 +270,14 @@ static void handshake_req_submit_test5(struct kunit *test)
 
        saved = hn->hn_pending;
        hn->hn_pending = hn->hn_pending_max + 1;
+       fcount_before = file_count(filp);
 
        /* Act */
        err = handshake_req_submit(sock, req, GFP_KERNEL);
 
        /* Assert */
        KUNIT_EXPECT_EQ(test, err, -EAGAIN);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        fput(filp);
        hn->hn_pending = saved;
@@ -279,6 +286,7 @@ static void handshake_req_submit_test5(struct kunit *test)
 static void handshake_req_submit_test6(struct kunit *test)
 {
        struct handshake_req *req1, *req2;
+       unsigned long fcount_before;
        struct socket *sock;
        struct file *filp;
        int err;
@@ -296,21 +304,26 @@ static void handshake_req_submit_test6(struct kunit *test)
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
        KUNIT_ASSERT_NOT_NULL(test, sock->sk);
        sock->file = filp;
+       fcount_before = file_count(filp);
 
        /* Act */
        err = handshake_req_submit(sock, req1, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
        err = handshake_req_submit(sock, req2, GFP_KERNEL);
 
        /* Assert */
        KUNIT_EXPECT_EQ(test, err, -EBUSY);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
 
        handshake_req_cancel(sock->sk);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
        fput(filp);
 }
 
 static void handshake_req_cancel_test1(struct kunit *test)
 {
+       unsigned long fcount_before;
        struct handshake_req *req;
        struct socket *sock;
        struct file *filp;
@@ -329,8 +342,10 @@ static void handshake_req_cancel_test1(struct kunit *test)
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
        sock->file = filp;
 
+       fcount_before = file_count(filp);
        err = handshake_req_submit(sock, req, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
 
        /* NB: handshake_req hasn't been accepted */
 
@@ -339,12 +354,14 @@ static void handshake_req_cancel_test1(struct kunit *test)
 
        /* Assert */
        KUNIT_EXPECT_TRUE(test, result);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        fput(filp);
 }
 
 static void handshake_req_cancel_test2(struct kunit *test)
 {
+       unsigned long fcount_before;
        struct handshake_req *req, *next;
        struct handshake_net *hn;
        struct socket *sock;
@@ -365,8 +382,10 @@ static void handshake_req_cancel_test2(struct kunit *test)
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
        sock->file = filp;
 
+       fcount_before = file_count(filp);
        err = handshake_req_submit(sock, req, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
 
        net = sock_net(sock->sk);
        hn = handshake_pernet(net);
@@ -385,12 +404,14 @@ static void handshake_req_cancel_test2(struct kunit *test)
 
        /* Assert */
        KUNIT_EXPECT_TRUE(test, result);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        fput(filp);
 }
 
 static void handshake_req_cancel_test3(struct kunit *test)
 {
+       unsigned long fcount_before;
        struct handshake_req *req, *next;
        struct handshake_net *hn;
        struct socket *sock;
@@ -411,8 +432,10 @@ static void handshake_req_cancel_test3(struct kunit *test)
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
        sock->file = filp;
 
+       fcount_before = file_count(filp);
        err = handshake_req_submit(sock, req, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
 
        net = sock_net(sock->sk);
        hn = handshake_pernet(net);
@@ -428,12 +451,14 @@ static void handshake_req_cancel_test3(struct kunit *test)
 
        /* Pretend to complete this request */
        handshake_complete(next, -ETIMEDOUT, NULL);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        /* Act */
        result = handshake_req_cancel(sock->sk);
 
        /* Assert */
        KUNIT_EXPECT_FALSE(test, result);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        fput(filp);
 }
@@ -454,6 +479,7 @@ static struct handshake_proto handshake_req_alloc_proto_destroy = {
 
 static void handshake_req_destroy_test1(struct kunit *test)
 {
+       unsigned long fcount_before;
        struct handshake_req *req;
        struct socket *sock;
        struct file *filp;
@@ -473,10 +499,12 @@ static void handshake_req_destroy_test1(struct kunit *test)
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
        sock->file = filp;
 
+       fcount_before = file_count(filp);
        err = handshake_req_submit(sock, req, GFP_KERNEL);
        KUNIT_ASSERT_EQ(test, err, 0);
 
        handshake_req_cancel(sock->sk);
+       KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
 
        /* Act */
        /* Ensure the close/release/put process has run to