Commit 204a5efd authored by Chuck Lever's avatar Chuck Lever Committed by Paolo Abeni
Browse files

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: default avatarChuck Lever <chuck.lever@oracle.com>
Reviewed-by: default avatarHannes Reinecke <hare@kernel.org>
Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-7-66c616906ead@oracle.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 5da98f55
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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