Skip to content

Commit

Permalink
Implemented getrandom, and /dev/urandom
Browse files Browse the repository at this point in the history
We now handle return values from system calls correctly.
Added dettraceDebug environment variable.
ssh-keygen example working and added to testing suite.
  • Loading branch information
gatowololo committed Mar 8, 2018
1 parent 04efe5d commit 46978f5
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 37 deletions.
48 changes: 47 additions & 1 deletion include/dettraceSystemCall.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class fcntlSystemCall : public systemCall{
};
// =======================================================================================
/**
* fstat()
* int fstat(int fd, struct stat *statbuf);
*
* These functions return information about a file, in the buffer pointed to by statbuf.
* No permissions are required on the file itself, but—in the case of stat(), fstatat(),
Expand All @@ -308,6 +308,24 @@ class fstatSystemCall : public systemCall{
void handleDetPost(state& s, ptracer& t) override;
};
// =======================================================================================
/**
* int fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);
*
* The fstatat() system call operates in exactly the same way as stat(), except for if
* the pathname given in pathname is relative, then it is interpreted relative to the
* directory referred to by the file descriptor dirfd (rather than relative to the cur‐
* rent working directory of the calling process, as is done by stat() for a relative
* pathname).
*
* Actual name of underlying system call is newfstatat.
*/
class newfstatatSystemCall : public systemCall{
public:
newfstatatSystemCall(long syscallName, string syscallNumber);
bool handleDetPre(state& s, ptracer& t) override;
void handleDetPost(state& s, ptracer& t) override;
};
// =======================================================================================
/**
* int fstatfs(int fd, struct statfs *buf);
*
Expand Down Expand Up @@ -1266,6 +1284,34 @@ class unlinkSystemCall : public systemCall{
void handleDetPost(state& s, ptracer& t) override;
};
// =======================================================================================
/**
*
* int unlinkat(int dirfd, const char *pathname, int flags);
*
* The unlinkat() system call operates in exactly the same way as either unlink() or
* rmdir(2) (depending on whether or not flags includes the AT_REMOVEDIR flag) except
* for the differences described here.
* If the pathname given in pathname is relative, then it is interpreted relative to the
* directory referred to by the file descriptor dirfd (rather than relative to the cur‐
* rent working directory of the calling process, as is done by unlink() and rmdir(2)
* for a relative pathname).
* If the pathname given in pathname is relative and dirfd is the special value
* AT_FDCWD, then pathname is interpreted relative to the current working directory of
* the calling process (like unlink() and rmdir(2)).
* If the pathname given in pathname is absolute, then dirfd is ignored.
* Seems deterministic enough :)
*/
class unlinkatSystemCall : public systemCall{
public:
unlinkatSystemCall(long syscallName, string syscallNumber);
bool handleDetPre(state& s, ptracer& t) override;
void handleDetPost(state& s, ptracer& t) override;
};
// =======================================================================================
/**
*
* int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
Expand Down
100 changes: 70 additions & 30 deletions src/dettraceSystemCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ bool cloneSystemCall::handleDetPre(state &s, ptracer &t){
void cloneSystemCall::handleDetPost(state &s, ptracer &t){
// Non deterministic failure due to signal.
pid_t returnPid = t.getReturnValue();
if(returnPid == -1){
throw runtime_error("Clone system call failed:\n" + string { strerror(errno) });
if(returnPid < 0){
throw runtime_error("Clone system call failed:\n" + string { strerror(- returnPid) });
}

// In older versions of ptrace, the tid value was cached to skip getpid calls. This
Expand All @@ -168,8 +168,9 @@ bool closeSystemCall::handleDetPre(state &s, ptracer &t){

void closeSystemCall::handleDetPost(state &s, ptracer &t){
// Non deterministic failure due to signal.
if((int64_t) t.getReturnValue() == -1){
throw runtime_error("Close system call failed:\n" + string { strerror(errno) });
int returnVal = t.getReturnValue();
if(returnVal < 0){
throw runtime_error("Close system call failed:\n" + string { strerror(- returnVal) });
}

return;
Expand Down Expand Up @@ -284,6 +285,23 @@ void fstatSystemCall::handleDetPost(state &s, ptracer &t){
return;
}
// =======================================================================================
newfstatatSystemCall::newfstatatSystemCall(long syscallNumber, string syscallName):
systemCall(syscallNumber, syscallName){
return;
}

bool newfstatatSystemCall::handleDetPre(state &s, ptracer &t){
return true;
}

void newfstatatSystemCall::handleDetPost(state &s, ptracer &t){
string path = ptracer::readTraceeCString((const char*)t.arg2(), t.getPid());
string msg = "newfstatat-ing path: " + logger::makeTextColored(Color::green, path) + "\n";
s.log.writeToLog(Importance::info, msg);
handleStatFamily(s, t, "newfstatat");
return;
}
// =======================================================================================
fstatfsSystemCall::fstatfsSystemCall(long syscallNumber, string syscallName):
systemCall(syscallNumber, syscallName){
return;
Expand All @@ -310,8 +328,6 @@ void fstatfsSystemCall::handleDetPost(state &s, ptracer &t){

// Write back result for child.
ptracer::writeToTracee(statfsPtr, myStatfs, s.traceePid);
}else{
s.log.writeToLog(Importance::info, "Negative number returned from fstatfs call\n" );
}

return;
Expand Down Expand Up @@ -519,7 +535,7 @@ void getrlimitSystemCall::handleDetPost(state &s, ptracer &t){
struct rlimit noLimits = {};
noLimits.rlim_cur = RLIM_INFINITY;
noLimits.rlim_max = RLIM_INFINITY;

ptracer::writeToTracee(rp, noLimits, t.getPid());
}

Expand Down Expand Up @@ -594,7 +610,7 @@ void gettimeofdaySystemCall::handleDetPost(state &s, ptracer &t){
struct timeval myTv = {};
myTv.tv_sec = s.getLogicalTime();
myTv.tv_usec = 0;

ptracer::writeToTracee(tp, myTv, t.getPid());
s.incrementTime();
}
Expand Down Expand Up @@ -803,12 +819,7 @@ bool nanosleepSystemCall::handleDetPre(state &s, ptracer &t){
}

void nanosleepSystemCall::handleDetPost(state &s, ptracer &t){
// Check return value. We wish we could check for EINTR but we can't as I don't
// know a way to get errno from tracee. Instead we merely fail if nanosleep returns
// -1.
if(t.getReturnValue() == (u_int64_t)-1){
throw runtime_error("nanosleep returned with error.");
}
// TODO: Turn nano sleep into a no op.

return;
}
Expand Down Expand Up @@ -928,10 +939,11 @@ bool readSystemCall::handleDetPre(state &s, ptracer &t){

void readSystemCall::handleDetPost(state &s, ptracer &t){
// TODO:
if (t.getReturnValue() == (uint64_t) -1) {
throw runtime_error("read returned -1 with some error");
int retVal = t.getReturnValue();
if(retVal < 0) {
throw runtime_error("Read failed with: " + string{ strerror(- retVal) });
}
ssize_t bytes_read = t.getReturnValue();
ssize_t bytes_read = retVal;
ssize_t bytes_requested = t.arg3();
if (bytes_read != bytes_requested && bytes_read != 0) {
t.writeArg2(t.arg2() + bytes_read);
Expand Down Expand Up @@ -1225,9 +1237,10 @@ bool timeSystemCall::handleDetPre(state &s, ptracer &t){
}

void timeSystemCall::handleDetPost(state &s, ptracer &t){
time_t retval = (time_t) t.getReturnValue();
if(retval == -1){
s.log.writeToLog(Importance::info, "time: sycall returned -1");
int retVal = (int) t.getReturnValue();
if(retVal < 0){
s.log.writeToLog(Importance::info,
"Time call failed: \n" + string { strerror(- retVal)});
return;
}

Expand Down Expand Up @@ -1301,12 +1314,32 @@ unlinkSystemCall::unlinkSystemCall(long syscallNumber, string syscallName):
}

bool unlinkSystemCall::handleDetPre(state &s, ptracer &t){
string path = ptracer::readTraceeCString((const char*)t.arg1(), t.getPid());
string msg = "unlink-ing path: " + logger::makeTextColored(Color::green, path) + "\n";
s.log.writeToLog(Importance::info, msg);
return true;
}

void unlinkSystemCall::handleDetPost(state &s, ptracer &t){
return;
}
// =======================================================================================
unlinkatSystemCall::unlinkatSystemCall(long syscallNumber, string syscallName):
systemCall(syscallNumber, syscallName){
return;
}

bool unlinkatSystemCall::handleDetPre(state &s, ptracer &t){
string path = ptracer::readTraceeCString((const char*)t.arg2(), t.getPid());
string msg = "unlinkat-ing path: " + logger::makeTextColored(Color::green, path) + "\n";
s.log.writeToLog(Importance::info, msg);
return true;
}

void unlinkatSystemCall::handleDetPost(state &s, ptracer &t){
return;
}

// =======================================================================================
utimensatSystemCall::utimensatSystemCall(long syscallNumber, string syscallName):
systemCall(syscallNumber, syscallName){
Expand Down Expand Up @@ -1421,14 +1454,21 @@ void zeroOutStatfs(struct statfs& stats){
}
// =======================================================================================
void handleStatFamily(state& s, ptracer& t, string syscallName){
struct stat* statPtr = (struct stat*) t.arg2();
struct stat* statPtr;

if(syscallName == "newfstatat"){
statPtr = (struct stat*) t.arg3();
}else{
statPtr = (struct stat*) t.arg2();
}

if(statPtr == nullptr){
s.log.writeToLog(Importance::info, syscallName + ": statbuf null.\n");
return;
}

if(t.getReturnValue() == 0){
int retVal = t.getReturnValue();
if(retVal == 0){
struct stat myStat = ptracer::readFromTracee(statPtr, s.traceePid);

myStat.st_atim = timespec { .tv_sec = s.getLogicalTime(),
Expand All @@ -1437,7 +1477,7 @@ void handleStatFamily(state& s, ptracer& t, string syscallName){
.tv_nsec = s.getLogicalTime() };
myStat.st_ctim = timespec { .tv_sec = s.getLogicalTime(),
.tv_nsec = s.getLogicalTime() }; /* user CPU time used */

