test: cover controller destination faults and partial reads

Exercise keyframe and ring reads with unmapped, PROT_NONE, read-only and page-boundary destinations. Verify complete-event progress within one vector and across vectors, including a NULL destination. The Linux fakeinput suite passes 11 tests with 3 i386 skips; both native ARM64 probes pass on the Thor.
This commit is contained in:
Delete228 authored and GitHub committed 2026-10-20 17:55:25 +03:00
1 parent c0359d1c8c
commit 197d31c2ed
1 file changed
+80 -2
+80 -2
View File
@@ -147,6 +147,74 @@ int main(void)
return 0;
}
"""
READV_DESTINATION_CHECKS = r"""
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct input_event events[64];
int fd = open("/dev/input/event16", O_RDONLY | O_NONBLOCK);
if (fd < 0)
return 1;
if (argc > 1)
while (read(fd, events, sizeof(events)) > 0)
;
puts("ready");
fflush(stdout);
char go;
if (read(0, &go, 1) != 1)
return 2;
long page = sysconf(_SC_PAGESIZE);
char *area = mmap(NULL, 2 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (area == MAP_FAILED || mprotect(area + page, page, PROT_NONE))
return 3;
struct iovec vector = {area + page, sizeof(struct input_event)};
if (readv(fd, &vector, 1) != -1 || errno != EFAULT)
return 4;
if (read(fd, vector.iov_base, vector.iov_len) != -1 || errno != EFAULT)
return 5;
if (mprotect(area, page, PROT_READ))
return 6;
vector.iov_base = area;
if (readv(fd, &vector, 1) != -1 || errno != EFAULT)
return 7;
if (mprotect(area, page, PROT_READ | PROT_WRITE) || munmap(area + page, page))
return 8;
vector.iov_base = area + page;
if (readv(fd, &vector, 1) != -1 || errno != EFAULT)
return 9;
vector.iov_base = area + page - 1;
if (readv(fd, &vector, 1) != -1 || errno != EFAULT)
return 15;
vector.iov_base = area + page - sizeof(struct input_event);
vector.iov_len = 2 * sizeof(struct input_event);
if (readv(fd, &vector, 1) != sizeof(struct input_event))
return 10;
struct input_event *first = vector.iov_base;
if (first->type != EV_KEY || first->code != BTN_A)
return 11;
struct iovec parts[2] = {{events, sizeof(struct input_event)}, {NULL, sizeof(struct input_event)}};
if (readv(fd, parts, 2) != sizeof(struct input_event))
return 12;
if (events[0].type != (argc > 1 ? EV_ABS : EV_KEY) || events[0].code != (argc > 1 ? ABS_Y : BTN_B))
return 13;
vector.iov_base = events;
vector.iov_len = sizeof(events);
ssize_t got = readv(fd, &vector, 1);
if (got != (argc > 1 ? 1 : 18) * sizeof(struct input_event) ||
events[0].type != (argc > 1 ? EV_SYN : EV_KEY) || events[0].code != (argc > 1 ? SYN_REPORT : BTN_X))
return 14;
munmap(area, page);
puts("ok");
return 0;
}
"""
OVERLAY_READ = r"""
#include <sys/uio.h>
#include <unistd.h>
@@ -199,8 +267,9 @@ class FakeinputRingTest(unittest.TestCase):
subprocess.run(["gcc", bits, "-o", str(binary), str(self.tmp / (name + ".c"))], check=True)
return library, binary
def read_through(self, bits, vectors=None, overlay=False, virtual=True):
program = READER if virtual else READER.replace("event16", "event0")
def read_through(self, bits, vectors=None, overlay=False, virtual=True, program=READER):
if not virtual:
program = program.replace("event16", "event0")
library, reader = self.build(bits, program, "reader")
hooks, path = self.ring(virtual=virtual)
env = dict(os.environ, LD_PRELOAD=str(library), FAKE_EVDEV_DIR=str(hooks))
@@ -262,6 +331,15 @@ class FakeinputRingTest(unittest.TestCase):
self.assertEqual(run.returncode, 0, run.stderr)
self.assertEqual(run.stdout, "ok\n")
def test_readv_keyframes_handle_destination_faults_and_partial_progress(self):
self.assertEqual(self.read_through("-m64", program=READV_DESTINATION_CHECKS), "ok\n")
def test_readv_ring_events_handle_destination_faults_and_partial_progress(self):
self.assertEqual(self.read_through("-m64", vectors=1, program=READV_DESTINATION_CHECKS), "ok\n")
def test_a_32_bit_guest_handles_readv_destination_faults(self):
self.assertEqual(self.read_through("-m32", program=READV_DESTINATION_CHECKS), "ok\n")
def test_an_event_node_rejects_joydev_requests_so_sdl_reads_its_id(self):
library, probe = self.build("-m64", PROBE, "probe")
hooks, _ = self.ring()