lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250826-tty-tests-v1-4-e904a817df92@gmail.com>
Date: Tue, 26 Aug 2025 16:51:34 -0600
From: Abhinav Saxena <xandfury@...il.com>
To: Greg Kroah-Hartman <gregkh@...uxfoundation.org>, 
 Jiri Slaby <jirislaby@...nel.org>, Nathan Chancellor <nathan@...nel.org>, 
 Nick Desaulniers <nick.desaulniers+lkml@...il.com>, 
 Bill Wendling <morbo@...gle.com>, Justin Stitt <justinstitt@...gle.com>, 
 Kees Cook <kees@...nel.org>
Cc: linux-kernel@...r.kernel.org, linux-serial@...r.kernel.org, 
 llvm@...ts.linux.dev, linux-hardening@...r.kernel.org, 
 Abhinav Saxena <xandfury@...il.com>
Subject: [RFC PATCH 4/5] tty: Add KUnit tests for core TTY functionality

Add comprehensive KUnit tests covering fundamental TTY operations:
- Driver registration and operation validation
- Open/close lifecycle with proper cleanup
- Write operations and buffer management
- Flow control via write_room() and chars_in_buffer()
- RX data injection via line discipline
- Termios configuration for hardware parameters

Tests exercise real kernel code paths using the mock driver to ensure
TTY subsystem changes don't introduce regressions in core functionality.

Signed-off-by: Abhinav Saxena <xandfury@...il.com>
---
 drivers/tty/tests/test_tty_io_core.c | 249 +++++++++++++++++++++++++++++++++++
 1 file changed, 249 insertions(+)

