mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 02:53:36 +02:00
net/handshake: Verify file-reference balance in submit paths
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>
This commit is contained in:
parent
5da98f55b1
commit
204a5efde5
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user