From: Cyril Hrubis <chrubis@suse.cz>
To: Andrea Cervesato <andrea.cervesato@suse.de>
Cc: Linux Test Project <ltp@lists.linux.it>
Subject: Re: [LTP] [PATCH] sockets/xfrm02: Add ESP-in-TCP page cache corruption test
Date: Wed, 13 May 2026 17:00:03 +0200 [thread overview]
Message-ID: <agSR86ObHrfu7mjP@yuki.lan> (raw)
In-Reply-To: <20260513-fragnesia-v1-1-80c5b3b09005@suse.com>
Hi!
> +#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)
> +
> +#define XFRM_CMD \
> + "ip xfrm state add" \
> + " src ::1 dst ::1" \
> + " proto esp spi 0x%08x" \
> + " encap espintcp %d %d ::" \
> + " aead 'rfc4106(gcm(aes))' %s 128" \
> + " mode transport"
> +
> +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 cmd[512];
> + 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(cmd, sizeof(cmd), XFRM_CMD, SPI, TCP_PORT, TCP_PORT, keyhex);
> +
> + ret = tst_system(cmd);
> + if (ret)
> + tst_brk(TBROK, "Failed to install xfrm ESP-in-TCP state");
I do not like this part that much. We have tst_cmd() that is better than
tst_system() and also netlink library which could probably do the same.
Given that this is important we can go with this for now, but I would
like to see it fixed later.
> + 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";
> + char drain;
> + int acc_fd;
> + loff_t off;
> + pid_t pid;
> + int status;
> +
> + 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);
> +
> + pid = SAFE_FORK();
> +
> + if (!pid) {
> + 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_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);
> +
> + TST_CHECKPOINT_WAKE(0);
> + exit(0);
> + }
> +
> + acc_fd = SAFE_ACCEPT(srv_fd, NULL, NULL);
> + SAFE_CLOSE(srv_fd);
> +
> + TST_CHECKPOINT_WAIT(0);
> +
> + SAFE_SETSOCKOPT(acc_fd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp));
> +
> + /* Kick the espintcp strparser to process buffered ESP data */
> + SAFE_RECV(0, acc_fd, &drain, 1, 0);
> +
> + SAFE_CLOSE(acc_fd);
> + SAFE_WAITPID(pid, &status, 0);
This WAITPID shouldn't be here, it will mask any errors from the SAFE_
macros in the child.
> +}
> +
> +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);
Since we corrupted the page cache when the test fails maybe we should
drop it on failure here just in case?
I.e. write 1 to /proc/sys/vm/drop_caches?
> +}
> +
> +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_checkpoints = 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
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-05-13 15:00 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 14:36 [LTP] [PATCH] sockets/xfrm02: Add ESP-in-TCP page cache corruption test Andrea Cervesato
2026-05-13 15:00 ` Cyril Hrubis [this message]
2026-05-13 16:27 ` [LTP] " linuxtestproject.agent
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=agSR86ObHrfu7mjP@yuki.lan \
--to=chrubis@suse.cz \
--cc=andrea.cervesato@suse.de \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.