cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

openat2_test.c (10041B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Author: Aleksa Sarai <cyphar@cyphar.com>
      4 * Copyright (C) 2018-2019 SUSE LLC.
      5 */
      6
      7#define _GNU_SOURCE
      8#include <fcntl.h>
      9#include <sched.h>
     10#include <sys/stat.h>
     11#include <sys/types.h>
     12#include <sys/mount.h>
     13#include <stdlib.h>
     14#include <stdbool.h>
     15#include <string.h>
     16
     17#include "../kselftest.h"
     18#include "helpers.h"
     19
     20/*
     21 * O_LARGEFILE is set to 0 by glibc.
     22 * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
     23 */
     24#undef	O_LARGEFILE
     25#ifdef __aarch64__
     26#define	O_LARGEFILE 0x20000
     27#else
     28#define	O_LARGEFILE 0x8000
     29#endif
     30
     31struct open_how_ext {
     32	struct open_how inner;
     33	uint32_t extra1;
     34	char pad1[128];
     35	uint32_t extra2;
     36	char pad2[128];
     37	uint32_t extra3;
     38};
     39
     40struct struct_test {
     41	const char *name;
     42	struct open_how_ext arg;
     43	size_t size;
     44	int err;
     45};
     46
     47#define NUM_OPENAT2_STRUCT_TESTS 7
     48#define NUM_OPENAT2_STRUCT_VARIATIONS 13
     49
     50void test_openat2_struct(void)
     51{
     52	int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
     53
     54	struct struct_test tests[] = {
     55		/* Normal struct. */
     56		{ .name = "normal struct",
     57		  .arg.inner.flags = O_RDONLY,
     58		  .size = sizeof(struct open_how) },
     59		/* Bigger struct, with zeroed out end. */
     60		{ .name = "bigger struct (zeroed out)",
     61		  .arg.inner.flags = O_RDONLY,
     62		  .size = sizeof(struct open_how_ext) },
     63
     64		/* TODO: Once expanded, check zero-padding. */
     65
     66		/* Smaller than version-0 struct. */
     67		{ .name = "zero-sized 'struct'",
     68		  .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
     69		{ .name = "smaller-than-v0 struct",
     70		  .arg.inner.flags = O_RDONLY,
     71		  .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
     72
     73		/* Bigger struct, with non-zero trailing bytes. */
     74		{ .name = "bigger struct (non-zero data in first 'future field')",
     75		  .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
     76		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
     77		{ .name = "bigger struct (non-zero data in middle of 'future fields')",
     78		  .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
     79		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
     80		{ .name = "bigger struct (non-zero data at end of 'future fields')",
     81		  .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
     82		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
     83	};
     84
     85	BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
     86	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
     87
     88	for (int i = 0; i < ARRAY_LEN(tests); i++) {
     89		struct struct_test *test = &tests[i];
     90		struct open_how_ext how_ext = test->arg;
     91
     92		for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
     93			int fd, misalign = misalignments[j];
     94			char *fdpath = NULL;
     95			bool failed;
     96			void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
     97
     98			void *copy = NULL, *how_copy = &how_ext;
     99
    100			if (!openat2_supported) {
    101				ksft_print_msg("openat2(2) unsupported\n");
    102				resultfn = ksft_test_result_skip;
    103				goto skip;
    104			}
    105
    106			if (misalign) {
    107				/*
    108				 * Explicitly misalign the structure copying it with the given
    109				 * (mis)alignment offset. The other data is set to be non-zero to
    110				 * make sure that non-zero bytes outside the struct aren't checked
    111				 *
    112				 * This is effectively to check that is_zeroed_user() works.
    113				 */
    114				copy = malloc(misalign + sizeof(how_ext));
    115				how_copy = copy + misalign;
    116				memset(copy, 0xff, misalign);
    117				memcpy(how_copy, &how_ext, sizeof(how_ext));
    118			}
    119
    120			fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
    121			if (test->err >= 0)
    122				failed = (fd < 0);
    123			else
    124				failed = (fd != test->err);
    125			if (fd >= 0) {
    126				fdpath = fdreadlink(fd);
    127				close(fd);
    128			}
    129
    130			if (failed) {
    131				resultfn = ksft_test_result_fail;
    132
    133				ksft_print_msg("openat2 unexpectedly returned ");
    134				if (fdpath)
    135					ksft_print_msg("%d['%s']\n", fd, fdpath);
    136				else
    137					ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
    138			}
    139
    140skip:
    141			if (test->err >= 0)
    142				resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
    143					 test->name, misalign);
    144			else
    145				resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
    146					 test->name, misalign, test->err,
    147					 strerror(-test->err));
    148
    149			free(copy);
    150			free(fdpath);
    151			fflush(stdout);
    152		}
    153	}
    154}
    155
    156struct flag_test {
    157	const char *name;
    158	struct open_how how;
    159	int err;
    160};
    161
    162#define NUM_OPENAT2_FLAG_TESTS 25
    163
    164void test_openat2_flags(void)
    165{
    166	struct flag_test tests[] = {
    167		/* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
    168		{ .name = "incompatible flags (O_TMPFILE | O_PATH)",
    169		  .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
    170		{ .name = "incompatible flags (O_TMPFILE | O_CREAT)",
    171		  .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
    172
    173		/* O_PATH only permits certain other flags to be set ... */
    174		{ .name = "compatible flags (O_PATH | O_CLOEXEC)",
    175		  .how.flags = O_PATH | O_CLOEXEC },
    176		{ .name = "compatible flags (O_PATH | O_DIRECTORY)",
    177		  .how.flags = O_PATH | O_DIRECTORY },
    178		{ .name = "compatible flags (O_PATH | O_NOFOLLOW)",
    179		  .how.flags = O_PATH | O_NOFOLLOW },
    180		/* ... and others are absolutely not permitted. */
    181		{ .name = "incompatible flags (O_PATH | O_RDWR)",
    182		  .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
    183		{ .name = "incompatible flags (O_PATH | O_CREAT)",
    184		  .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
    185		{ .name = "incompatible flags (O_PATH | O_EXCL)",
    186		  .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
    187		{ .name = "incompatible flags (O_PATH | O_NOCTTY)",
    188		  .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
    189		{ .name = "incompatible flags (O_PATH | O_DIRECT)",
    190		  .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
    191		{ .name = "incompatible flags (O_PATH | O_LARGEFILE)",
    192		  .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
    193
    194		/* ->mode must only be set with O_{CREAT,TMPFILE}. */
    195		{ .name = "non-zero how.mode and O_RDONLY",
    196		  .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
    197		{ .name = "non-zero how.mode and O_PATH",
    198		  .how.flags = O_PATH,   .how.mode = 0600, .err = -EINVAL },
    199		{ .name = "valid how.mode and O_CREAT",
    200		  .how.flags = O_CREAT,  .how.mode = 0600 },
    201		{ .name = "valid how.mode and O_TMPFILE",
    202		  .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
    203		/* ->mode must only contain 0777 bits. */
    204		{ .name = "invalid how.mode and O_CREAT",
    205		  .how.flags = O_CREAT,
    206		  .how.mode = 0xFFFF, .err = -EINVAL },
    207		{ .name = "invalid (very large) how.mode and O_CREAT",
    208		  .how.flags = O_CREAT,
    209		  .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
    210		{ .name = "invalid how.mode and O_TMPFILE",
    211		  .how.flags = O_TMPFILE | O_RDWR,
    212		  .how.mode = 0x1337, .err = -EINVAL },
    213		{ .name = "invalid (very large) how.mode and O_TMPFILE",
    214		  .how.flags = O_TMPFILE | O_RDWR,
    215		  .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
    216
    217		/* ->resolve flags must not conflict. */
    218		{ .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
    219		  .how.flags = O_RDONLY,
    220		  .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
    221		  .err = -EINVAL },
    222
    223		/* ->resolve must only contain RESOLVE_* flags. */
    224		{ .name = "invalid how.resolve and O_RDONLY",
    225		  .how.flags = O_RDONLY,
    226		  .how.resolve = 0x1337, .err = -EINVAL },
    227		{ .name = "invalid how.resolve and O_CREAT",
    228		  .how.flags = O_CREAT,
    229		  .how.resolve = 0x1337, .err = -EINVAL },
    230		{ .name = "invalid how.resolve and O_TMPFILE",
    231		  .how.flags = O_TMPFILE | O_RDWR,
    232		  .how.resolve = 0x1337, .err = -EINVAL },
    233		{ .name = "invalid how.resolve and O_PATH",
    234		  .how.flags = O_PATH,
    235		  .how.resolve = 0x1337, .err = -EINVAL },
    236
    237		/* currently unknown upper 32 bit rejected. */
    238		{ .name = "currently unknown bit (1 << 63)",
    239		  .how.flags = O_RDONLY | (1ULL << 63),
    240		  .how.resolve = 0, .err = -EINVAL },
    241	};
    242
    243	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
    244
    245	for (int i = 0; i < ARRAY_LEN(tests); i++) {
    246		int fd, fdflags = -1;
    247		char *path, *fdpath = NULL;
    248		bool failed = false;
    249		struct flag_test *test = &tests[i];
    250		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
    251
    252		if (!openat2_supported) {
    253			ksft_print_msg("openat2(2) unsupported\n");
    254			resultfn = ksft_test_result_skip;
    255			goto skip;
    256		}
    257
    258		path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
    259		unlink(path);
    260
    261		fd = sys_openat2(AT_FDCWD, path, &test->how);
    262		if (fd < 0 && fd == -EOPNOTSUPP) {
    263			/*
    264			 * Skip the testcase if it failed because not supported
    265			 * by FS. (e.g. a valid O_TMPFILE combination on NFS)
    266			 */
    267			ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
    268					      test->name, fd, strerror(-fd));
    269			goto next;
    270		}
    271
    272		if (test->err >= 0)
    273			failed = (fd < 0);
    274		else
    275			failed = (fd != test->err);
    276		if (fd >= 0) {
    277			int otherflags;
    278
    279			fdpath = fdreadlink(fd);
    280			fdflags = fcntl(fd, F_GETFL);
    281			otherflags = fcntl(fd, F_GETFD);
    282			close(fd);
    283
    284			E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
    285			E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
    286
    287			/* O_CLOEXEC isn't shown in F_GETFL. */
    288			if (otherflags & FD_CLOEXEC)
    289				fdflags |= O_CLOEXEC;
    290			/* O_CREAT is hidden from F_GETFL. */
    291			if (test->how.flags & O_CREAT)
    292				fdflags |= O_CREAT;
    293			if (!(test->how.flags & O_LARGEFILE))
    294				fdflags &= ~O_LARGEFILE;
    295			failed |= (fdflags != test->how.flags);
    296		}
    297
    298		if (failed) {
    299			resultfn = ksft_test_result_fail;
    300
    301			ksft_print_msg("openat2 unexpectedly returned ");
    302			if (fdpath)
    303				ksft_print_msg("%d['%s'] with %X (!= %X)\n",
    304					       fd, fdpath, fdflags,
    305					       test->how.flags);
    306			else
    307				ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
    308		}
    309
    310skip:
    311		if (test->err >= 0)
    312			resultfn("openat2 with %s succeeds\n", test->name);
    313		else
    314			resultfn("openat2 with %s fails with %d (%s)\n",
    315				 test->name, test->err, strerror(-test->err));
    316next:
    317		free(fdpath);
    318		fflush(stdout);
    319	}
    320}
    321
    322#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
    323		   NUM_OPENAT2_FLAG_TESTS)
    324
    325int main(int argc, char **argv)
    326{
    327	ksft_print_header();
    328	ksft_set_plan(NUM_TESTS);
    329
    330	test_openat2_struct();
    331	test_openat2_flags();
    332
    333	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
    334		ksft_exit_fail();
    335	else
    336		ksft_exit_pass();
    337}