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

chan_user.c (7155B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
      4 */
      5
      6#include <stdlib.h>
      7#include <unistd.h>
      8#include <errno.h>
      9#include <sched.h>
     10#include <signal.h>
     11#include <termios.h>
     12#include <sys/ioctl.h>
     13#include "chan_user.h"
     14#include <os.h>
     15#include <um_malloc.h>
     16
     17void generic_close(int fd, void *unused)
     18{
     19	close(fd);
     20}
     21
     22int generic_read(int fd, char *c_out, void *unused)
     23{
     24	int n;
     25
     26	n = read(fd, c_out, sizeof(*c_out));
     27	if (n > 0)
     28		return n;
     29	else if (n == 0)
     30		return -EIO;
     31	else if (errno == EAGAIN)
     32		return 0;
     33	return -errno;
     34}
     35
     36/* XXX Trivial wrapper around write */
     37
     38int generic_write(int fd, const char *buf, int n, void *unused)
     39{
     40	int err;
     41
     42	err = write(fd, buf, n);
     43	if (err > 0)
     44		return err;
     45	else if (errno == EAGAIN)
     46		return 0;
     47	else if (err == 0)
     48		return -EIO;
     49	return -errno;
     50}
     51
     52int generic_window_size(int fd, void *unused, unsigned short *rows_out,
     53			unsigned short *cols_out)
     54{
     55	struct winsize size;
     56	int ret;
     57
     58	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
     59		return -errno;
     60
     61	ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
     62
     63	*rows_out = size.ws_row;
     64	*cols_out = size.ws_col;
     65
     66	return ret;
     67}
     68
     69void generic_free(void *data)
     70{
     71	kfree(data);
     72}
     73
     74int generic_console_write(int fd, const char *buf, int n)
     75{
     76	sigset_t old, no_sigio;
     77	struct termios save, new;
     78	int err;
     79
     80	if (isatty(fd)) {
     81		sigemptyset(&no_sigio);
     82		sigaddset(&no_sigio, SIGIO);
     83		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
     84			goto error;
     85
     86		CATCH_EINTR(err = tcgetattr(fd, &save));
     87		if (err)
     88			goto error;
     89		new = save;
     90		/*
     91		 * The terminal becomes a bit less raw, to handle \n also as
     92		 * "Carriage Return", not only as "New Line". Otherwise, the new
     93		 * line won't start at the first column.
     94		 */
     95		new.c_oflag |= OPOST;
     96		CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
     97		if (err)
     98			goto error;
     99	}
    100	err = generic_write(fd, buf, n, NULL);
    101	/*
    102	 * Restore raw mode, in any case; we *must* ignore any error apart
    103	 * EINTR, except for debug.
    104	 */
    105	if (isatty(fd)) {
    106		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
    107		sigprocmask(SIG_SETMASK, &old, NULL);
    108	}
    109
    110	return err;
    111error:
    112	return -errno;
    113}
    114
    115/*
    116 * UML SIGWINCH handling
    117 *
    118 * The point of this is to handle SIGWINCH on consoles which have host
    119 * ttys and relay them inside UML to whatever might be running on the
    120 * console and cares about the window size (since SIGWINCH notifies
    121 * about terminal size changes).
    122 *
    123 * So, we have a separate thread for each host tty attached to a UML
    124 * device (side-issue - I'm annoyed that one thread can't have
    125 * multiple controlling ttys for the purpose of handling SIGWINCH, but
    126 * I imagine there are other reasons that doesn't make any sense).
    127 *
    128 * SIGWINCH can't be received synchronously, so you have to set up to
    129 * receive it as a signal.  That being the case, if you are going to
    130 * wait for it, it is convenient to sit in sigsuspend() and wait for
    131 * the signal to bounce you out of it (see below for how we make sure
    132 * to exit only on SIGWINCH).
    133 */
    134
    135static void winch_handler(int sig)
    136{
    137}
    138
    139struct winch_data {
    140	int pty_fd;
    141	int pipe_fd;
    142};
    143
    144static int winch_thread(void *arg)
    145{
    146	struct winch_data *data = arg;
    147	sigset_t sigs;
    148	int pty_fd, pipe_fd;
    149	int count;
    150	char c = 1;
    151
    152	pty_fd = data->pty_fd;
    153	pipe_fd = data->pipe_fd;
    154	count = write(pipe_fd, &c, sizeof(c));
    155	if (count != sizeof(c))
    156		printk(UM_KERN_ERR "winch_thread : failed to write "
    157		       "synchronization byte, err = %d\n", -count);
    158
    159	/*
    160	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
    161	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
    162	 * SIGWINCH.
    163	 */
    164
    165	signal(SIGWINCH, winch_handler);
    166	sigfillset(&sigs);
    167	/* Block all signals possible. */
    168	if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
    169		printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
    170		       "errno = %d\n", errno);
    171		exit(1);
    172	}
    173	/* In sigsuspend(), block anything else than SIGWINCH. */
    174	sigdelset(&sigs, SIGWINCH);
    175
    176	if (setsid() < 0) {
    177		printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
    178		       errno);
    179		exit(1);
    180	}
    181
    182	if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
    183		printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
    184		       "fd %d err = %d\n", pty_fd, errno);
    185		exit(1);
    186	}
    187
    188	if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
    189		printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
    190		       "fd %d err = %d\n", pty_fd, errno);
    191		exit(1);
    192	}
    193
    194	/*
    195	 * These are synchronization calls between various UML threads on the
    196	 * host - since they are not different kernel threads, we cannot use
    197	 * kernel semaphores. We don't use SysV semaphores because they are
    198	 * persistent.
    199	 */
    200	count = read(pipe_fd, &c, sizeof(c));
    201	if (count != sizeof(c))
    202		printk(UM_KERN_ERR "winch_thread : failed to read "
    203		       "synchronization byte, err = %d\n", errno);
    204
    205	while(1) {
    206		/*
    207		 * This will be interrupted by SIGWINCH only, since
    208		 * other signals are blocked.
    209		 */
    210		sigsuspend(&sigs);
    211
    212		count = write(pipe_fd, &c, sizeof(c));
    213		if (count != sizeof(c))
    214			printk(UM_KERN_ERR "winch_thread : write failed, "
    215			       "err = %d\n", errno);
    216	}
    217}
    218
    219static int winch_tramp(int fd, struct tty_port *port, int *fd_out,
    220		       unsigned long *stack_out)
    221{
    222	struct winch_data data;
    223	int fds[2], n, err, pid;
    224	char c;
    225
    226	err = os_pipe(fds, 1, 1);
    227	if (err < 0) {
    228		printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n",
    229		       -err);
    230		goto out;
    231	}
    232
    233	data = ((struct winch_data) { .pty_fd 		= fd,
    234				      .pipe_fd 		= fds[1] } );
    235	/*
    236	 * CLONE_FILES so this thread doesn't hold open files which are open
    237	 * now, but later closed in a different thread.  This is a
    238	 * problem with /dev/net/tun, which if held open by this
    239	 * thread, prevents the TUN/TAP device from being reused.
    240	 */
    241	pid = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
    242	if (pid < 0) {
    243		err = pid;
    244		printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n",
    245		       -err);
    246		goto out_close;
    247	}
    248
    249	*fd_out = fds[0];
    250	n = read(fds[0], &c, sizeof(c));
    251	if (n != sizeof(c)) {
    252		printk(UM_KERN_ERR "winch_tramp : failed to read "
    253		       "synchronization byte\n");
    254		printk(UM_KERN_ERR "read failed, err = %d\n", errno);
    255		printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd);
    256		err = -EINVAL;
    257		goto out_close;
    258	}
    259
    260	err = os_set_fd_block(*fd_out, 0);
    261	if (err) {
    262		printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
    263		       "non-blocking.\n");
    264		goto out_close;
    265	}
    266
    267	return pid;
    268
    269 out_close:
    270	close(fds[1]);
    271	close(fds[0]);
    272 out:
    273	return err;
    274}
    275
    276void register_winch(int fd, struct tty_port *port)
    277{
    278	unsigned long stack;
    279	int pid, thread, count, thread_fd = -1;
    280	char c = 1;
    281
    282	if (!isatty(fd))
    283		return;
    284
    285	pid = tcgetpgrp(fd);
    286	if (is_skas_winch(pid, fd, port)) {
    287		register_winch_irq(-1, fd, -1, port, 0);
    288		return;
    289	}
    290
    291	if (pid == -1) {
    292		thread = winch_tramp(fd, port, &thread_fd, &stack);
    293		if (thread < 0)
    294			return;
    295
    296		register_winch_irq(thread_fd, fd, thread, port, stack);
    297
    298		count = write(thread_fd, &c, sizeof(c));
    299		if (count != sizeof(c))
    300			printk(UM_KERN_ERR "register_winch : failed to write "
    301			       "synchronization byte, err = %d\n", errno);
    302	}
    303}