1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
4 * Copyright (c) 2018 Andrew Lutomirski
5 */
6
7#define _GNU_SOURCE
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <stdbool.h>
12#include <errno.h>
13#include <unistd.h>
14#include <syscall.h>
15
16static int nerrs;
17
18#define X32_BIT 0x40000000UL
19
20static void check_enosys(unsigned long nr, bool *ok)
21{
22	/* If this fails, a segfault is reasonably likely. */
23	fflush(stdout);
24
25	long ret = syscall(nr, 0, 0, 0, 0, 0, 0);
26	if (ret == 0) {
27		printf("[FAIL]\tsyscall %lu succeeded, but it should have failed\n", nr);
28		*ok = false;
29	} else if (errno != ENOSYS) {
30		printf("[FAIL]\tsyscall %lu had error code %d, but it should have reported ENOSYS\n", nr, errno);
31		*ok = false;
32	}
33}
34
35static void test_x32_without_x32_bit(void)
36{
37	bool ok = true;
38
39	/*
40	 * Syscalls 512-547 are "x32" syscalls.  They are intended to be
41	 * called with the x32 (0x40000000) bit set.  Calling them without
42	 * the x32 bit set is nonsense and should not work.
43	 */
44	printf("[RUN]\tChecking syscalls 512-547\n");
45	for (int i = 512; i <= 547; i++)
46		check_enosys(i, &ok);
47
48	/*
49	 * Check that a handful of 64-bit-only syscalls are rejected if the x32
50	 * bit is set.
51	 */
52	printf("[RUN]\tChecking some 64-bit syscalls in x32 range\n");
53	check_enosys(16 | X32_BIT, &ok);	/* ioctl */
54	check_enosys(19 | X32_BIT, &ok);	/* readv */
55	check_enosys(20 | X32_BIT, &ok);	/* writev */
56
57	/*
58	 * Check some syscalls with high bits set.
59	 */
60	printf("[RUN]\tChecking numbers above 2^32-1\n");
61	check_enosys((1UL << 32), &ok);
62	check_enosys(X32_BIT | (1UL << 32), &ok);
63
64	if (!ok)
65		nerrs++;
66	else
67		printf("[OK]\tThey all returned -ENOSYS\n");
68}
69
70int main()
71{
72	/*
73	 * Anyone diagnosing a failure will want to know whether the kernel
74	 * supports x32.  Tell them.
75	 */
76	printf("\tChecking for x32...");
77	fflush(stdout);
78	if (syscall(39 | X32_BIT, 0, 0, 0, 0, 0, 0) >= 0) {
79		printf(" supported\n");
80	} else if (errno == ENOSYS) {
81		printf(" not supported\n");
82	} else {
83		printf(" confused\n");
84	}
85
86	test_x32_without_x32_bit();
87
88	return nerrs ? 1 : 0;
89}
90