brintos

brintos / linux-shallow public Read only

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