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

nosymfollow-test.c (4515B)


      1// SPDX-License-Identifier: GPL-2.0
      2#define _GNU_SOURCE
      3#include <errno.h>
      4#include <fcntl.h>
      5#include <limits.h>
      6#include <sched.h>
      7#include <stdarg.h>
      8#include <stdbool.h>
      9#include <stdio.h>
     10#include <stdlib.h>
     11#include <string.h>
     12#include <sys/mount.h>
     13#include <sys/stat.h>
     14#include <sys/types.h>
     15#include <sys/vfs.h>
     16#include <unistd.h>
     17
     18#ifndef MS_NOSYMFOLLOW
     19# define MS_NOSYMFOLLOW 256     /* Do not follow symlinks */
     20#endif
     21
     22#ifndef ST_NOSYMFOLLOW
     23# define ST_NOSYMFOLLOW 0x2000  /* Do not follow symlinks */
     24#endif
     25
     26#define DATA "/tmp/data"
     27#define LINK "/tmp/symlink"
     28#define TMP  "/tmp"
     29
     30static void die(char *fmt, ...)
     31{
     32	va_list ap;
     33
     34	va_start(ap, fmt);
     35	vfprintf(stderr, fmt, ap);
     36	va_end(ap);
     37	exit(EXIT_FAILURE);
     38}
     39
     40static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt,
     41		va_list ap)
     42{
     43	ssize_t written;
     44	char buf[4096];
     45	int buf_len;
     46	int fd;
     47
     48	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
     49	if (buf_len < 0)
     50		die("vsnprintf failed: %s\n", strerror(errno));
     51
     52	if (buf_len >= sizeof(buf))
     53		die("vsnprintf output truncated\n");
     54
     55	fd = open(filename, O_WRONLY);
     56	if (fd < 0) {
     57		if ((errno == ENOENT) && enoent_ok)
     58			return;
     59		die("open of %s failed: %s\n", filename, strerror(errno));
     60	}
     61
     62	written = write(fd, buf, buf_len);
     63	if (written != buf_len) {
     64		if (written >= 0) {
     65			die("short write to %s\n", filename);
     66		} else {
     67			die("write to %s failed: %s\n",
     68				filename, strerror(errno));
     69		}
     70	}
     71
     72	if (close(fd) != 0)
     73		die("close of %s failed: %s\n", filename, strerror(errno));
     74}
     75
     76static void maybe_write_file(char *filename, char *fmt, ...)
     77{
     78	va_list ap;
     79
     80	va_start(ap, fmt);
     81	vmaybe_write_file(true, filename, fmt, ap);
     82	va_end(ap);
     83}
     84
     85static void write_file(char *filename, char *fmt, ...)
     86{
     87	va_list ap;
     88
     89	va_start(ap, fmt);
     90	vmaybe_write_file(false, filename, fmt, ap);
     91	va_end(ap);
     92}
     93
     94static void create_and_enter_ns(void)
     95{
     96	uid_t uid = getuid();
     97	gid_t gid = getgid();
     98
     99	if (unshare(CLONE_NEWUSER) != 0)
    100		die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno));
    101
    102	maybe_write_file("/proc/self/setgroups", "deny");
    103	write_file("/proc/self/uid_map", "0 %d 1", uid);
    104	write_file("/proc/self/gid_map", "0 %d 1", gid);
    105
    106	if (setgid(0) != 0)
    107		die("setgid(0) failed %s\n", strerror(errno));
    108	if (setuid(0) != 0)
    109		die("setuid(0) failed %s\n", strerror(errno));
    110
    111	if (unshare(CLONE_NEWNS) != 0)
    112		die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno));
    113}
    114
    115static void setup_symlink(void)
    116{
    117	int data, err;
    118
    119	data = creat(DATA, O_RDWR);
    120	if (data < 0)
    121		die("creat failed: %s\n", strerror(errno));
    122
    123	err = symlink(DATA, LINK);
    124	if (err < 0)
    125		die("symlink failed: %s\n", strerror(errno));
    126
    127	if (close(data) != 0)
    128		die("close of %s failed: %s\n", DATA, strerror(errno));
    129}
    130
    131static void test_link_traversal(bool nosymfollow)
    132{
    133	int link;
    134
    135	link = open(LINK, 0, O_RDWR);
    136	if (nosymfollow) {
    137		if ((link != -1 || errno != ELOOP)) {
    138			die("link traversal unexpected result: %d, %s\n",
    139					link, strerror(errno));
    140		}
    141	} else {
    142		if (link < 0)
    143			die("link traversal failed: %s\n", strerror(errno));
    144
    145		if (close(link) != 0)
    146			die("close of link failed: %s\n", strerror(errno));
    147	}
    148}
    149
    150static void test_readlink(void)
    151{
    152	char buf[4096];
    153	ssize_t ret;
    154
    155	bzero(buf, sizeof(buf));
    156
    157	ret = readlink(LINK, buf, sizeof(buf));
    158	if (ret < 0)
    159		die("readlink failed: %s\n", strerror(errno));
    160	if (strcmp(buf, DATA) != 0)
    161		die("readlink strcmp failed: '%s' '%s'\n", buf, DATA);
    162}
    163
    164static void test_realpath(void)
    165{
    166	char *path = realpath(LINK, NULL);
    167
    168	if (!path)
    169		die("realpath failed: %s\n", strerror(errno));
    170	if (strcmp(path, DATA) != 0)
    171		die("realpath strcmp failed\n");
    172
    173	free(path);
    174}
    175
    176static void test_statfs(bool nosymfollow)
    177{
    178	struct statfs buf;
    179	int ret;
    180
    181	ret = statfs(TMP, &buf);
    182	if (ret)
    183		die("statfs failed: %s\n", strerror(errno));
    184
    185	if (nosymfollow) {
    186		if ((buf.f_flags & ST_NOSYMFOLLOW) == 0)
    187			die("ST_NOSYMFOLLOW not set on %s\n", TMP);
    188	} else {
    189		if ((buf.f_flags & ST_NOSYMFOLLOW) != 0)
    190			die("ST_NOSYMFOLLOW set on %s\n", TMP);
    191	}
    192}
    193
    194static void run_tests(bool nosymfollow)
    195{
    196	test_link_traversal(nosymfollow);
    197	test_readlink();
    198	test_realpath();
    199	test_statfs(nosymfollow);
    200}
    201
    202int main(int argc, char **argv)
    203{
    204	create_and_enter_ns();
    205
    206	if (mount("testing", TMP, "ramfs", 0, NULL) != 0)
    207		die("mount failed: %s\n", strerror(errno));
    208
    209	setup_symlink();
    210	run_tests(false);
    211
    212	if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0)
    213		die("remount failed: %s\n", strerror(errno));
    214
    215	run_tests(true);
    216
    217	return EXIT_SUCCESS;
    218}