myStat.st_dev = 1; /* ID of device containing file */

// inode virtualization
Expand All @@ -1451,27 +1491,27 @@ void handleStatFamily(state& s, ptracer& t, string syscallName){
// will think we don't have access to this file. Hence we keep our permissions
// as part of the stat.
// mode_t st_mode; /* File type and mode */

myStat.st_nlink = 1; /* Number of hard links */
myStat.st_uid = 65534; /* User ID of owner */
myStat.st_gid = 1; /* Group ID of owner */
myStat.st_rdev = 1; /* Device ID (if special file) */

// Program will stall if we put some arbitrary value here: TODO.
// myStat.st_size = 512; /* Total size, in bytes */

myStat.st_blksize = 512; /* Block size for filesystem I/O */

// TODO: could return actual value here?
myStat.st_blocks = 1; /* Number of 512B blocks allocated */

s.incrementTime();

// Write back result for child.
ptracer::writeToTracee(statPtr, myStat, s.traceePid);
} else {
s.log.writeToLog(Importance::info, "Negative number returned from "
+ syscallName + "call: " + to_string(t.getReturnValue()) + "\n");
s.log.writeToLog(Importance::info, "Error in "
+ syscallName + ":\n" + string { strerror(- retVal)} + "\n");
}
return;
}
Expand Down
4 changes: 4 additions & 0 deletions src/execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ execution::getSystemCall(int syscallNumber, string syscallName){
return make_unique<fcntlSystemCall>(syscallNumber, syscallName);
case SYS_fstat:
return make_unique<fstatSystemCall>(syscallNumber, syscallName);
case SYS_newfstatat:
return make_unique<newfstatatSystemCall>(syscallNumber, syscallName);
case SYS_fstatfs:
return make_unique<fstatfsSystemCall>(syscallNumber, syscallName);
case SYS_futex:
Expand Down Expand Up @@ -436,6 +438,8 @@ execution::getSystemCall(int syscallNumber, string syscallName){
return make_unique<unameSystemCall>(syscallNumber, syscallName);
case SYS_unlink:
return make_unique<unlinkSystemCall>(syscallNumber, syscallName);
case SYS_unlinkat:
return make_unique<unlinkatSystemCall>(syscallNumber, syscallName);
case SYS_utimensat:
return make_unique<utimensatSystemCall>(syscallNumber, syscallName);
case SYS_vfork:
Expand Down
17 changes: 17 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ int main(int argc, char** argv){
int optIndex, debugLevel;
tie(optIndex, debugLevel) = parseProgramArguments(argc, argv);

char* debugEnvvar = secure_getenv("dettraceDebug");
if(debugEnvvar != nullptr){
string str { debugEnvvar };
try{
debugLevel = stoi(str);
}catch (...){
throw runtime_error("Invalid integer: " + str);
}


if(debugLevel < 0 || debugLevel > 5){
throw runtime_error("Debug level must be between [0,5].");
}
}

// Set up new user namespace. This is needed as we will have root access withing
// our own user namespace. Other namepspace commands require CAP_SYS_ADMIN to work.
// Namespaces must must be done before fork. As changes don't apply until after
Expand Down Expand Up @@ -160,6 +175,8 @@ void setUpContainer(string pathToExe){
mountDir("/usr/", "../usr/");
mountDir("/lib/", "../lib/");
mountDir("/lib64/", "../lib64/");
// Mount dev/null
mountDir("/dev/null", "../dev/null");
// Mount our dettrace/bin and dettrace/lib folders.
mountDir("../../bin/", "../dettrace/bin");
mountDir("../../lib/", "../dettrace/lib");
Expand Down
17 changes: 17 additions & 0 deletions test/samplePrograms/ExpectedOutputs/callSshKeygen.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Generating public/private rsa key pair.
Your identification has been saved in thisKey.
Your public key has been saved in thisKey.pub.
The key fingerprint is:
SHA256:3JwulFT0YRAqKcWYs04SE4QokjE6eyWEcNv+RdHnSBQ nobody@
The key's randomart image is:
+---[RSA 2048]----+
|**=. +. .+E+o |
|O+oo+... +oo.. |
|= oo+oo +. +. |
| o.+o. = +... |
|. .+. S + |
| . .. o . |
| . . . |
| . |
| |
+----[SHA256]-----+
1 change: 1 addition & 0 deletions test/samplePrograms/ExpectedOutputs/clock_gettime.output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
res: {.tv_sec = 0, .tv_nsec = 0}
2 changes: 1 addition & 1 deletion test/samplePrograms/ExpectedOutputs/uname.output
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Operating name: Linux
Operating name: Linux
Node name:
Operating system release: 4.0
Operating system version: #1
Expand Down
4 changes: 3 additions & 1 deletion test/samplePrograms/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
OBJECTS=simpleFork inverseFork nestedFork vfork clock_gettime getpid uname alarm pipe pipe-multiwrite getRandom
OBJECTS=simpleFork inverseFork nestedFork vfork clock_gettime getpid uname alarm pipe pipe-multiwrite getRandom callSshKeygen


all: $(OBJECTS)
Expand All @@ -11,6 +11,8 @@ test:
./alarm
./pipe
./pipe-multiwrite
./getRandom
./callSshKeygen

$(OBJECTS):%:%.c
gcc $< -Wall -g -o $@
Expand Down
8 changes: 8 additions & 0 deletions test/samplePrograms/callSshKeygen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdlib.h>

int main(){
// No error if file not found.
system("rm -f thisKey");
system("rm -f thisKey.pub");
system("ssh-keygen -f thisKey -N \"\"");
}

0 comments on commit 46978f5

Please sign in to comment.