Skip to content

Commit

Permalink
Merge pull request #1075 from robgjansen/syscall-misc2
Browse files Browse the repository at this point in the history
Miscellaneous syscalls to support running Tor
  • Loading branch information
robgjansen committed Feb 9, 2021
2 parents 2448c17 + 6d2bdc7 commit d6a9ec6
Show file tree
Hide file tree
Showing 20 changed files with 431 additions and 28 deletions.
2 changes: 2 additions & 0 deletions src/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ set(shadow_srcs
host/syscall/mman.c
host/syscall/process.c
host/syscall/random.c
host/syscall/signal.c
host/syscall/socket.c
host/syscall/sysinfo.c
host/syscall/time.c
host/syscall/timerfd.c
host/syscall/unistd.c
Expand Down
21 changes: 21 additions & 0 deletions src/main/host/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,24 @@ const gchar* host_getDataPath(Host* host) {
}

FutexTable* host_getFutexTable(Host* host) { return host->futexTable; }

pid_t host_getNativeTID(Host* host, pid_t virtualPID, pid_t virtualTID) {
MAGIC_ASSERT(host);

// TODO: once we have a process table, we can do a constant time lookup instead
GList* current = g_queue_peek_head_link(host->processes);
pid_t nativeTID = 0;

while (current != NULL) {
Process* proc = current->data;
nativeTID = process_findNativeTID(proc, virtualPID, virtualTID);

if (nativeTID > 0) {
break;
}

current = current->next;
}

return nativeTID; // 0 if no process/thread has the given virtual PID/TID
}
3 changes: 3 additions & 0 deletions src/main/host/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,7 @@ in_port_t host_getRandomFreePort(Host* host, ProtocolType type,

FutexTable* host_getFutexTable(Host* host);

// converts a virtual (shadow) tid into the native tid
pid_t host_getNativeTID(Host* host, pid_t virtualPID, pid_t virtualTID);

#endif /* SHD_HOST_H_ */
27 changes: 27 additions & 0 deletions src/main/host/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,33 @@ static Thread* _process_threadLeader(Process* proc) {
return g_hash_table_lookup(proc->threads, GUINT_TO_POINTER(proc->processID));
}

pid_t process_findNativeTID(Process* proc, pid_t virtualPID, pid_t virtualTID) {
MAGIC_ASSERT(proc);

Thread* thread = NULL;

if (virtualPID > 0 && virtualTID > 0) {
// Both PID and TID must match
if (proc->processID == virtualPID) {
thread = g_hash_table_lookup(proc->threads, GINT_TO_POINTER(virtualTID));
}
} else if (virtualPID > 0) {
// Get the TID of the main thread if the PID matches
if (proc->processID == virtualPID) {
thread = _process_threadLeader(proc);
}
} else if (virtualTID > 0) {
// Get the TID of any thread that matches, ignoring PID
thread = g_hash_table_lookup(proc->threads, GINT_TO_POINTER(virtualTID));
}

if (thread != NULL) {
return thread_getNativeTid(thread);
} else {
return 0; // not found
}
}

static void _process_check(Process* proc) {
MAGIC_ASSERT(proc);

Expand Down
8 changes: 8 additions & 0 deletions src/main/host/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ const gchar* process_getPluginName(Process* proc);
/* Returns the processID that was assigned to us in process_new */
guint process_getProcessID(Process* proc);

/* Returns the native tid of the thread with the given virtual PID and TID.
* Although the process knows its own virtualPID already, giving it as a param
* here allows us to control which of the PIF and TID get matched:
* - If virtualPID is 0, then we only check for matching TIDs.
* - If virtualTID is 0, then we return the TID of the main thread if the virutal PIDs match.
* - If we don't find a matching thread, return 0. */
pid_t process_findNativeTID(Process* proc, pid_t virtualPID, pid_t virtualTID);

/* Handle all of the descriptors owned by this process. */
int process_registerCompatDescriptor(Process* proc, CompatDescriptor* compatDesc);
void process_deregisterCompatDescriptor(Process* proc, int handle);
Expand Down
16 changes: 16 additions & 0 deletions src/main/host/syscall/futex.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,19 @@ SysCallReturn syscallhandler_futex(SysCallHandler* sys, const SysCallArgs* args)

return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = result};
}

SysCallReturn syscallhandler_get_robust_list(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);

info("get_robust_list was called but we don't yet support it");

return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ENOSYS};
}

