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

devpts_pts.c (6083B)


      1// SPDX-License-Identifier: GPL-2.0
      2#define _GNU_SOURCE
      3#include <errno.h>
      4#include <fcntl.h>
      5#include <sched.h>
      6#include <stdbool.h>
      7#include <stdio.h>
      8#include <stdlib.h>
      9#include <string.h>
     10#include <unistd.h>
     11#include <asm/ioctls.h>
     12#include <sys/mount.h>
     13#include <sys/wait.h>
     14#include "../kselftest.h"
     15
     16static bool terminal_dup2(int duplicate, int original)
     17{
     18	int ret;
     19
     20	ret = dup2(duplicate, original);
     21	if (ret < 0)
     22		return false;
     23
     24	return true;
     25}
     26
     27static int terminal_set_stdfds(int fd)
     28{
     29	int i;
     30
     31	if (fd < 0)
     32		return 0;
     33
     34	for (i = 0; i < 3; i++)
     35		if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
     36					       STDERR_FILENO}[i]))
     37			return -1;
     38
     39	return 0;
     40}
     41
     42static int login_pty(int fd)
     43{
     44	int ret;
     45
     46	setsid();
     47
     48	ret = ioctl(fd, TIOCSCTTY, NULL);
     49	if (ret < 0)
     50		return -1;
     51
     52	ret = terminal_set_stdfds(fd);
     53	if (ret < 0)
     54		return -1;
     55
     56	if (fd > STDERR_FILENO)
     57		close(fd);
     58
     59	return 0;
     60}
     61
     62static int wait_for_pid(pid_t pid)
     63{
     64	int status, ret;
     65
     66again:
     67	ret = waitpid(pid, &status, 0);
     68	if (ret == -1) {
     69		if (errno == EINTR)
     70			goto again;
     71		return -1;
     72	}
     73	if (ret != pid)
     74		goto again;
     75
     76	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
     77		return -1;
     78
     79	return 0;
     80}
     81
     82static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
     83{
     84	int ret;
     85	char procfd[4096];
     86
     87	ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
     88	if (ret < 0 || ret >= 4096)
     89		return -1;
     90
     91	ret = readlink(procfd, buf, buflen);
     92	if (ret < 0 || (size_t)ret >= buflen)
     93		return -1;
     94
     95	buf[ret] = '\0';
     96
     97	return 0;
     98}
     99
    100static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
    101{
    102	int ret;
    103	int master = -1, slave = -1, fret = -1;
    104
    105	master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
    106	if (master < 0) {
    107		fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
    108			strerror(errno));
    109		return -1;
    110	}
    111
    112	/*
    113	 * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
    114	 * not really needed.
    115	 */
    116	ret = unlockpt(master);
    117	if (ret < 0) {
    118		fprintf(stderr, "Failed to unlock terminal\n");
    119		goto do_cleanup;
    120	}
    121
    122#ifdef TIOCGPTPEER
    123	slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
    124#endif
    125	if (slave < 0) {
    126		if (errno == EINVAL) {
    127			fprintf(stderr, "TIOCGPTPEER is not supported. "
    128					"Skipping test.\n");
    129			fret = KSFT_SKIP;
    130		} else {
    131			fprintf(stderr,
    132				"Failed to perform TIOCGPTPEER ioctl\n");
    133			fret = EXIT_FAILURE;
    134		}
    135		goto do_cleanup;
    136	}
    137
    138	pid_t pid = fork();
    139	if (pid < 0)
    140		goto do_cleanup;
    141
    142	if (pid == 0) {
    143		char buf[4096];
    144
    145		ret = login_pty(slave);
    146		if (ret < 0) {
    147			fprintf(stderr, "Failed to setup terminal\n");
    148			_exit(EXIT_FAILURE);
    149		}
    150
    151		ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
    152		if (ret < 0) {
    153			fprintf(stderr, "Failed to retrieve pathname of pts "
    154					"slave file descriptor\n");
    155			_exit(EXIT_FAILURE);
    156		}
    157
    158		if (strncmp(expected_procfd_contents, buf,
    159			    strlen(expected_procfd_contents)) != 0) {
    160			fprintf(stderr, "Received invalid contents for "
    161					"\"/proc/<pid>/fd/%d\" symlink: %s\n",
    162					STDIN_FILENO, buf);
    163			_exit(-1);
    164		}
    165
    166		fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
    167				"symlink are valid: %s\n", STDIN_FILENO, buf);
    168
    169		_exit(EXIT_SUCCESS);
    170	}
    171
    172	ret = wait_for_pid(pid);
    173	if (ret < 0)
    174		goto do_cleanup;
    175
    176	fret = EXIT_SUCCESS;
    177
    178do_cleanup:
    179	if (master >= 0)
    180		close(master);
    181	if (slave >= 0)
    182		close(slave);
    183
    184	return fret;
    185}
    186
    187static int verify_non_standard_devpts_mount(void)
    188{
    189	char *mntpoint;
    190	int ret = -1;
    191	char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
    192	char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
    193
    194	ret = umount("/dev/pts");
    195	if (ret < 0) {
    196		fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
    197				strerror(errno));
    198		return -1;
    199	}
    200
    201	(void)umount("/dev/ptmx");
    202
    203	mntpoint = mkdtemp(devpts);
    204	if (!mntpoint) {
    205		fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
    206				 strerror(errno));
    207		return -1;
    208	}
    209
    210	ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
    211		    "newinstance,ptmxmode=0666,mode=0620,gid=5");
    212	if (ret < 0) {
    213		fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
    214				"mount namespace: %s\n", mntpoint,
    215				strerror(errno));
    216		unlink(mntpoint);
    217		return -1;
    218	}
    219
    220	ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
    221	if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
    222		unlink(mntpoint);
    223		return -1;
    224	}
    225
    226	ret = do_tiocgptpeer(ptmx, mntpoint);
    227	unlink(mntpoint);
    228	if (ret < 0)
    229		return -1;
    230
    231	return 0;
    232}
    233
    234static int verify_ptmx_bind_mount(void)
    235{
    236	int ret;
    237
    238	ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
    239	if (ret < 0) {
    240		fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
    241				"\"/dev/ptmx\" mount namespace\n");
    242		return -1;
    243	}
    244
    245	ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
    246	if (ret < 0)
    247		return -1;
    248
    249	return 0;
    250}
    251
    252static int verify_invalid_ptmx_bind_mount(void)
    253{
    254	int ret;
    255	char mntpoint_fd;
    256	char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
    257
    258	mntpoint_fd = mkstemp(ptmx);
    259	if (mntpoint_fd < 0) {
    260		fprintf(stderr, "Failed to create temporary directory: %s\n",
    261				 strerror(errno));
    262		return -1;
    263	}
    264
    265	ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
    266	close(mntpoint_fd);
    267	if (ret < 0) {
    268		fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
    269				"\"%s\" mount namespace\n", ptmx);
    270		return -1;
    271	}
    272
    273	ret = do_tiocgptpeer(ptmx, "/dev/pts/");
    274	if (ret == 0)
    275		return -1;
    276
    277	return 0;
    278}
    279
    280int main(int argc, char *argv[])
    281{
    282	int ret;
    283
    284	if (!isatty(STDIN_FILENO)) {
    285		fprintf(stderr, "Standard input file descriptor is not attached "
    286				"to a terminal. Skipping test\n");
    287		exit(KSFT_SKIP);
    288	}
    289
    290	ret = unshare(CLONE_NEWNS);
    291	if (ret < 0) {
    292		fprintf(stderr, "Failed to unshare mount namespace\n");
    293		exit(EXIT_FAILURE);
    294	}
    295
    296	ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
    297	if (ret < 0) {
    298		fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
    299				"namespace\n");
    300		exit(EXIT_FAILURE);
    301	}
    302
    303	ret = verify_ptmx_bind_mount();
    304	if (ret < 0)
    305		exit(EXIT_FAILURE);
    306
    307	ret = verify_invalid_ptmx_bind_mount();
    308	if (ret < 0)
    309		exit(EXIT_FAILURE);
    310
    311	ret = verify_non_standard_devpts_mount();
    312	if (ret < 0)
    313		exit(EXIT_FAILURE);
    314
    315	exit(EXIT_SUCCESS);
    316}