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-3-e904a817df92@gmail.com>
Date: Tue, 26 Aug 2025 16:51:33 -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 3/5] tty: Add mock TTY driver for KUnit testing

Implement a minimal mock TTY driver that provides deterministic
behavior for testing core TTY functionality. The driver simulates
immediate data transmission with configurable statistics tracking.

The mock driver enables testing of TTY core paths without hardware
dependencies or timing-sensitive behavior, ensuring reproducible
test results across different systems.

Signed-off-by: Abhinav Saxena <xandfury@...il.com>
---
 drivers/tty/tests/tty_mock.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tests/tty_mock.h |  34 ++++++++
 2 files changed, 220 insertions(+)

diff --git a/drivers/tty/tests/tty_mock.c b/drivers/tty/tests/tty_mock.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5488760bb83c2837bb5226e3c33ec370c2c9c07
--- /dev/null
+++ b/drivers/tty/tests/tty_mock.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Minimal mock TTY driver for KUnit tests. Based on ttynull and ttyprintk
+ *
+ * Behavior:
+ *   - write() pretends to transmit all bytes immediately
+ *   - write_room() is large
+ *   - chars_in_buffer() is 0
+ *
+ * Tracks only: total_writes, total_bytes, last_write_len
+ *
+ * Copyright (c) 2025 Abhinav Saxena <xandury@...il.com>
+ */
+
+#include <kunit/visibility.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "tty_mock.h"
+
+#define TTYMOCK_NAME "ttymock"
+#define TTYMOCK_ROOM 4096
+
+static struct tty_port mock_port; /* single port */
+
+/* --- Stats (private) --- */
+static struct {
+	u64 total_writes;
+	u64 total_bytes;
+	u32 last_write_len;
+	spinlock_t lock;
+} mock_state;
+
+/* --- tty_operations --- */
+
+static int mock_open(struct tty_struct *tty, struct file *file)
+{
+	tty->driver_data = &mock_port;
+	return tty_port_open(&mock_port, tty, file);
+}
+
+static void mock_close(struct tty_struct *tty, struct file *file)
+{
+	tty_port_close(&mock_port, tty, file);
+	tty->driver_data = NULL;
+}
+
+static ssize_t mock_write(struct tty_struct *tty, const u8 *buf, size_t cnt)
+{
+	unsigned long flags;
+
+	if (!buf)
+		return -EINVAL;
+
+	spin_lock_irqsave(&mock_state.lock, flags);
+	mock_state.total_writes++;
+	mock_state.total_bytes += cnt;
+	mock_state.last_write_len = cnt;
+	spin_unlock_irqrestore(&mock_state.lock, flags);
+
+	return cnt; /* everything written immediately */
+}
+
+static unsigned int mock_write_room(struct tty_struct *tty)
+{
+	return TTYMOCK_ROOM;
+}
+
+static unsigned int mock_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;
+}
+
+static const struct tty_operations mock_ops = {
+	.open = mock_open,
+	.close = mock_close,
+	.write = mock_write,
+	.write_room = mock_write_room,
+	.chars_in_buffer = mock_chars_in_buffer,
+};
+
+/* --- tty_port_operations --- */
+
+static bool mock_carrier_raised(struct tty_port *port)
+{
+	return true;
+}
+
+static void mock_shutdown(struct tty_port *port) { }
+
+static const struct tty_port_operations mock_port_ops = {
+	.carrier_raised = mock_carrier_raised,
+	.shutdown = mock_shutdown,
+};
+
+/* --- Public helpers --- */
+
+int tty_mock_register(struct tty_driver **out_drv, struct device *parent)
+{
+	struct tty_driver *drv;
+	struct device *dev;
+	int ret;
+
+	spin_lock_init(&mock_state.lock);
+
+	drv = tty_alloc_driver(1, TTY_DRIVER_RESET_TERMIOS |
+				  TTY_DRIVER_REAL_RAW |
+				  TTY_DRIVER_UNNUMBERED_NODE |
+				  TTY_DRIVER_DYNAMIC_DEV);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	drv->driver_name = TTYMOCK_NAME;
+	drv->name = TTYMOCK_NAME;
+	drv->type = TTY_DRIVER_TYPE_SERIAL;
+	drv->subtype = SERIAL_TYPE_NORMAL;
+	drv->init_termios = tty_std_termios;
+	tty_set_operations(drv, &mock_ops);
+
+	ret = tty_register_driver(drv);
+	if (ret) {
+		tty_driver_kref_put(drv);
+		return ret;
+	}
+
+	tty_port_init(&mock_port);
+	mock_port.ops = &mock_port_ops;
+
+	dev = tty_port_register_device(&mock_port, drv, 0, parent);
+	if (IS_ERR(dev)) {
+		ret = PTR_ERR(dev);
+		tty_unregister_driver(drv);
+		tty_driver_kref_put(drv);
+		tty_port_destroy(&mock_port);
+		return ret;
+	}
+
+	if (out_drv)
+		*out_drv = drv;
+	return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_register);
+
+void tty_mock_unregister(struct tty_driver *drv)
+{
+	if (!drv)
+		return;
+
+	tty_port_unregister_device(&mock_port, drv, 0);
+	tty_unregister_driver(drv);
+	tty_driver_kref_put(drv);
+	tty_port_destroy(&mock_port);
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_unregister);
+
+struct tty_mock_stats tty_mock_get_stats(void)
+{
+	unsigned long flags;
+	struct tty_mock_stats state;
+
+	spin_lock_irqsave(&mock_state.lock, flags);
+	state.total_writes   = mock_state.total_writes;
+	state.total_bytes    = mock_state.total_bytes;
+	state.last_write_len = mock_state.last_write_len;
+	spin_unlock_irqrestore(&mock_state.lock, flags);
+
+	return state;
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_get_stats);
+
+void tty_mock_reset_stats(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mock_state.lock, flags);
+	mock_state.total_writes = 0;
+	mock_state.total_bytes = 0;
+	mock_state.last_write_len = 0;
+	spin_unlock_irqrestore(&mock_state.lock, flags);
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_reset_stats);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/tests/tty_mock.h b/drivers/tty/tests/tty_mock.h
new file mode 100644
index 0000000000000000000000000000000000000000..e61eeccc6181fc459d8db790b29350dbf3d9f588
--- /dev/null
+++ b/drivers/tty/tests/tty_mock.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * TTY - Mock driver
+ *
+ * Copyright (c) 2025 Abhinav Saxena <xandury@...il.com>
+ *
+ */
+
+#ifndef _TTY_MOCK_H
+#define _TTY_MOCK_H
+
+#include <linux/device.h>
+#include <linux/tty_driver.h>
+#include <linux/types.h>
+
+/* Register a single-port mock tty driver and create device #0. */
+int tty_mock_register(struct tty_driver **out_drv, struct device *parent);
+/* Tear down device, unregister driver and destroy port. */
+void tty_mock_unregister(struct tty_driver *drv);
+
+/* --- Stats available to KUnit tests --- */
+struct tty_mock_stats {
+	u64 total_writes;
+	u64 total_bytes;
+	u32 last_write_len;
+};
+
+/* Returns a snapshot of counters. */
+struct tty_mock_stats tty_mock_get_stats(void);
+
+/* Reset all statistics counters to zero. */
+void tty_mock_reset_stats(void);
+
+#endif /* _TTY_MOCK_H */

-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