SysCallReturn syscallhandler_set_robust_list(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);

info("set_robust_list was called but we don't yet support it");

return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ENOSYS};
}
2 changes: 2 additions & 0 deletions src/main/host/syscall/futex.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
#include "main/host/syscall/protected.h"

SYSCALL_HANDLER(futex);
SYSCALL_HANDLER(get_robust_list);
SYSCALL_HANDLER(set_robust_list);

#endif /* SRC_MAIN_HOST_SYSCALL_FUTEX_H_ */
75 changes: 75 additions & 0 deletions src/main/host/syscall/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,82 @@

#include "main/host/syscall/process.h"

#include <errno.h>
#include <sys/prctl.h>

#include "main/host/syscall/protected.h"
#include "main/host/thread.h"
#include "support/logger/logger.h"

///////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////

static SysCallReturn _syscallhandler_prlimitHelper(SysCallHandler* sys, pid_t pid, int resource,
PluginPtr newlim, PluginPtr oldlim) {
// TODO: for determinism, we may want to enforce static limits for certain resources, like
// RLIMIT_NOFILE. Some applications like Tor will change behavior depending on these limits.
if (pid == 0) {
// process is calling prlimit on itself
return (SysCallReturn){.state = SYSCALL_NATIVE};
} else {
// TODO: we do not currently support adjusting other processes limits.
// To support it, we just need to find the native pid associated
// with pid, and call prlimit on the native pid instead.
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ENOSYS};
}
}

///////////////////////////////////////////////////////////
// System Calls
///////////////////////////////////////////////////////////

SysCallReturn syscallhandler_prctl(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);

int option = args->args[0].as_i64;
debug("prctl called with option %i", option);

if (option == PR_GET_TID_ADDRESS) {
PluginVirtualPtr tid_addr = thread_getTidAddress(sys->thread);

// Make sure we have somewhere to copy the output
PluginPtr outptr = args->args[1].as_ptr;
if (!outptr.val) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -EFAULT};
}

int** out = process_getWriteablePtr(sys->process, sys->thread, outptr, sizeof(*out));
if (!out) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -EFAULT};
}

*out = (int*)tid_addr.val;
return (SysCallReturn){.state = SYSCALL_DONE};
} else {
return (SysCallReturn){.state = SYSCALL_NATIVE};
}
}

SysCallReturn syscallhandler_prlimit(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);
pid_t pid = args->args[0].as_i64;
int resource = args->args[1].as_i64;
PluginPtr newlim = args->args[2].as_ptr; // const struct rlimit*
PluginPtr oldlim = args->args[3].as_ptr; // const struct rlimit*
debug("prlimit called on pid %i for resource %i", pid, resource);
return _syscallhandler_prlimitHelper(sys, pid, resource, newlim, oldlim);
}

SysCallReturn syscallhandler_prlimit64(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);
pid_t pid = args->args[0].as_i64;
int resource = args->args[1].as_i64;
PluginPtr newlim = args->args[2].as_ptr; // const struct rlimit*
PluginPtr oldlim = args->args[3].as_ptr; // const struct rlimit*
debug("prlimit called on pid %i for resource %i", pid, resource);
return _syscallhandler_prlimitHelper(sys, pid, resource, newlim, oldlim);
}

SysCallReturn syscallhandler_execve(SysCallHandler* sys, const SysCallArgs* args) {
// The MemoryManager's state is no longer valid after an exec.
Expand Down
3 changes: 3 additions & 0 deletions src/main/host/syscall/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
#include "main/host/syscall/protected.h"

SYSCALL_HANDLER(execve);
SYSCALL_HANDLER(prctl);
SYSCALL_HANDLER(prlimit);
SYSCALL_HANDLER(prlimit64);

#endif
126 changes: 126 additions & 0 deletions src/main/host/syscall/signal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* The Shadow Simulator
* See LICENSE for licensing information
*/

#include "main/host/syscall/process.h"

#include <errno.h>
#include <stdbool.h>
#include <sys/syscall.h>

#include "main/host/host.h"
#include "main/host/syscall/protected.h"
#include "main/host/thread.h"
#include "main/utility/syscall.h"
#include "support/logger/logger.h"

///////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////

static SysCallReturn _syscallhandler_killHelper(SysCallHandler* sys, pid_t pid, pid_t tid, int sig,
long syscallnum) {
pid_t my_tid = thread_getNativeTid(sys->thread);

// Return error if trying to stop/continue a process so we don't disrupt our ptracer.
// NOTE: If we run into signal problems, we could consider only allowing a process to
// send signals to itself, i.e., disallow inter-process signaling. See Github PR#1075.
if (sig == SIGSTOP || sig == SIGCONT) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ENOSYS};
}