diff --git a/drivers/tty/tests/test_tty_io_core.c b/drivers/tty/tests/test_tty_io_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..626160e6ed738d56575cd340b3662aaa94f46a0a
--- /dev/null
+++ b/drivers/tty/tests/test_tty_io_core.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Minimal KUnit tests for TTY core using the mock driver.
+ *
+ * Scope:
+ *  - open/close via tty_port_* paths
+ *  - write() returns cnt, write_room() is large, chars_in_buffer() == 0
+ *  - stats (writes, bytes, last len) observable via tty_mock_get_stats()
+ *  - file_ops read functionality with various termios configurations
+ *  - set_termios operations for practical use cases
+ *
+ * Keep this small and obvious—no driver-side buffering or timers.
+ *
+ * Copyright (c) 2025 Abhinav Saxena <xandury@...il.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/string.h>
+
+#include "tty_test_helpers.h"
+#include "tty_mock.h"          /* mock driver API: register/unregister/stats */
+
+/* Single-port driver used across tests (minor 0). */
+static struct tty_driver *mock_driver;
+
+/* ---------- Per-test init: sanity only ---------- */
+static int tty_core_init(struct kunit *test)
+{
+	KUNIT_ASSERT_NOT_NULL(test, mock_driver);
+
+	/* Reset mock statistics before each test */
+	tty_mock_reset_stats();
+
+	if (mock_driver && mock_driver->ports)
+		KUNIT_EXPECT_NOT_NULL(test, mock_driver->ports[0]);
+	return 0;
+}
+
+/* ---------- Tests ---------- */
+
+/*
+ * Test: Verify mock driver has all required operations wired correctly.
+ * Expected: All mandatory ops present, ports array initialised, no NULL pointers.
+ */
+static void test_driver_ops_present(struct kunit *test)
+{
+	KUNIT_ASSERT_NOT_NULL(test, mock_driver);
+
+	/* Basic ops presence and wiring checks via helper. */
+	tty_test_assert_valid_ops(test, mock_driver);
+	KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->write);
+	KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->write_room);
+	KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->chars_in_buffer);
+
+	if (mock_driver->ports)
+		KUNIT_EXPECT_NOT_NULL(test, mock_driver->ports[0]);
+}
+
+/*
+ * Test: Basic TTY lifecycle (open/write/close).
+ * Expected: write() returns byte count, stats increment correctly,
+ * ldisc/port setup.
+ */
+static void test_basic_open_write_close(struct kunit *test)
+{
+	unsigned int idx = 0;
+	struct tty_test_fixture *fx;
+	const char *msg = "Hello, tty!\n";
+	struct tty_mock_stats before, after;
+	ssize_t ret;
+
+	fx = tty_test_create_fixture(test, mock_driver, idx);
+	KUNIT_ASSERT_NOT_NULL(test, fx);
+	KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0);
+	KUNIT_ASSERT_TRUE(test, fx->opened);
+
+	KUNIT_EXPECT_TRUE(test,
+			  fx->tty && fx->tty->ldisc && fx->tty->ldisc->ops);
+	KUNIT_EXPECT_TRUE(test, !list_empty(&fx->tty->tty_files));
+	KUNIT_EXPECT_NOT_NULL(test, fx->tty->port);
+
+	before = tty_mock_get_stats();
+
+	ret = tty_test_write(fx, msg, strlen(msg));
+	KUNIT_EXPECT_EQ(test, ret, (ssize_t)strlen(msg));
+
+	after = tty_mock_get_stats();
+
+	/* Test interface contract, not implementation details */
+	KUNIT_EXPECT_GT(test, after.total_writes, before.total_writes);
+	KUNIT_EXPECT_GE(test, after.total_bytes,
+			before.total_bytes + strlen(msg));
+	KUNIT_EXPECT_GT(test, after.last_write_len, 0u);
+
+	KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0);
+}
+
+/*
+ * Test: write_room() and chars_in_buffer() consistency during operations.
+ * Expected: write_room() > 0, chars_in_buffer() == 0, room unchanged after
+ * writes.
+ */
+static void test_write_room_and_chars_in_buffer_invariants(struct kunit *test)
+{
+	unsigned int idx = 0;
+	struct tty_test_fixture *fx;
+	char buf[64];
+	unsigned int room_before, room_after;
+	ssize_t ret;
+
+	memset(buf, 'A', sizeof(buf));
+
+	fx = tty_test_create_fixture(test, mock_driver, idx);
+	KUNIT_ASSERT_NOT_NULL(test, fx);
+	KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0);
+
+	room_before = tty_write_room(fx->tty);
+	KUNIT_EXPECT_GT(test, room_before, 0u);
+	KUNIT_EXPECT_EQ(test, fx->tty->ops->chars_in_buffer(fx->tty), 0u);
+
+	ret = tty_test_write(fx, buf, sizeof(buf));
+	KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buf));
+
+	room_after = tty_write_room(fx->tty);
+	KUNIT_EXPECT_EQ(test, fx->tty->ops->chars_in_buffer(fx->tty), 0u);
+	KUNIT_EXPECT_GE(test, room_after, room_before);
+
+	KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0);
+}
+
+/*
+ * Test: RX data injection via flip buffers if line discipline supports it.
+ * Expected: tty_test_simulate_rx() returns injected byte count, or test
+ * skipped.
+ */
+static void test_flip_rx_if_supported(struct kunit *test)
+{
+	unsigned int idx = 0;
+	struct tty_test_fixture *fx;
+	int rx_result;
+
+	/* Raw byte buffer, not NUL-terminated */
+	static const unsigned char rx_data[] = "rx-line\n";
+
+	fx = tty_test_create_fixture(test, mock_driver, idx);
+	KUNIT_ASSERT_NOT_NULL(test, fx);
+	KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0);
+
+	KUNIT_EXPECT_TRUE(test,
+			  fx->tty && fx->tty->ldisc && fx->tty->ldisc->ops);
+	KUNIT_EXPECT_TRUE(test, !list_empty(&fx->tty->tty_files));
+	KUNIT_EXPECT_NOT_NULL(test, fx->tty->port);
+
+	if (tty_fx_supports_rx(fx)) {
+		rx_result = tty_test_simulate_rx(fx, rx_data, sizeof(rx_data));
+		KUNIT_EXPECT_EQ(test, rx_result, (int)sizeof(rx_data));
+	} else {
+		kunit_skip(test,
+			   "ldisc does not support RX; skipping injection");
+	}
+
+	KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0);
+}
+
+/*
+ * Test: set_termios() for hardware settings (baud rate, character size, parity).
+ * Expected: c_cflag settings persist correctly, hardware parameters validated.
+ */
+static void test_set_termios_baud_rate_and_character_size(struct kunit *test)
+{
+	unsigned int idx = 0;
+	struct tty_test_fixture *fx;
+	struct ktermios termios_before, termios_after;
+
+	fx = tty_test_create_fixture(test, mock_driver, idx);
+	KUNIT_ASSERT_NOT_NULL(test, fx);
+	KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0);
+
+	/* Get initial termios */
+	termios_before = fx->tty->termios;
+
+	/* Test baud rate changes */
+	termios_after = termios_before;
+	termios_after.c_cflag &= ~CBAUD;
+	termios_after.c_cflag |= B9600;
+	KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0);
+	KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CBAUD, B9600);
+
+	/* Test higher baud rate */
+	termios_after.c_cflag &= ~CBAUD;
+	termios_after.c_cflag |= B115200;
+	KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0);
+	KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CBAUD, B115200);
+
+	/* Test character size changes */
+	termios_after.c_cflag &= ~CSIZE;
+	termios_after.c_cflag |= CS7; /* 7-bit characters */
+	KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0);
+	KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CSIZE, CS7);
+
+	termios_after.c_cflag &= ~CSIZE;
+	termios_after.c_cflag |= CS8; /* 8-bit characters */
+	KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0);
+	KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CSIZE, CS8);
+
+	/* Test parity settings */
+	termios_after.c_cflag |= PARENB; /* Enable parity */
+	termios_after.c_cflag &= ~PARODD; /* Even parity */
+	KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0);
+	KUNIT_EXPECT_TRUE(test, fx->tty->termios.c_cflag & PARENB);
+	KUNIT_EXPECT_FALSE(test, fx->tty->termios.c_cflag & PARODD);
+
+	KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0);
+}
+
+/* ---------- Suite registration ---------- */
+
+static int tty_core_suite_init(struct kunit_suite *suite)
+{
+	return tty_mock_register(&mock_driver, NULL);
+}
+
+static void tty_core_suite_exit(struct kunit_suite *suite)
+{
+	tty_mock_unregister(mock_driver);
+	mock_driver = NULL;
+}
+
+static struct kunit_case tty_core_cases[] = {
+	KUNIT_CASE(test_driver_ops_present),
+	KUNIT_CASE(test_basic_open_write_close),
+	KUNIT_CASE(test_write_room_and_chars_in_buffer_invariants),
+	KUNIT_CASE(test_flip_rx_if_supported),
+	KUNIT_CASE(test_set_termios_baud_rate_and_character_size),
+	{}
+};
+
+static struct kunit_suite tty_core_suite = {
+	.name = "tty_io_core",
+	.init = tty_core_init,
+	.suite_init = tty_core_suite_init,
+	.suite_exit = tty_core_suite_exit,
+	.test_cases = tty_core_cases,
+};
+
+kunit_test_suite(tty_core_suite);

-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