/* SPDX-License-Identifier: GPL-2.0 */ /* * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args * Copyright (c) 2018 Andrew Lutomirski */ #define _GNU_SOURCE #include #include #include #include #include #include static int nerrs; #define X32_BIT 0x40000000UL static void check_enosys(unsigned long nr, bool *ok) { /* If this fails, a segfault is reasonably likely. */ fflush(stdout); long ret = syscall(nr, 0, 0, 0, 0, 0, 0); if (ret == 0) { printf("[FAIL]\tsyscall %lu succeeded, but it should have failed\n", nr); *ok = false; } else if (errno != ENOSYS) { printf("[FAIL]\tsyscall %lu had error code %d, but it should have reported ENOSYS\n", nr, errno); *ok = false; } } static void test_x32_without_x32_bit(void) { bool ok = true; /* * Syscalls 512-547 are "x32" syscalls. They are intended to be * called with the x32 (0x40000000) bit set. Calling them without * the x32 bit set is nonsense and should not work. */ printf("[RUN]\tChecking syscalls 512-547\n"); for (int i = 512; i <= 547; i++) check_enosys(i, &ok); /* * Check that a handful of 64-bit-only syscalls are rejected if the x32 * bit is set. */ printf("[RUN]\tChecking some 64-bit syscalls in x32 range\n"); check_enosys(16 | X32_BIT, &ok); /* ioctl */ check_enosys(19 | X32_BIT, &ok); /* readv */ check_enosys(20 | X32_BIT, &ok); /* writev */ /* * Check some syscalls with high bits set. */ printf("[RUN]\tChecking numbers above 2^32-1\n"); check_enosys((1UL << 32), &ok); check_enosys(X32_BIT | (1UL << 32), &ok); if (!ok) nerrs++; else printf("[OK]\tThey all returned -ENOSYS\n"); } int main() { /* * Anyone diagnosing a failure will want to know whether the kernel * supports x32. Tell them. */ printf("\tChecking for x32..."); fflush(stdout); if (syscall(39 | X32_BIT, 0, 0, 0, 0, 0, 0) >= 0) { printf(" supported\n"); } else if (errno == ENOSYS) { printf(" not supported\n"); } else { printf(" confused\n"); } test_x32_without_x32_bit(); return nerrs ? 1 : 0; }