debug(
"making syscall %li in native thread %i (pid=%i and tid=%i)", syscallnum, my_tid, pid, tid);

long result = 0;

switch (syscallnum) {
case SYS_kill: result = thread_nativeSyscall(sys->thread, SYS_kill, pid, sig); break;
case SYS_tkill: result = thread_nativeSyscall(sys->thread, SYS_tkill, tid, sig); break;
case SYS_tgkill:
result = thread_nativeSyscall(sys->thread, SYS_tgkill, pid, tid, sig);
break;
default: error("Invalid syscall number %li given", syscallnum); break;
}

int error = syscall_rawReturnValueToErrno(result);

debug("native syscall returned error code %i", error);

return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -error};
}

///////////////////////////////////////////////////////////
// System Calls
///////////////////////////////////////////////////////////

SysCallReturn syscallhandler_kill(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);
pid_t pid = args->args[0].as_i64;
int sig = args->args[1].as_i64;

debug("kill called on pid %i with signal %i", pid, sig);

pid_t native_pid = 0;

if (pid == -1 || pid == 0) {
// Special pids do not need translation
native_pid = pid;
} else {
// Translate from virtual to native pid
// Support -pid for kill
native_pid = (pid > 0) ? host_getNativeTID(sys->host, pid, 0)
: -host_getNativeTID(sys->host, -pid, 0);

// If there is no such thread, it's an error
if (native_pid == 0) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ESRCH};
}
}

debug("translated virtual pid %i to native pid %i", pid, native_pid);
return _syscallhandler_killHelper(sys, native_pid, 0, sig, SYS_kill);
}

SysCallReturn syscallhandler_tgkill(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);

pid_t tgid = args->args[0].as_i64;
pid_t tid = args->args[1].as_i64;
int sig = args->args[2].as_i64;

debug("tgkill called on tgid %i and tid %i with signal %i", tgid, tid, sig);

// Translate from virtual to native tgid and tid
pid_t native_tgid = host_getNativeTID(sys->host, tgid, 0);
pid_t native_tid = host_getNativeTID(sys->host, tgid, tid);

// If there is no such threads it's an error
if (native_tgid == 0 || native_tid == 0) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ESRCH};
}

debug("translated virtual tgid %i to native tgid %i and virtual tid %i to native tid %i", tgid,
native_tgid, tid, native_tid);
return _syscallhandler_killHelper(sys, native_tgid, native_tid, sig, SYS_tgkill);
}

SysCallReturn syscallhandler_tkill(SysCallHandler* sys, const SysCallArgs* args) {
utility_assert(sys && args);
pid_t tid = args->args[0].as_i64;
int sig = args->args[1].as_i64;

debug("tkill called on tid %i with signal %i", tid, sig);

// Translate from virtual to native tid
pid_t native_tid = host_getNativeTID(sys->host, 0, tid);

// If there is no such thread it's an error
if (native_tid == 0) {
return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -ESRCH};
}

debug("translated virtual tid %i to native tid %i", tid, native_tid);
return _syscallhandler_killHelper(sys, 0, native_tid, sig, SYS_tkill);
}
15 changes: 15 additions & 0 deletions src/main/host/syscall/signal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* The Shadow Simulator
* See LICENSE for licensing information
*/

#ifndef MAIN_HOST_SYSCALL_SIGNAL_H
#define MAIN_HOST_SYSCALL_SIGNAL_H

#include "main/host/syscall/protected.h"

SYSCALL_HANDLER(kill);
SYSCALL_HANDLER(tgkill);
SYSCALL_HANDLER(tkill);

#endif

0 comments on commit d6a9ec6

Please sign in to comment.