Commit 2a689f76 authored by Melbin K Mathew's avatar Melbin K Mathew Committed by Paolo Abeni
Browse files

vsock/test: add stream TX credit bounds test



Add a regression test for the TX credit bounds fix. The test verifies
that a sender with a small local buffer size cannot queue excessive
data even when the peer advertises a large receive buffer.

The client:
  - Sets a small buffer size (64 KiB)
  - Connects to server (which advertises 2 MiB buffer)
  - Sends in non-blocking mode until EAGAIN
  - Verifies total queued data is bounded

This guards against the original vulnerability where a remote peer
could cause unbounded kernel memory allocation by advertising a large
buffer and reading slowly.

Suggested-by: default avatarStefano Garzarella <sgarzare@redhat.com>
Signed-off-by: default avatarMelbin K Mathew <mlbnkm1@gmail.com>
[Stefano: use sock_buf_size to check the bytes sent + small fixes]
Signed-off-by: default avatarStefano Garzarella <sgarzare@redhat.com>
Link: https://patch.msgid.link/20260121093628.9941-5-sgarzare@redhat.com


Acked-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 8ee784fd
Loading
Loading
Loading
Loading
+101 −0
Original line number Diff line number Diff line
@@ -347,6 +347,7 @@ static void test_stream_msg_peek_server(const struct test_opts *opts)
}

#define SOCK_BUF_SIZE (2 * 1024 * 1024)
#define SOCK_BUF_SIZE_SMALL (64 * 1024)
#define MAX_MSG_PAGES 4

static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
@@ -2230,6 +2231,101 @@ static void test_stream_accepted_setsockopt_server(const struct test_opts *opts)
	close(fd);
}

static void test_stream_tx_credit_bounds_client(const struct test_opts *opts)
{
	unsigned long long sock_buf_size;
	size_t total = 0;
	char buf[4096];
	int fd;

	memset(buf, 'A', sizeof(buf));

	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
	if (fd < 0) {
		perror("connect");
		exit(EXIT_FAILURE);
	}

	sock_buf_size = SOCK_BUF_SIZE_SMALL;

	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
			     sock_buf_size,
			     "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");

	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
			     sock_buf_size,
			     "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");

	if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
		perror("fcntl(F_SETFL)");
		exit(EXIT_FAILURE);
	}

	control_expectln("SRVREADY");

	for (;;) {
		ssize_t sent = send(fd, buf, sizeof(buf), 0);

		if (sent == 0) {
			fprintf(stderr, "unexpected EOF while sending bytes\n");
			exit(EXIT_FAILURE);
		}

		if (sent < 0) {
			if (errno == EINTR)
				continue;

			if (errno == EAGAIN || errno == EWOULDBLOCK)
				break;

			perror("send");
			exit(EXIT_FAILURE);
		}

		total += sent;
	}

	control_writeln("CLIDONE");
	close(fd);

	/* We should not be able to send more bytes than the value set as
	 * local buffer size.
	 */
	if (total > sock_buf_size) {
		fprintf(stderr,
			"TX credit too large: queued %zu bytes (expected <= %llu)\n",
			total, sock_buf_size);
		exit(EXIT_FAILURE);
	}
}

static void test_stream_tx_credit_bounds_server(const struct test_opts *opts)
{
	unsigned long long sock_buf_size;
	int fd;

	fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
	if (fd < 0) {
		perror("accept");
		exit(EXIT_FAILURE);
	}

	sock_buf_size = SOCK_BUF_SIZE;

	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
			     sock_buf_size,
			     "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");

	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
			     sock_buf_size,
			     "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");

	control_writeln("SRVREADY");
	control_expectln("CLIDONE");

	close(fd);
}

static struct test_case test_cases[] = {
	{
		.name = "SOCK_STREAM connection reset",
@@ -2419,6 +2515,11 @@ static struct test_case test_cases[] = {
		.run_client = test_stream_msgzcopy_mangle_client,
		.run_server = test_stream_msgzcopy_mangle_server,
	},
	{
		.name = "SOCK_STREAM TX credit bounds",
		.run_client = test_stream_tx_credit_bounds_client,
		.run_server = test_stream_tx_credit_bounds_server,
	},
	{},
};