Skip to content

Commit

Permalink
Add SIGIO signal to fill fake /dev/urandom (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
clementgallet committed Apr 27, 2020
1 parent f89b24d commit 6955cd8
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 81 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* Check mask size in XISelectEvents
* XI_RawMotion now uses raw inputs
* Track handles of savefiles
* Fill asynchronously fake /dev/urandom for games that read a large number of bytes (#310)

## [1.3.5] - 2019-11-26
### Added
Expand Down
1 change: 1 addition & 0 deletions src/library/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ libtas_so_SOURCES = \
fileio/SaveFile.cpp \
fileio/SaveFileList.cpp \
fileio/stdiowrappers.cpp \
fileio/URandom.cpp \
inputs/evdev.cpp \
inputs/inputevents.cpp \
inputs/inputs.cpp \
Expand Down
111 changes: 111 additions & 0 deletions src/library/fileio/URandom.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
Copyright 2015-2020 Clément Gallet <clement.gallet@ens-lyon.org>
This file is part of libTAS.
libTAS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libTAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libTAS. If not, see <http://www.gnu.org/licenses/>.
*/

#include "URandom.h"

#include "FileHandleList.h"
#include "../logging.h"
#include <fcntl.h>
#include <unistd.h> // getpid()

namespace libtas {

static int readfd = -1;
static int writefd = -1;
static char* datestr = nullptr;
static FILE* stream = nullptr;

static void urandom_handler(int signum)
{
/* TODO: write a simple PRNG which is seeded by shared_config.initial_time_sec
* instead of outputting it directly as a string.
*/
debuglogstdio(LCF_FILEIO | LCF_RANDOM, "Filling urandom fd");
if (!datestr) {
time_t tsec = static_cast<time_t>(shared_config.initial_time_sec);
datestr = asctime(gmtime(&tsec));
}

/* Fill the pipe with data from initial time */
int err = write(writefd, datestr, strlen(datestr));
while (err != -1) {
err = write(writefd, datestr, strlen(datestr));
}
}

int urandom_create_fd()
{
debuglogstdio(LCF_FILEIO | LCF_RANDOM, "Open /dev/urandom");

if (readfd == -1) {
std::pair<int, int> fds = FileHandleList::createPipe(O_NONBLOCK);
readfd = fds.first;
writefd = fds.second;

/* Set the pipe size to some small value, because we won't need much.
* It should be at least 2*page_size, because on Linux a pipe is
* considered writeable if at least page_size can be written.
*/
MYASSERT(fcntl(writefd, F_SETPIPE_SZ, 2*4096) != -1);

/* Fill the pipe */
urandom_handler(0);

GlobalNative gn;

/* Add async signal for when the pipe is writeable */
MYASSERT(fcntl(writefd, F_SETOWN, getpid()) != -1);
MYASSERT(fcntl(writefd, F_SETSIG, 0) != -1);
MYASSERT(fcntl(writefd, F_SETFL, O_ASYNC | O_NONBLOCK) != -1);

/* Unblock SIGIO signal */
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGIO);
MYASSERT(pthread_sigmask(SIG_UNBLOCK, &mask, nullptr) == 0);

/* Add signal handler for SIGIO signal */
struct sigaction sigio;
sigfillset(&sigio.sa_mask);
sigio.sa_handler = urandom_handler;
MYASSERT(sigaction(SIGIO, &sigio, nullptr) == 0)
}

debuglog(LCF_FILEIO | LCF_RANDOM, "Return fd ", readfd);
return readfd;
}

int urandom_get_fd() {
return readfd;
}

FILE* urandom_create_file() {
if (!stream) {
int readfd = urandom_create_fd();
stream = fdopen(readfd, "r");
setvbuf(stream, nullptr, _IONBF, 0);
}
return stream;
}

FILE* urandom_get_file() {
return stream;
}

}
41 changes: 41 additions & 0 deletions src/library/fileio/URandom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2015-2020 Clément Gallet <clement.gallet@ens-lyon.org>
This file is part of libTAS.
libTAS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libTAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libTAS. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef LIBTAS_URANDOM_H_INCLUDED
#define LIBTAS_URANDOM_H_INCLUDED

#include <cstdio>

namespace libtas {

/* Creates a fd implementing our own deterministic /dev/urandom */
int urandom_create_fd();

/* Returns that fd, or -1 if not created */
int urandom_get_fd();

/* Creates a FILE* stream containing the above fd */
FILE* urandom_create_file();

/* Returns the /dev/urandom FILE* stream */
FILE* urandom_get_file();

}

#endif
49 changes: 8 additions & 41 deletions src/library/fileio/posixiowrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "../hook.h"
#include "SaveFileList.h"
#include "FileHandleList.h"
#include "URandom.h"
#include "../GlobalState.h"
#include "../inputs/jsdev.h"
#include "../inputs/evdev.h"
Expand Down Expand Up @@ -75,27 +76,7 @@ int open (const char *file, int oflag, ...)
int fd = 0;

if ((strcmp(file, "/dev/urandom") == 0) || (strcmp(file, "/dev/random") == 0)) {
if (SaveFileList::getSaveFileFd(file) == 0) {
/* Create a file with memory storage (reusing the savefile code),
* and fill it with values from the initial time, so that, for
* games that use it as PRNG seed, tweaking the initial time will
* change the seed value.
*/
fd = SaveFileList::openSaveFile(file, O_RDWR | O_TRUNC);

time_t tsec = static_cast<time_t>(shared_config.initial_time_sec);
char* datestr = asctime(gmtime(&tsec));
debuglogstdio(LCF_FILEIO, "Creating fake %s with %s", file, datestr);

write(fd, datestr, strlen(datestr));
char buf[256];
sprintf(buf, "%0*d", 255, 0);
write(fd, buf, 255);
lseek(fd, 0, SEEK_SET);
}
else {
fd = SaveFileList::openSaveFile(file, oflag);
}
return urandom_create_fd();
}

else if (strcmp(file, "/proc/uptime") == 0) {
Expand Down Expand Up @@ -175,26 +156,7 @@ int open64 (const char *file, int oflag, ...)
int fd = 0;

if ((strcmp(file, "/dev/urandom") == 0) || (strcmp(file, "/dev/random") == 0)) {
if (SaveFileList::getSaveFileFd(file) == 0) {
/* Create a file with memory storage (reusing the savefile code),
* and fill it with values from the initial time, so that, for
* games that use it as PRNG seed, tweaking the initial time will
* change the seed value.
*/
fd = SaveFileList::openSaveFile(file, O_RDWR | O_TRUNC);

time_t tsec = static_cast<time_t>(shared_config.initial_time_sec);
char* datestr = asctime(gmtime(&tsec));
debuglogstdio(LCF_FILEIO, "Creating fake %s with %s", file, datestr);
write(fd, datestr, strlen(datestr));
char buf[256];
sprintf(buf, "%0*d", 255, 0);
write(fd, buf, 255);
lseek(fd, 0, SEEK_SET);
}
else {
fd = SaveFileList::openSaveFile(file, oflag);
}
return urandom_create_fd();
}

else if (strcmp(file, "/proc/uptime") == 0) {
Expand Down Expand Up @@ -391,6 +353,11 @@ int close (int fd)

debuglogstdio(LCF_FILEIO, "%s call", __func__);

/* Check for urandom */
if (urandom_get_fd() == fd) {
return 0;
}

/* Check if we must actually close the file */
bool doClose = FileHandleList::closeFile(fd);

Expand Down
48 changes: 8 additions & 40 deletions src/library/fileio/stdiowrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "SaveFileList.h"
#include "FileHandleList.h"
#include "../GlobalState.h"
#include "URandom.h"

namespace libtas {

Expand Down Expand Up @@ -51,26 +52,7 @@ FILE *fopen (const char *filename, const char *modes)
FILE* f = nullptr;

if ((strcmp(filename, "/dev/urandom") == 0) || (strcmp(filename, "/dev/random") == 0)) {
if (SaveFileList::getSaveFileFd(filename) == 0) {
/* Create a file with memory storage (reusing the savefile code),
* and fill it with values from the initial time, so that, for
* games that use it as PRNG seed, tweaking the initial time will
* change the seed value.
*/
f = SaveFileList::openSaveFile(filename, "w");

time_t tsec = static_cast<time_t>(shared_config.initial_time_sec);
char* datestr = asctime(gmtime(&tsec));
debuglogstdio(LCF_FILEIO, "Creating fake %s with %s", filename, datestr);
fwrite(datestr, sizeof(char), strlen(datestr), f);
char buf[256];
sprintf(buf, "%0*d", 255, 0);
fwrite(buf, sizeof(char), 255, f);
fseek(f, 0, SEEK_SET);
}
else {
f = SaveFileList::openSaveFile(filename, modes);
}
return urandom_create_file();
}

else if (strcmp(filename, "/proc/uptime") == 0) {
Expand Down Expand Up @@ -132,26 +114,7 @@ FILE *fopen64 (const char *filename, const char *modes)
FILE* f = nullptr;

if ((strcmp(filename, "/dev/urandom") == 0) || (strcmp(filename, "/dev/random") == 0)) {
if (SaveFileList::getSaveFileFd(filename) == 0) {
/* Create a file with memory storage (reusing the savefile code),
* and fill it with values from the initial time, so that, for
* games that use it as PRNG seed, tweaking the initial time will
* change the seed value.
*/
f = SaveFileList::openSaveFile(filename, "w");

time_t tsec = static_cast<time_t>(shared_config.initial_time_sec);
char* datestr = asctime(gmtime(&tsec));
debuglogstdio(LCF_FILEIO, "Creating fake %s with %s", filename, datestr);
fwrite(datestr, sizeof(char), strlen(datestr), f);
char buf[256];
sprintf(buf, "%0*d", 255, 0);
fwrite(buf, sizeof(char), 255, f);
fseek(f, 0, SEEK_SET);
}
else {
f = SaveFileList::openSaveFile(filename, modes);
}
return urandom_create_file();
}

else if (strcmp(filename, "/proc/uptime") == 0) {
Expand Down Expand Up @@ -207,6 +170,11 @@ int fclose (FILE *stream)

DEBUGLOGCALL(LCF_FILEIO);

/* Check for urandom */
if (urandom_get_file() == stream) {
return 0;
}

/* Check if we must actually close the file */
bool doClose = FileHandleList::closeFile(fileno(stream));

Expand Down

0 comments on commit 6955cd8

Please sign in to comment.