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

umid.c (8268B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
      4 */
      5
      6#include <stdio.h>
      7#include <stdlib.h>
      8#include <dirent.h>
      9#include <errno.h>
     10#include <fcntl.h>
     11#include <signal.h>
     12#include <string.h>
     13#include <unistd.h>
     14#include <sys/stat.h>
     15#include <init.h>
     16#include <os.h>
     17
     18#define UML_DIR "~/.uml/"
     19
     20#define UMID_LEN 64
     21
     22/* Changed by set_umid, which is run early in boot */
     23static char umid[UMID_LEN] = { 0 };
     24
     25/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
     26static char *uml_dir = UML_DIR;
     27
     28static int __init make_uml_dir(void)
     29{
     30	char dir[512] = { '\0' };
     31	int len, err;
     32
     33	if (*uml_dir == '~') {
     34		char *home = getenv("HOME");
     35
     36		err = -ENOENT;
     37		if (home == NULL) {
     38			printk(UM_KERN_ERR
     39				"%s: no value in environment for $HOME\n",
     40				__func__);
     41			goto err;
     42		}
     43		strlcpy(dir, home, sizeof(dir));
     44		uml_dir++;
     45	}
     46	strlcat(dir, uml_dir, sizeof(dir));
     47	len = strlen(dir);
     48	if (len > 0 && dir[len - 1] != '/')
     49		strlcat(dir, "/", sizeof(dir));
     50
     51	err = -ENOMEM;
     52	uml_dir = malloc(strlen(dir) + 1);
     53	if (uml_dir == NULL) {
     54		printk(UM_KERN_ERR "%s : malloc failed, errno = %d\n",
     55			__func__, errno);
     56		goto err;
     57	}
     58	strcpy(uml_dir, dir);
     59
     60	if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) {
     61		printk(UM_KERN_ERR "Failed to mkdir '%s': %s\n",
     62			uml_dir, strerror(errno));
     63		err = -errno;
     64		goto err_free;
     65	}
     66	return 0;
     67
     68err_free:
     69	free(uml_dir);
     70err:
     71	uml_dir = NULL;
     72	return err;
     73}
     74
     75/*
     76 * Unlinks the files contained in @dir and then removes @dir.
     77 * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We
     78 * ignore ENOENT errors for anything (they happen, strangely enough - possibly
     79 * due to races between multiple dying UML threads).
     80 */
     81static int remove_files_and_dir(char *dir)
     82{
     83	DIR *directory;
     84	struct dirent *ent;
     85	int len;
     86	char file[256];
     87	int ret;
     88
     89	directory = opendir(dir);
     90	if (directory == NULL) {
     91		if (errno != ENOENT)
     92			return -errno;
     93		else
     94			return 0;
     95	}
     96
     97	while ((ent = readdir(directory)) != NULL) {
     98		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
     99			continue;
    100		len = strlen(dir) + strlen("/") + strlen(ent->d_name) + 1;
    101		if (len > sizeof(file)) {
    102			ret = -E2BIG;
    103			goto out;
    104		}
    105
    106		sprintf(file, "%s/%s", dir, ent->d_name);
    107		if (unlink(file) < 0 && errno != ENOENT) {
    108			ret = -errno;
    109			goto out;
    110		}
    111	}
    112
    113	if (rmdir(dir) < 0 && errno != ENOENT) {
    114		ret = -errno;
    115		goto out;
    116	}
    117
    118	ret = 0;
    119out:
    120	closedir(directory);
    121	return ret;
    122}
    123
    124/*
    125 * This says that there isn't already a user of the specified directory even if
    126 * there are errors during the checking.  This is because if these errors
    127 * happen, the directory is unusable by the pre-existing UML, so we might as
    128 * well take it over.  This could happen either by
    129 * 	the existing UML somehow corrupting its umid directory
    130 * 	something other than UML sticking stuff in the directory
    131 *	this boot racing with a shutdown of the other UML
    132 * In any of these cases, the directory isn't useful for anything else.
    133 *
    134 * Boolean return: 1 if in use, 0 otherwise.
    135 */
    136static inline int is_umdir_used(char *dir)
    137{
    138	char pid[sizeof("nnnnnnnnn")], *end, *file;
    139	int dead, fd, p, n, err;
    140	size_t filelen = strlen(dir) + sizeof("/pid") + 1;
    141
    142	file = malloc(filelen);
    143	if (!file)
    144		return -ENOMEM;
    145
    146	snprintf(file, filelen, "%s/pid", dir);
    147
    148	dead = 0;
    149	fd = open(file, O_RDONLY);
    150	if (fd < 0) {
    151		fd = -errno;
    152		if (fd != -ENOENT) {
    153			printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
    154			       "file '%s', err = %d\n", file, -fd);
    155		}
    156		goto out;
    157	}
    158
    159	err = 0;
    160	n = read(fd, pid, sizeof(pid));
    161	if (n < 0) {
    162		printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
    163		       "'%s', err = %d\n", file, errno);
    164		goto out_close;
    165	} else if (n == 0) {
    166		printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
    167		       "'%s', 0-byte read\n", file);
    168		goto out_close;
    169	}
    170
    171	p = strtoul(pid, &end, 0);
    172	if (end == pid) {
    173		printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
    174		       "'%s', errno = %d\n", file, errno);
    175		goto out_close;
    176	}
    177
    178	if ((kill(p, 0) == 0) || (errno != ESRCH)) {
    179		printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n",
    180		       umid, p);
    181		return 1;
    182	}
    183
    184out_close:
    185	close(fd);
    186out:
    187	free(file);
    188	return 0;
    189}
    190
    191/*
    192 * Try to remove the directory @dir unless it's in use.
    193 * Precondition: @dir exists.
    194 * Returns 0 for success, < 0 for failure in removal or if the directory is in
    195 * use.
    196 */
    197static int umdir_take_if_dead(char *dir)
    198{
    199	int ret;
    200	if (is_umdir_used(dir))
    201		return -EEXIST;
    202
    203	ret = remove_files_and_dir(dir);
    204	if (ret) {
    205		printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
    206		       "failed with err = %d\n", ret);
    207	}
    208	return ret;
    209}
    210
    211static void __init create_pid_file(void)
    212{
    213	char pid[sizeof("nnnnnnnnn")], *file;
    214	int fd, n;
    215
    216	n = strlen(uml_dir) + UMID_LEN + sizeof("/pid");
    217	file = malloc(n);
    218	if (!file)
    219		return;
    220
    221	if (umid_file_name("pid", file, n))
    222		goto out;
    223
    224	fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
    225	if (fd < 0) {
    226		printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
    227		       "%s\n", file, strerror(errno));
    228		goto out;
    229	}
    230
    231	snprintf(pid, sizeof(pid), "%d\n", getpid());
    232	n = write(fd, pid, strlen(pid));
    233	if (n != strlen(pid))
    234		printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
    235		       errno);
    236
    237	close(fd);
    238out:
    239	free(file);
    240}
    241
    242int __init set_umid(char *name)
    243{
    244	if (strlen(name) > UMID_LEN - 1)
    245		return -E2BIG;
    246
    247	strlcpy(umid, name, sizeof(umid));
    248
    249	return 0;
    250}
    251
    252/* Changed in make_umid, which is called during early boot */
    253static int umid_setup = 0;
    254
    255static int __init make_umid(void)
    256{
    257	int fd, err;
    258	char tmp[256];
    259
    260	if (umid_setup)
    261		return 0;
    262
    263	make_uml_dir();
    264
    265	if (*umid == '\0') {
    266		strlcpy(tmp, uml_dir, sizeof(tmp));
    267		strlcat(tmp, "XXXXXX", sizeof(tmp));
    268		fd = mkstemp(tmp);
    269		if (fd < 0) {
    270			printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
    271			       "%s\n", tmp, strerror(errno));
    272			err = -errno;
    273			goto err;
    274		}
    275
    276		close(fd);
    277
    278		set_umid(&tmp[strlen(uml_dir)]);
    279
    280		/*
    281		 * There's a nice tiny little race between this unlink and
    282		 * the mkdir below.  It'd be nice if there were a mkstemp
    283		 * for directories.
    284		 */
    285		if (unlink(tmp)) {
    286			err = -errno;
    287			goto err;
    288		}
    289	}
    290
    291	snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
    292	err = mkdir(tmp, 0777);
    293	if (err < 0) {
    294		err = -errno;
    295		if (err != -EEXIST)
    296			goto err;
    297
    298		if (umdir_take_if_dead(tmp) < 0)
    299			goto err;
    300
    301		err = mkdir(tmp, 0777);
    302	}
    303	if (err) {
    304		err = -errno;
    305		printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
    306		       errno);
    307		goto err;
    308	}
    309
    310	umid_setup = 1;
    311
    312	create_pid_file();
    313
    314	err = 0;
    315 err:
    316	return err;
    317}
    318
    319static int __init make_umid_init(void)
    320{
    321	if (!make_umid())
    322		return 0;
    323
    324	/*
    325	 * If initializing with the given umid failed, then try again with
    326	 * a random one.
    327	 */
    328	printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
    329	       "random umid\n", umid);
    330	*umid = '\0';
    331	make_umid();
    332
    333	return 0;
    334}
    335
    336__initcall(make_umid_init);
    337
    338int __init umid_file_name(char *name, char *buf, int len)
    339{
    340	int n, err;
    341
    342	err = make_umid();
    343	if (err)
    344		return err;
    345
    346	n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
    347	if (n >= len) {
    348		printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
    349		return -E2BIG;
    350	}
    351
    352	return 0;
    353}
    354
    355char *get_umid(void)
    356{
    357	return umid;
    358}
    359
    360static int __init set_uml_dir(char *name, int *add)
    361{
    362	if (*name == '\0') {
    363		os_warn("uml_dir can't be an empty string\n");
    364		return 0;
    365	}
    366
    367	if (name[strlen(name) - 1] == '/') {
    368		uml_dir = name;
    369		return 0;
    370	}
    371
    372	uml_dir = malloc(strlen(name) + 2);
    373	if (uml_dir == NULL) {
    374		os_warn("Failed to malloc uml_dir - error = %d\n", errno);
    375
    376		/*
    377		 * Return 0 here because do_initcalls doesn't look at
    378		 * the return value.
    379		 */
    380		return 0;
    381	}
    382	sprintf(uml_dir, "%s/", name);
    383
    384	return 0;
    385}
    386
    387__uml_setup("uml_dir=", set_uml_dir,
    388"uml_dir=<directory>\n"
    389"    The location to place the pid and umid files.\n\n"
    390);
    391
    392static void remove_umid_dir(void)
    393{
    394	char *dir, err;
    395
    396	dir = malloc(strlen(uml_dir) + UMID_LEN + 1);
    397	if (!dir)
    398		return;
    399
    400	sprintf(dir, "%s%s", uml_dir, umid);
    401	err = remove_files_and_dir(dir);
    402	if (err)
    403		os_warn("%s - remove_files_and_dir failed with err = %d\n",
    404			__func__, err);
    405
    406	free(dir);
    407}
    408
    409__uml_exitcall(remove_umid_dir);