"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/Rose/BinaryAnalysis/Concolic/LinuxI386.C" between
rose-0.11.49.0.tar.gz and rose-0.11.50.0.tar.gz

About: ROSE is a compiler infrastructure to build source-to-source program transformation and analysis tools for large-scale C, C++, UPC, Fortran, OpenMP, Java, Python and PHP applications.

LinuxI386.C  (rose-0.11.49.0):LinuxI386.C  (rose-0.11.50.0)
#include <featureTests.h> #include <featureTests.h>
#ifdef ROSE_ENABLE_CONCOLIC_TESTING #ifdef ROSE_ENABLE_CONCOLIC_TESTING
#include <sage3basic.h> #include <sage3basic.h>
#include <Rose/BinaryAnalysis/Concolic/LinuxI386.h> #include <Rose/BinaryAnalysis/Concolic/LinuxI386.h>
#include <Rose/BinaryAnalysis/Concolic/ConcolicExecutor.h> #include <Rose/BinaryAnalysis/Concolic/ConcolicExecutor.h>
#include <Rose/BinaryAnalysis/Concolic/Database.h> #include <Rose/BinaryAnalysis/Concolic/Database.h>
#include <Rose/BinaryAnalysis/Concolic/ExecutionEvent.h> #include <Rose/BinaryAnalysis/Concolic/ExecutionEvent.h>
#include <Rose/BinaryAnalysis/Concolic/InputVariables.h> #include <Rose/BinaryAnalysis/Concolic/InputVariables.h>
#include <Rose/BinaryAnalysis/Concolic/SharedMemory.h>
#include <Rose/BinaryAnalysis/Concolic/Specimen.h> #include <Rose/BinaryAnalysis/Concolic/Specimen.h>
#include <Rose/BinaryAnalysis/Concolic/SystemCall.h> #include <Rose/BinaryAnalysis/Concolic/SystemCall.h>
#include <Rose/BinaryAnalysis/Concolic/TestCase.h> #include <Rose/BinaryAnalysis/Concolic/TestCase.h>
#include <Rose/BinaryAnalysis/Debugger.h> #include <Rose/BinaryAnalysis/Debugger.h>
#include <Rose/BinaryAnalysis/InstructionSemantics2/SymbolicSemantics.h> #include <Rose/BinaryAnalysis/InstructionSemantics2/SymbolicSemantics.h>
#include <Rose/BinaryAnalysis/Partitioner2/Partitioner.h> #include <Rose/BinaryAnalysis/Partitioner2/Partitioner.h>
#include <Rose/StringUtility.h> #include <Rose/StringUtility.h>
#include <boost/format.hpp> #include <boost/format.hpp>
skipping to change at line 54 skipping to change at line 55
size_t nToRead = std::min(where.size(), sizeof buffer); size_t nToRead = std::min(where.size(), sizeof buffer);
AddressInterval regionRead = map->at(where.least()).limit(nToRead).read( buffer); AddressInterval regionRead = map->at(where.least()).limit(nToRead).read( buffer);
ASSERT_require(!regionRead.isEmpty() && regionRead.size() == nToRead); ASSERT_require(!regionRead.isEmpty() && regionRead.size() == nToRead);
hasher.append(buffer, nToRead); hasher.append(buffer, nToRead);
if (regionRead.greatest() == where.greatest()) if (regionRead.greatest() == where.greatest())
break; // prevents overflow in next sta tement break; // prevents overflow in next sta tement
where = AddressInterval::hull(regionRead.greatest() + 1, where.greatest( )); where = AddressInterval::hull(regionRead.greatest() + 1, where.greatest( ));
} }
} }
static std::string
syscallName(int n) {
switch (n) {
case 1: return "exit";
case 2: return "fork";
case 3: return "read";
case 4: return "write";
case 5: return "open";
case 6: return "close";
case 7: return "waitpid";
case 8: return "creat";
case 9: return "link";
case 10: return "unlink";
case 11: return "execve";
case 12: return "chdir";
case 13: return "time";
case 14: return "mknod";
case 15: return "chmod";
case 16: return "lchown";
case 17: return "break";
case 18: return "oldstat";
case 19: return "lseek";
case 20: return "getpid";
case 21: return "mount";
case 22: return "umount";
case 23: return "setuid";
case 24: return "getuid";
case 25: return "stime";
case 26: return "ptrace";
case 27: return "alarm";
case 28: return "oldfstat";
case 29: return "pause";
case 30: return "utime";
case 31: return "stty";
case 32: return "gtty";
case 33: return "access";
case 34: return "nice";
case 35: return "ftime";
case 36: return "sync";
case 37: return "kill";
case 38: return "rename";
case 39: return "mkdir";
case 40: return "rmdir";
case 41: return "dup";
case 42: return "pipe";
case 43: return "times";
case 44: return "prof";
case 45: return "brk";
case 46: return "setgid";
case 47: return "getgid";
case 48: return "signal";
case 49: return "geteuid";
case 50: return "getegid";
case 51: return "acct";
case 52: return "umount2";
case 53: return "lock";
case 54: return "ioctl";
case 55: return "fcntl";
case 56: return "mpx";
case 57: return "setpgid";
case 58: return "ulimit";
case 59: return "oldolduname";
case 60: return "umask";
case 61: return "chroot";
case 62: return "ustat";
case 63: return "dup2";
case 64: return "getppid";
case 65: return "getpgrp";
case 66: return "setsid";
case 67: return "sigaction";
case 68: return "sgetmask";
case 69: return "ssetmask";
case 70: return "setreuid";
case 71: return "setregid";
case 72: return "sigsuspend";
case 73: return "sigpending";
case 74: return "sethostname";
case 75: return "setrlimit";
case 76: return "getrlimit";
case 77: return "getrusage";
case 78: return "gettimeofday";
case 79: return "settimeofday";
case 80: return "getgroups";
case 81: return "setgroups";
case 82: return "select";
case 83: return "symlink";
case 84: return "oldlstat";
case 85: return "readlink";
case 86: return "uselib";
case 87: return "swapon";
case 88: return "reboot";
case 89: return "readdir";
case 90: return "mmap";
case 91: return "munmap";
case 92: return "truncate";
case 93: return "ftruncate";
case 94: return "fchmod";
case 95: return "fchown";
case 96: return "getpriority";
case 97: return "setpriority";
case 98: return "profil";
case 99: return "statfs";
case 100: return "fstatfs";
case 101: return "ioperm";
case 102: return "socketcall";
case 103: return "syslog";
case 104: return "setitimer";
case 105: return "getitimer";
case 106: return "stat";
case 107: return "lstat";
case 108: return "fstat";
case 109: return "olduname";
case 110: return "iopl";
case 111: return "vhangup";
case 112: return "idle";
case 113: return "vm86old";
case 114: return "wait4";
case 115: return "swapoff";
case 116: return "sysinfo";
case 117: return "ipc";
case 118: return "fsync";
case 119: return "sigreturn";
case 120: return "clone";
case 121: return "setdomainname";
case 122: return "uname";
case 123: return "modify_ldt";
case 124: return "adjtimex";
case 125: return "mprotect";
case 126: return "sigprocmask";
case 127: return "create_module";
case 128: return "init_module";
case 129: return "delete_module";
case 130: return "get_kernel_syms";
case 131: return "quotactl";
case 132: return "getpgid";
case 133: return "fchdir";
case 134: return "bdflush";
case 135: return "sysfs";
case 136: return "personality";
case 137: return "afs_syscall";
case 138: return "setfsuid";
case 139: return "setfsgid";
case 140: return "_llseek";
case 141: return "getdents";
case 142: return "_newselect";
case 143: return "flock";
case 144: return "msync";
case 145: return "readv";
case 146: return "writev";
case 147: return "getsid";
case 148: return "fdatasync";
case 149: return "_sysctl";
case 150: return "mlock";
case 151: return "munlock";
case 152: return "mlockall";
case 153: return "munlockall";
case 154: return "sched_setparam";
case 155: return "sched_getparam";
case 156: return "sched_setscheduler";
case 157: return "sched_getscheduler";
case 158: return "sched_yield";
case 159: return "sched_get_priority_max";
case 160: return "sched_get_priority_min";
case 161: return "sched_rr_get_interval";
case 162: return "nanosleep";
case 163: return "mremap";
case 164: return "setresuid";
case 165: return "getresuid";
case 166: return "vm86";
case 167: return "query_module";
case 168: return "poll";
case 169: return "nfsservctl";
case 170: return "setresgid";
case 171: return "getresgid";
case 172: return "prctl";
case 173: return "rt_sigreturn";
case 174: return "rt_sigaction";
case 175: return "rt_sigprocmask";
case 176: return "rt_sigpending";
case 177: return "rt_sigtimedwait";
case 178: return "rt_sigqueueinfo";
case 179: return "rt_sigsuspend";
case 180: return "pread64";
case 181: return "pwrite64";
case 182: return "chown";
case 183: return "getcwd";
case 184: return "capget";
case 185: return "capset";
case 186: return "sigaltstack";
case 187: return "sendfile";
case 188: return "getpmsg";
case 189: return "putpmsg";
case 190: return "vfork";
case 191: return "ugetrlimit";
case 192: return "mmap2";
case 193: return "truncate64";
case 194: return "ftruncate64";
case 195: return "stat64";
case 196: return "lstat64";
case 197: return "fstat64";
case 198: return "lchown32";
case 199: return "getuid32";
case 200: return "getgid32";
case 201: return "geteuid32";
case 202: return "getegid32";
case 203: return "setreuid32";
case 204: return "setregid32";
case 205: return "getgroups32";
case 206: return "setgroups32";
case 207: return "fchown32";
case 208: return "setresuid32";
case 209: return "getresuid32";
case 210: return "setresgid32";
case 211: return "getresgid32";
case 212: return "chown32";
case 213: return "setuid32";
case 214: return "setgid32";
case 215: return "setfsuid32";
case 216: return "setfsgid32";
case 217: return "pivot_root";
case 218: return "mincore";
case 219: return "madvise";
case 220: return "getdents64";
case 221: return "fcntl64";
case 224: return "gettid";
case 225: return "readahead";
case 226: return "setxattr";
case 227: return "lsetxattr";
case 228: return "fsetxattr";
case 229: return "getxattr";
case 230: return "lgetxattr";
case 231: return "fgetxattr";
case 232: return "listxattr";
case 233: return "llistxattr";
case 234: return "flistxattr";
case 235: return "removexattr";
case 236: return "lremovexattr";
case 237: return "fremovexattr";
case 238: return "tkill";
case 239: return "sendfile64";
case 240: return "futex";
case 241: return "sched_setaffinity";
case 242: return "sched_getaffinity";
case 243: return "set_thread_area";
case 244: return "get_thread_area";
case 245: return "io_setup";
case 246: return "io_destroy";
case 247: return "io_getevents";
case 248: return "io_submit";
case 249: return "io_cancel";
case 250: return "fadvise64";
case 252: return "exit_group";
case 253: return "lookup_dcookie";
case 254: return "epoll_create";
case 255: return "epoll_ctl";
case 256: return "epoll_wait";
case 257: return "remap_file_pages";
case 258: return "set_tid_address";
case 259: return "timer_create";
case 260: return "timer_settime";
case 261: return "timer_gettime";
case 262: return "timer_getoverrun";
case 263: return "timer_delete";
case 264: return "clock_settime";
case 265: return "clock_gettime";
case 266: return "clock_getres";
case 267: return "clock_nanosleep";
case 268: return "statfs64";
case 269: return "fstatfs64";
case 270: return "tgkill";
case 271: return "utimes";
case 272: return "fadvise64_64";
case 273: return "vserver";
case 274: return "mbind";
case 275: return "get_mempolicy";
case 276: return "set_mempolicy";
case 277: return "mq_open";
case 278: return "mq_unlink";
case 279: return "mq_timedsend";
case 280: return "mq_timedreceive";
case 281: return "mq_notify";
case 282: return "mq_getsetattr";
case 283: return "kexec_load";
case 284: return "waitid";
case 286: return "add_key";
case 287: return "request_key";
case 288: return "keyctl";
case 289: return "ioprio_set";
case 290: return "ioprio_get";
case 291: return "inotify_init";
case 292: return "inotify_add_watch";
case 293: return "inotify_rm_watch";
case 294: return "migrate_pages";
case 295: return "openat";
case 296: return "mkdirat";
case 297: return "mknodat";
case 298: return "fchownat";
case 299: return "futimesat";
case 300: return "fstatat64";
case 301: return "unlinkat";
case 302: return "renameat";
case 303: return "linkat";
case 304: return "symlinkat";
case 305: return "readlinkat";
case 306: return "fchmodat";
case 307: return "faccessat";
case 308: return "pselect6";
case 309: return "ppoll";
case 310: return "unshare";
case 311: return "set_robust_list";
case 312: return "get_robust_list";
case 313: return "splice";
case 314: return "sync_file_range";
case 315: return "tee";
case 316: return "vmsplice";
case 317: return "move_pages";
case 318: return "getcpu";
case 319: return "epoll_pwait";
case 320: return "utimensat";
case 321: return "signalfd";
case 322: return "timerfd_create";
case 323: return "eventfd";
case 324: return "fallocate";
case 325: return "timerfd_settime";
case 326: return "timerfd_gettime";
case 327: return "signalfd4";
case 328: return "eventfd2";
case 329: return "epoll_create1";
case 330: return "dup3";
case 331: return "pipe2";
case 332: return "inotify_init1";
case 333: return "preadv";
case 334: return "pwritev";
case 335: return "rt_tgsigqueueinfo";
case 336: return "perf_event_open";
case 337: return "recvmmsg";
case 338: return "fanotify_init";
case 339: return "fanotify_mark";
case 340: return "prlimit64";
case 341: return "name_to_handle_at";
case 342: return "open_by_handle_at";
case 343: return "clock_adjtime";
case 344: return "syncfs";
case 345: return "sendmmsg";
case 346: return "setns";
case 347: return "process_vm_readv";
case 348: return "process_vm_writev";
case 349: return "kcmp";
case 350: return "finit_module";
case 351: return "sched_setattr";
case 352: return "sched_getattr";
case 353: return "renameat2";
case 354: return "seccomp";
case 355: return "getrandom";
case 356: return "memfd_create";
case 357: return "bpf";
case 358: return "execveat";
case 359: return "socket";
case 360: return "socketpair";
case 361: return "bind";
case 362: return "connect";
case 363: return "listen";
case 364: return "accept4";
case 365: return "getsockopt";
case 366: return "setsockopt";
case 367: return "getsockname";
case 368: return "getpeername";
case 369: return "sendto";
case 370: return "sendmsg";
case 371: return "recvfrom";
case 372: return "recvmsg";
case 373: return "shutdown";
case 374: return "userfaultfd";
case 375: return "membarrier";
case 376: return "mlock2";
case 377: return "copy_file_range";
case 378: return "preadv2";
case 379: return "pwritev2";
case 380: return "pkey_mprotect";
case 381: return "pkey_alloc";
case 382: return "pkey_free";
case 383: return "statx";
case 384: return "arch_prctl";
}
return "sys" + boost::lexical_cast<std::string>(n);
}
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
// LinuxI386::SyscallContext // Syscall callback base class
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
LinuxI386::SyscallContext::SyscallContext(const LinuxI386::Ptr &architecture, co LinuxI386SyscallBase::LinuxI386SyscallBase() {}
nst BS::RiscOperatorsPtr &ops,
const P2::Partitioner &partitioner, co LinuxI386SyscallBase::~LinuxI386SyscallBase() {}
nst Debugger::Ptr &debugger)
: partitioner(partitioner), debugger(debugger) { bool
ASSERT_not_null(debugger); LinuxI386SyscallBase::operator()(bool /*handled*/, SyscallContext &ctx) {
ASSERT_not_null(architecture); // Since a system call might have multiple callbacks, and since all of them
this->architecture = architecture; inherit from this class, they will all end up
ASSERT_not_null(ops); // calling this code. Some things (such as creating new events and single st
this->ops = ops; epping the concrete process) should only be
// done once, so be careful!
hello("linux i386 base class", ctx);
Sawyer::Message::Stream debug(mlog[DEBUG]);
auto i386 = ctx.architecture.dynamicCast<LinuxI386>();
ASSERT_not_null(i386);
const RegisterDescriptor SYS_RET = i386->systemCallReturnRegister();
penultimateReturnEvent_ = latestReturnEvent_;
latestReturnEvent_ = ExecutionEvent::Ptr();
if (ConcolicPhase::REPLAY == ctx.phase) {
// Pick out the return value and save it for later. The return value wil
l have been created as an event that describes
// the side effect of writing the return value to the return register. W
hen replaying, the return value will be
// concrete.
if (!ctx.returnEvent) {
for (const ExecutionEvent::Ptr &relatedEvent: ctx.relatedEvents) {
if (relatedEvent->actionType() == ExecutionEvent::Action::WRITE_
REGISTER &&
RegisterDescriptor::fromRaw(relatedEvent->scalar()) == SYS_R
ET) {
ctx.returnEvent = relatedEvent;
break;
}
}
}
ASSERT_not_null(ctx.returnEvent);
latestReturnEvent_ = ctx.returnEvent;
// Allow the child class to do its thing.
playback(ctx);
} else {
// For non-replay (i.e., concolic execution) we should make sure that th
ere's a return event if no other callback has
// already created one. If we create one here, then also give it an inpu
t variable to affect the behavior of future
// test cases for this same specimen (subsequent callbacks can remove th
is variable if desired).
if (!ctx.returnEvent) {
handlePreSyscall(ctx);
i386->debugger()->stepIntoSyscall(); // after this, we're in
the syscall-exit-stop state
// If the syscall terminated the program, we should still allow the
subclass to handle things even though we're
// not creating a return event or input variable for the return.
if (i386->debugger()->isTerminated()) {
ASSERT_require(ctx.returnEvent == nullptr);
handlePostSyscall(ctx);
ASSERT_not_reachable("unexpected concrete termination in system
call");
}
// Create the input variable and execution event for this return val
ue.
if (!ctx.symbolicReturn) {
ctx.symbolicReturn = SymbolicExpr::makeIntegerVariable(SYS_RET.n
Bits());
SAWYER_MESG(debug) <<" created input variable " <<*ctx.symbolic
Return <<"\n";
} else {
SAWYER_MESG(debug) <<" using existing variable " <<*ctx.symboli
cReturn <<"\n";
ASSERT_require(ctx.symbolicReturn->nBits() == SYS_RET.nBits());
}
uint64_t retConcrete = i386->debugger()->readRegister(SYS_RET).toInt
eger();
ctx.returnEvent = ExecutionEvent::instanceWriteRegister(i386->testCa
se(), i386->nextEventLocation(When::POST),
ctx.syscallE
vent->instructionPointer(), SYS_RET,
retConcrete)
;
ctx.returnEvent->name(ctx.syscallEvent->name() + "_return");
ctx.ops->inputVariables().insertSystemCallReturn(ctx.returnEvent, ct
x.symbolicReturn);
SAWYER_MESG(debug) <<" created " <<ctx.returnEvent->printableName(i
386->database()) <<"\n";
// Update the symbolic state
BS::SValuePtr retSValue = ctx.ops->svalueExpr(ctx.symbolicReturn);
ctx.ops->writeRegister(SYS_RET, retSValue);
SAWYER_MESG(debug) <<" return value saved in symbolic state: " <<*r
etSValue <<"\n";
}
latestReturnEvent_ = ctx.returnEvent;
handlePostSyscall(ctx);
}
return true; // handled
}
void
LinuxI386SyscallBase::hello(const std::string &name, const SyscallContext &ctx)
const {
if (name.empty()) {
SyscallCallback::hello("", ctx);
} else {
SyscallCallback::hello(name + " for " + syscallName(ctx.syscallEvent->sc
alar()) + " system call", ctx);
}
}
ExecutionEvent::Ptr
LinuxI386SyscallBase::latestReturnEvent() const {
return latestReturnEvent_;
}
ExecutionEvent::Ptr
LinuxI386SyscallBase::penultimateReturnEvent() const {
return penultimateReturnEvent_;
}
SymbolicExpr::Ptr
LinuxI386SyscallBase::penultimateSymbolicReturn() const {
if (!penultimateReturnEvent_) {
return SymbolicExpr::Ptr();
} else if (SymbolicExpr::Ptr variable = penultimateReturnEvent_->inputVariab
le()) {
return variable;
} else {
ASSERT_require(penultimateReturnEvent_->actionType() == ExecutionEvent::
Action::WRITE_REGISTER);
ASSERT_require(penultimateReturnEvent_->words().size() == 1);
const RegisterDescriptor SYS_RET = RegisterDescriptor::fromRaw(penultima
teReturnEvent_->scalar());
return SymbolicExpr::makeIntegerConstant(SYS_RET.nBits(), penultimateRet
urnEvent_->words()[0]);
}
}
void
LinuxI386SyscallBase::showRecentReturnValues(std::ostream &out, const Database::
Ptr &database) const {
if (!latestReturnEvent_) {
out <<" this system call has not yet returned\n";
} else {
const RegisterDescriptor SYS_RET = RegisterDescriptor::fromRaw(latestRet
urnEvent_->scalar());
out <<" latest return event: " <<latestReturnEvent_->printableName(data
base) <<"\n";
out <<" latest return concrete: "
<<StringUtility::toHex2(latestReturnEvent_->words()[0], SYS_RET.nBit
s()) <<"\n";
}
if (!penultimateReturnEvent_) {
out <<" this is the first occurrence of this system call\n";
} else {
const RegisterDescriptor SYS_RET = RegisterDescriptor::fromRaw(latestRet
urnEvent_->scalar());
out <<" penultimate return event: " <<penultimateReturnEvent_->printabl
eName(database) <<"\n";
if (penultimateReturnEvent_->inputVariable())
out <<" penultimate return variable: " <<*penultimateReturnEvent_->
inputVariable() <<"\n";
out <<" penultimate return concrete: "
<<StringUtility::toHex2(penultimateReturnEvent_->words()[0],
SYS_RET.nBits()) <<"\n";
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// System calls that are unimplemented
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
LinuxI386SyscallUnimplemented::LinuxI386SyscallUnimplemented() {}
LinuxI386SyscallUnimplemented::~LinuxI386SyscallUnimplemented() {}
SyscallCallback::Ptr
LinuxI386SyscallUnimplemented::instance() {
return Ptr(new LinuxI386SyscallUnimplemented);
}
void
LinuxI386SyscallUnimplemented::playback(SyscallContext &ctx) {
hello("not-implemented", ctx);
mlog[ERROR] <<" " <<syscallName(ctx.syscallEvent->scalar()) <<" system call
is not implemented\n";
} }
LinuxI386::SyscallContext::~SyscallContext() {} void
LinuxI386SyscallUnimplemented::handlePostSyscall(SyscallContext &ctx) {
hello("not-implemented", ctx);
mlog[ERROR] <<" " <<syscallName(ctx.syscallEvent->scalar()) <<" system call
is not implemented\n";
}
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
// System call behaviors // System calls that terminate
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
class SyscallExitsProcess: public SyscallCallback { LinuxI386SyscallTerminates::LinuxI386SyscallTerminates() {}
public:
static Ptr instance() {
return Ptr(new SyscallExitsProcess);
}
bool operator()(bool handled, SyscallContext &ctx) const override { LinuxI386SyscallTerminates::~LinuxI386SyscallTerminates() {}
if (!handled) {
auto ops = Emulation::RiscOperators::promote(ctx.ops); SyscallCallback::Ptr
ops->doExit(ctx.argsConcrete[0]); LinuxI386SyscallTerminates::instance() {
handled = true; return Ptr(new LinuxI386SyscallTerminates);
} }
return handled;
void
LinuxI386SyscallTerminates::playback(SyscallContext &ctx) {
hello("syscall-exits-process", ctx);
ASSERT_not_reachable("cannot occur during startup phase");
}
void
LinuxI386SyscallTerminates::handlePostSyscall(SyscallContext &ctx) {
hello("syscall-exits-process", ctx);
auto ops = Emulation::RiscOperators::promote(ctx.ops);
ops->doExit(ctx.argsConcrete[0]);
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// System call return constraints.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
LinuxI386SyscallReturn::LinuxI386SyscallReturn() {}
LinuxI386SyscallReturn::~LinuxI386SyscallReturn() {}
void
LinuxI386SyscallReturn::handlePostSyscall(SyscallContext &ctx) {
hello("syscall-return-constraint", ctx);
Sawyer::Message::Stream debug(mlog[DEBUG]);
auto i386 = ctx.architecture.dynamicCast<LinuxI386>();
ASSERT_not_null(i386);
const RegisterDescriptor SYS_RET = i386->systemCallReturnRegister();
showRecentReturnValues(debug, i386->database());
// If this is the first invocation of this syscall, then there's nothing we
need to do.
if (!penultimateReturnEvent())
return;
// Build the SMT solver constraint that the current return value must be equ
al to the previous return value.
std::pair<SymbolicExpr::Ptr, Sawyer::Optional<uint64_t>> x = makeReturnConst
raint(ctx);
SymbolicExpr::Ptr constraint = x.first;
Sawyer::Optional<uint64_t> concreteReturn = x.second;
if (constraint) {
SAWYER_MESG(mlog[DEBUG]) <<" return value constraint: " <<*constraint <
<"\n";
ctx.ops->solver()->insert(constraint);
} else {
SAWYER_MESG(mlog[DEBUG]) <<" return value constraint: none\n";
} }
};
class SyscallReturnsConstant: public SyscallCallback { if (concreteReturn) {
public: SAWYER_MESG(mlog[DEBUG]) <<" modifying concrete state to have returned
static Ptr instance() { "
return Ptr(new SyscallReturnsConstant); <<StringUtility::toHex2(*concreteReturn, SYS_RE
T.nBits()) <<"\n";
i386->debugger()->writeRegister(SYS_RET, *concreteReturn);
} else {
SAWYER_MESG(mlog[DEBUG]) <<" not modifying concrete return value\n";
} }
}
bool operator()(bool handled, SyscallContext &ctx_) const override { ////////////////////////////////////////////////////////////////////////////////
LinuxI386::SyscallContext &ctx = dynamic_cast<LinuxI386::SyscallContext& ////////////////////////////////////////////////
>(ctx_); // System calls that are constant, always returning the same value.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
if (!handled) { LinuxI386SyscallConstant::LinuxI386SyscallConstant() {}
Sawyer::Message::Stream debug(mlog[DEBUG]);
LinuxI386::Ptr architecture = ctx.architecture.dynamicCast<LinuxI386
>();
ASSERT_not_null(architecture);
ctx.debugger->stepIntoSyscall(); // after this, we're
in the syscall-exit-stop state
ctx.retEvent = architecture->applySystemCallReturn(ctx.partitioner,
ctx.ops, ctx.syscallEvent->name(), ctx.callSite);
const RegisterDescriptor SYS_RET = architecture->systemCallReturnReg
ister();
ctx.retSValue = ctx.ops->undefined_(SYS_RET.nBits());
SymbolicExpr::Ptr retSymbolic = IS::SymbolicSemantics::SValue::promo
te(ctx.retSValue)->get_expression();
uint64_t retConcrete = ctx.retEvent->words()[0];
if (Sawyer::Optional<uint64_t> prevRetConcrete = ctx.systemCall->pre
viousReturnConcrete()) {
// Make the syscall return the same symbolic value as before. We
do this by adding a solver constraint to say that
// the current return value is the same as the past return value
.
SymbolicExpr::Ptr prevRetSymbolic = ctx.systemCall->previousRetu
rnSymbolic();
ASSERT_not_null(prevRetSymbolic);
SAWYER_MESG(debug) <<" symbolic return must equal previous symb
olic return\n";
SymbolicExpr::Ptr retvalsAreEqual = SymbolicExpr::makeEq(prevRet
Symbolic, retSymbolic);
ctx.ops->solver()->insert(retvalsAreEqual);
// Make the syscall return the same concrete value as before.
if (*prevRetConcrete != retConcrete) {
SAWYER_MESG(debug) <<" replacing return value with " <<Stri
ngUtility::toHex2(*prevRetConcrete, SYS_RET.nBits()) <<"\n";
ctx.retEvent->words(std::vector<uint64_t>{*prevRetConcrete})
;
ctx.debugger->writeRegister(SYS_RET, *prevRetConcrete);
}
}
// Save the concrete and symbolic return values so we can make subse LinuxI386SyscallConstant::~LinuxI386SyscallConstant() {}
quent calls return the same.
ctx.systemCall->previousReturnConcrete(retConcrete);
ctx.systemCall->previousReturnSymbolic(retSymbolic);
ctx.ops->writeRegister(SYS_RET, ctx.retSValue);
handled = true; SyscallCallback::Ptr
LinuxI386SyscallConstant::instance() {
return Ptr(new LinuxI386SyscallConstant);
}
void
LinuxI386SyscallConstant::playback(SyscallContext &ctx) {}
std::pair<SymbolicExpr::Ptr, Sawyer::Optional<uint64_t>>
LinuxI386SyscallConstant::makeReturnConstraint(SyscallContext &ctx) {
SymbolicExpr::Ptr constraint;
Sawyer::Optional<uint64_t> concreteReturn;
if (penultimateReturnEvent()) {
if (latestReturnEvent()->inputVariable()) {
SymbolicExpr::Ptr curRet = latestReturnEvent()->inputVariable();
SymbolicExpr::Ptr prevRet = penultimateSymbolicReturn();
ASSERT_not_null(prevRet);
constraint = SymbolicExpr::makeEq(curRet, prevRet);
} }
return handled;
}
};
class SyscallReturnsIncreasing: public SyscallCallback { concreteReturn = penultimateReturnEvent()->words()[0];
public:
static Ptr instance() {
return Ptr(new SyscallReturnsIncreasing);
} }
bool operator()(bool handled, SyscallContext &ctx_) const override { return {constraint, concreteReturn};
LinuxI386::SyscallContext &ctx = dynamic_cast<LinuxI386::SyscallContext& }
>(ctx_);
if (!handled) { ////////////////////////////////////////////////////////////////////////////////
Sawyer::Message::Stream debug(mlog[DEBUG]); ////////////////////////////////////////////////
LinuxI386::Ptr architecture = ctx.architecture.dynamicCast<LinuxI386 // System calls that return non-decreasing values, such as time
>(); ////////////////////////////////////////////////////////////////////////////////
ASSERT_not_null(architecture); ////////////////////////////////////////////////
ctx.debugger->stepIntoSyscall(); // after this, we're LinuxI386SyscallNondecreasing::LinuxI386SyscallNondecreasing() {}
in the syscall-exit-stop state
ctx.retEvent = architecture->applySystemCallReturn(ctx.partitioner,
ctx.ops, ctx.syscallEvent->name(), ctx.callSite);
const RegisterDescriptor SYS_RET = architecture->systemCallReturnReg
ister();
ctx.retSValue = ctx.ops->undefined_(SYS_RET.nBits());
SymbolicExpr::Ptr retSymbolic = IS::SymbolicSemantics::SValue::promo
te(ctx.retSValue)->get_expression();
uint64_t retConcrete = ctx.retEvent->words()[0];
if (Sawyer::Optional<uint64_t> prevRetConcrete = ctx.systemCall->pre
viousReturnConcrete()) {
// Make the syscall return a symbolic value that's not less than
the previous return value.
SymbolicExpr::Ptr prevRetSymbolic = ctx.systemCall->previousRetu
rnSymbolic();
ASSERT_not_null(prevRetSymbolic);
SAWYER_MESG(debug) <<" symbolic return must be >= previous symb
olic return\n";
SymbolicExpr::Ptr retvalsAreIncreasing = SymbolicExpr::makeGe(re
tSymbolic, prevRetSymbolic);
ctx.ops->solver()->insert(retvalsAreIncreasing);
// Make the syscall return a concrete value that's not less than
the previous concrete return value.
if (retConcrete < *prevRetConcrete) {
SAWYER_MESG(debug) <<" replacing return value with " <<Stri
ngUtility::toHex2(*prevRetConcrete, SYS_RET.nBits()) <<"\n";
ctx.retEvent->words(std::vector<uint64_t>{*prevRetConcrete})
;
ctx.debugger->writeRegister(SYS_RET, *prevRetConcrete);
}
}
// Save the concrete and symbolic return values for comparisons in t LinuxI386SyscallNondecreasing::~LinuxI386SyscallNondecreasing() {}
he next call.
ctx.systemCall->previousReturnConcrete(retConcrete);
ctx.systemCall->previousReturnSymbolic(retSymbolic);
ctx.ops->writeRegister(SYS_RET, ctx.retSValue);
handled = true; SyscallCallback::Ptr
LinuxI386SyscallNondecreasing::instance() {
return Ptr(new LinuxI386SyscallNondecreasing);
}
void
LinuxI386SyscallNondecreasing::playback(SyscallContext &ctx) {}
std::pair<SymbolicExpr::Ptr, Sawyer::Optional<uint64_t>>
LinuxI386SyscallNondecreasing::makeReturnConstraint(SyscallContext &ctx) {
SymbolicExpr::Ptr constraint;
Sawyer::Optional<uint64_t> concreteReturn;
if (penultimateReturnEvent()) {
if (latestReturnEvent()->inputVariable()) {
SymbolicExpr::Ptr curRet = latestReturnEvent()->inputVariable();
SymbolicExpr::Ptr prevRet = penultimateSymbolicReturn();
ASSERT_not_null(prevRet);
constraint = SymbolicExpr::makeGe(curRet, prevRet);
} }
return handled;
concreteReturn = penultimateReturnEvent()->words()[0];
} }
};
return {constraint, concreteReturn};
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// System call definitions
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
void void
LinuxI386::configureSystemCalls() { LinuxI386::configureSystemCalls() {
// These are the generally useful configurations. Feel free to override thes e to accomplish whatever kind of testing you // These are the generally useful configurations. Feel free to override thes e to accomplish whatever kind of testing you
// need. // need.
SystemCall::Ptr sc;
// SYS_exit // SYS_exit
sc = SystemCall::instance(); systemCalls(1, LinuxI386SyscallTerminates::instance());
sc->callbacks().append(SyscallExitsProcess::instance());
systemCalls().insert(1, sc);
// SYS_time // SYS_time
sc = SystemCall::instance(); systemCalls(13, LinuxI386SyscallNondecreasing::instance());
sc->callbacks().append(SyscallReturnsIncreasing::instance());
systemCalls().insert(13, sc);
// SYS_getpid // SYS_getpid
sc = SystemCall::instance(); systemCalls(20, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(20, sc);
// SYS_getuid: assumes SYS_setuid is never successfully called // SYS_getuid: assumes SYS_setuid is never successfully called
sc = SystemCall::instance(); systemCalls(24, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(24, sc);
// SYS_getgid: assumes SYS_setgid is never successfully called // SYS_getgid: assumes SYS_setgid is never successfully called
sc = SystemCall::instance(); systemCalls(47, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(47, sc);
// SYS_geteuid: assumes SYS_setuid is never successfully called // SYS_geteuid: assumes SYS_setuid is never successfully called
sc = SystemCall::instance(); systemCalls(50, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(50, sc);
// SYS_getppid // SYS_getppid
sc = SystemCall::instance(); systemCalls(64, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(64, sc);
// SYS_getpgrp // SYS_getpgrp
sc = SystemCall::instance(); systemCalls(65, LinuxI386SyscallConstant::instance());
sc->callbacks().append(SyscallReturnsConstant::instance());
systemCalls().insert(65, sc);
// SYS_exit_group // SYS_exit_group
sc = SystemCall::instance(); systemCalls(252, LinuxI386SyscallTerminates::instance());
sc->callbacks().append(SyscallExitsProcess::instance()); }
systemCalls().insert(252, sc);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// Shared memory behaviors
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// Any access to this memory exits the program.
class NullDeref: public SharedMemoryCallback {
public:
static Ptr instance() {
return Ptr(new NullDeref);
}
void playback(SharedMemoryContext&) override {}
void handlePreSharedMemory(SharedMemoryContext &ctx) override {
hello("null-deref-handler", ctx);
auto ops = Emulation::RiscOperators::promote(ctx.ops);
ops->doExit(255); // FIXME[Robb Matzke 2021-09-08]: perhaps a way to sim
ulate a segfault instead
}
};
void
LinuxI386::configureSharedMemory() {
// These are the generally useful configurations. Feel free to override thes
e to accomplish whatever kind of testing you
// need.
// Null dereferences
sharedMemory(AddressInterval::baseSize(0, 4096), NullDeref::instance());
} }
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
// LinuxI386 // LinuxI386
//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////
LinuxI386::LinuxI386(const Database::Ptr &db, TestCaseId tcid) LinuxI386::LinuxI386(const Database::Ptr &db, TestCaseId tcid, const P2::Partiti
: Architecture(db, tcid) {} oner &partitioner)
: Architecture(db, tcid, partitioner) {}
LinuxI386::~LinuxI386() {} LinuxI386::~LinuxI386() {}
LinuxI386::Ptr LinuxI386::Ptr
LinuxI386::instance(const Database::Ptr &db, TestCaseId tcid) { LinuxI386::instance(const Database::Ptr &db, TestCaseId tcid, const P2::Partitio ner &partitioner) {
ASSERT_not_null(db); ASSERT_not_null(db);
ASSERT_require(tcid); ASSERT_require(tcid);
auto retval = Ptr(new LinuxI386(db, tcid)); auto retval = Ptr(new LinuxI386(db, tcid, partitioner));
retval->configureSystemCalls(); retval->configureSystemCalls();
retval->configureSharedMemory();
return retval; return retval;
} }
LinuxI386::Ptr LinuxI386::Ptr
LinuxI386::instance(const Database::Ptr &db, const TestCase::Ptr &tc) { LinuxI386::instance(const Database::Ptr &db, const TestCase::Ptr &tc, const P2:: Partitioner &partitioner) {
ASSERT_not_null(db); ASSERT_not_null(db);
ASSERT_not_null(tc); ASSERT_not_null(tc);
TestCaseId tcid = db->id(tc); TestCaseId tcid = db->id(tc);
ASSERT_require(tcid); ASSERT_require(tcid);
auto retval = Ptr(new LinuxI386(db, tcid)); auto retval = Ptr(new LinuxI386(db, tcid, partitioner));
retval->configureSystemCalls(); retval->configureSystemCalls();
retval->configureSharedMemory();
return retval; return retval;
} }
Debugger::Ptr
LinuxI386::debugger() const {
return debugger_;
}
void void
LinuxI386::load(const boost::filesystem::path &targetDir) { LinuxI386::load(const boost::filesystem::path &targetDir) {
// Extract the executable into the working directory. // Extract the executable into the working directory.
auto exeName = boost::filesystem::path(testCase()->specimen()->name()).filen ame(); auto exeName = boost::filesystem::path(testCase()->specimen()->name()).filen ame();
if (exeName.empty()) if (exeName.empty())
exeName = "a.out"; exeName = "a.out";
exeName = targetDir / exeName; exeName = targetDir / exeName;
{ {
std::ofstream executable(exeName.string().c_str(), std::ios_base::binary | std::ios_base::trunc); std::ofstream executable(exeName.string().c_str(), std::ios_base::binary | std::ios_base::trunc);
if (!executable) { if (!executable) {
skipping to change at line 416 skipping to change at line 993
bool handled = Super::playEvent(event); bool handled = Super::playEvent(event);
switch (event->actionType()) { switch (event->actionType()) {
case ExecutionEvent::Action::RESTORE_REGISTERS: { case ExecutionEvent::Action::RESTORE_REGISTERS: {
SAWYER_MESG(mlog[DEBUG]) <<" restore registers\n"; SAWYER_MESG(mlog[DEBUG]) <<" restore registers\n";
Debugger::AllRegisters allRegisters = event->allRegisters(); Debugger::AllRegisters allRegisters = event->allRegisters();
debugger_->writeAllRegisters(allRegisters); debugger_->writeAllRegisters(allRegisters);
return true; return true;
} }
case ExecutionEvent::Action::OS_SYSCALL: { case ExecutionEvent::Action::OS_SYSCALL:
// System call events adjust the simulated operating system but not if (!handled) {
the process. If the process needs to be // If it wasn't handled by Super, then it must be because there
// adjusted then the syscall event will be followed by additional ev are no callbacks.
ents to adjust the memory and registers. These const uint64_t functionNumber = event->scalar();
// additional events are fairly generic, so we need to keep track of SyscallCallbacks callbacks = systemCalls().getOrDefault(function
some state to know they're associated with a Number);
// prior system call event. ASSERT_require(callbacks.isEmpty());
uint64_t functionNumber = event->scalar(); callbacks.append(LinuxI386SyscallUnimplemented::instance());
playingSyscall_ = std::make_pair(functionNumber, event->location().p SyscallContext ctx(sharedFromThis(), event, getRelatedEvents(eve
rimary); nt));
++syscallSequenceNumbers_[functionNumber]; return callbacks.apply(false, ctx);
playingSyscall_ = {functionNumber, event->location().primary};
return true;
}
case ExecutionEvent::Action::WRITE_REGISTER: {
// The writing-to-register already happened in Super::playEvent, but
if this event represents a system call return
// value we should save it. This is important for system calls that
always return the same value, like getpid.
ASSERT_require(handled);
if (playingSyscall_.second == event->location().primary) {
uint64_t functionNumber = playingSyscall_.first;
if (SystemCallPtr systemCall = systemCalls().getOrDefault(functi
onNumber)) {
if (RegisterDescriptor::fromRaw(event->scalar()) == systemCa
llReturnRegister()) {
systemCall->previousReturnSymbolic(event->inputVariable(
));
systemCall->previousReturnConcrete(event->words()[0]);
}
}
} }
return true; return true;
}
default: default:
return handled; return handled;
} }
} }
void void
LinuxI386::mapMemory(const AddressInterval &where, unsigned permissions) { LinuxI386::mapMemory(const AddressInterval &where, unsigned permissions) {
ASSERT_forbid(where.isEmpty()); ASSERT_forbid(where.isEmpty());
SAWYER_MESG(mlog[DEBUG]) <<"map " <<StringUtility::plural(where.size(), "byt es") <<" "; SAWYER_MESG(mlog[DEBUG]) <<"map " <<StringUtility::plural(where.size(), "byt es") <<" ";
skipping to change at line 512 skipping to change at line 1073
LinuxI386::writeRegister(RegisterDescriptor reg, const Sawyer::Container::BitVec tor &bv) { LinuxI386::writeRegister(RegisterDescriptor reg, const Sawyer::Container::BitVec tor &bv) {
debugger_->writeRegister(reg, bv); debugger_->writeRegister(reg, bv);
} }
Sawyer::Container::BitVector Sawyer::Container::BitVector
LinuxI386::readRegister(RegisterDescriptor reg) { LinuxI386::readRegister(RegisterDescriptor reg) {
return debugger_->readRegister(reg); return debugger_->readRegister(reg);
} }
void void
LinuxI386::executeInstruction() { LinuxI386::executeInstruction(const P2::Partitioner &partitioner) {
if (mlog[DEBUG]) {
rose_addr_t va = debugger_->executionAddress();
SgAsmInstruction *insn = partitioner.instructionProvider()[va];
mlog[DEBUG] <<"concretely executing insn #" <<currentLocation().primary(
)
<<" " <<partitioner.unparse(insn) <<"\n";
}
debugger_->singleStep(); debugger_->singleStep();
incrementPathLength();
} }
void void
LinuxI386::executeInstruction(const BS::RiscOperatorsPtr &ops_, SgAsmInstruction *insn) { LinuxI386::executeInstruction(const BS::RiscOperatorsPtr &ops_, SgAsmInstruction *insn) {
auto ops = Emulation::RiscOperators::promote(ops_); auto ops = Emulation::RiscOperators::promote(ops_);
ASSERT_not_null(ops); ASSERT_not_null(ops);
ASSERT_not_null(insn); ASSERT_not_null(insn);
rose_addr_t va = insn->get_address(); rose_addr_t va = insn->get_address();
// Make sure the executable has the same instruction in those bytes. // Make sure the executable has the same instruction in those bytes.
skipping to change at line 548 skipping to change at line 1115
} }
throw Exception("symbolic instruction doesn't match concrete instructon at " + StringUtility::addrToString(va)); throw Exception("symbolic instruction doesn't match concrete instructon at " + StringUtility::addrToString(va));
} }
debugger_->executionAddress(va); debugger_->executionAddress(va);
if (ops->hadSystemCall()) { if (ops->hadSystemCall()) {
debugger_->stepIntoSyscall(); debugger_->stepIntoSyscall();
} else { } else {
debugger_->singleStep(); debugger_->singleStep();
} }
incrementPathLength();
} }
void void
LinuxI386::mapScratchPage() { LinuxI386::mapScratchPage() {
ASSERT_require(debugger_->isAttached()); ASSERT_require(debugger_->isAttached());
// Create the scratch page // Create the scratch page
int64_t status = debugger_->remoteSystemCall(i386_NR_mmap, 0, 4096, int64_t status = debugger_->remoteSystemCall(i386_NR_mmap, 0, 4096,
PROT_EXEC | PROT_READ | PROT_WR ITE, PROT_EXEC | PROT_READ | PROT_WR ITE,
MAP_ANONYMOUS | MAP_PRIVATE, MAP_ANONYMOUS | MAP_PRIVATE,
skipping to change at line 649 skipping to change at line 1215
// argc // argc
//-------------------------------------------------------------------------- ------------------------------------------------- //-------------------------------------------------------------------------- -------------------------------------------------
ASSERT_require(SP.nBits() == 32); // we only handle 32-bit for now ASSERT_require(SP.nBits() == 32); // we only handle 32-bit for now
ASSERT_require(ops->currentState()->memoryState()->get_byteOrder() == memory ByteOrder()); ASSERT_require(ops->currentState()->memoryState()->get_byteOrder() == memory ByteOrder());
rose_addr_t argcVa = readRegister(SP).toInteger(); rose_addr_t argcVa = readRegister(SP).toInteger();
uint32_t argc = readMemoryUnsigned(argcVa, wordSizeBytes); uint32_t argc = readMemoryUnsigned(argcVa, wordSizeBytes);
BS::SValuePtr argcSValue = ops->undefined_(SP.nBits()); BS::SValuePtr argcSValue = ops->undefined_(SP.nBits());
SymbolicExpr::Ptr argcSymbolic = IS::SymbolicSemantics::SValue::promote(argc SValue)->get_expression(); SymbolicExpr::Ptr argcSymbolic = IS::SymbolicSemantics::SValue::promote(argc SValue)->get_expression();
auto argcEvent = ExecutionEvent::instanceWriteMemory(testCase(), nextLocatio auto argcEvent = ExecutionEvent::instanceWriteMemory(testCase(), nextEventLo
n(), ip(), argcVa, argc); cation(When::PRE),
ip(), argcVa, argc);
inputVariables.insertProgramArgumentCount(argcEvent, argcSymbolic); inputVariables.insertProgramArgumentCount(argcEvent, argcSymbolic);
argcSValue->comment(argcEvent->name()); argcSValue->comment(argcEvent->name());
ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), argcVa), arg cSValue, ops->boolean_(true)); ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), argcVa), arg cSValue, ops->boolean_(true));
ExecutionEventId argcEventId = database()->id(argcEvent); ExecutionEventId argcEventId = database()->id(argcEvent);
SAWYER_MESG(debug) <<" argc @" <<StringUtility::addrToString(argcVa) <<" = " <<argc SAWYER_MESG(debug) <<" argc @" <<StringUtility::addrToString(argcVa) <<" = " <<argc
<<"; symbolic = " <<(*argcSValue + fmt) <<"; symbolic = " <<(*argcSValue + fmt)
<<"; event = " <<*argcEventId <<"\n"; <<"; event = " <<*argcEventId <<"\n";
// The argc value cannot be less than 1 since it always points to at least t he program name. // The argc value cannot be less than 1 since it always points to at least t he program name.
skipping to change at line 692 skipping to change at line 1259
<<" \"" <<StringUtility::cEscape(s) <<"\"\n"; <<" \"" <<StringUtility::cEscape(s) <<"\"\n";
if (markingArgvAsInput_) { if (markingArgvAsInput_) {
SymbolicExpr::Ptr anyPreviousCharIsNul; // is any previous char of this argument an ASCII NUL character? SymbolicExpr::Ptr anyPreviousCharIsNul; // is any previous char of this argument an ASCII NUL character?
for (size_t j = 0; j <= s.size(); ++j) { for (size_t j = 0; j <= s.size(); ++j) {
rose_addr_t charVa = strVa + j; rose_addr_t charVa = strVa + j;
uint8_t charVal = s[j]; uint8_t charVal = s[j];
BS::SValuePtr charSValue = ops->undefined_(8); BS::SValuePtr charSValue = ops->undefined_(8);
SymbolicExpr::Ptr charSymbolic = IS::SymbolicSemantics::SValue:: promote(charSValue)->get_expression(); SymbolicExpr::Ptr charSymbolic = IS::SymbolicSemantics::SValue:: promote(charSValue)->get_expression();
auto charEvent = ExecutionEvent::instanceWriteMemory(testCase(), auto charEvent = ExecutionEvent::instanceWriteMemory(testCase(),
nextLocation(), ip(), charVa, charVal); nextEventLocation(When::PRE),
ip(), charV
a, charVal);
inputVariables.insertProgramArgument(charEvent, i, j, charSymbol ic); inputVariables.insertProgramArgument(charEvent, i, j, charSymbol ic);
charSValue->comment(charEvent->name()); charSValue->comment(charEvent->name());
ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), charVa), charSValue, ops->boolean_(true)); ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), charVa), charSValue, ops->boolean_(true));
ExecutionEventId charEventId = database()->id(charEvent); ExecutionEventId charEventId = database()->id(charEvent);
SAWYER_MESG(debug) <<" byte " <<j <<" @" <<StringUtility::a ddrToString(charVa) SAWYER_MESG(debug) <<" byte " <<j <<" @" <<StringUtility::a ddrToString(charVa)
<<"; symbolic = " <<(*charSValue + fmt) <<"; symbolic = " <<(*charSValue + fmt)
<<"; event = " <<*charEventId <<"\n"; <<"; event = " <<*charEventId <<"\n";
SymbolicExpr::Ptr currentCharIsNul = SymbolicExpr::makeEq(charSy mbolic, SymbolicExpr::makeIntegerConstant(8, 0)); SymbolicExpr::Ptr currentCharIsNul = SymbolicExpr::makeEq(charSy mbolic, SymbolicExpr::makeIntegerConstant(8, 0));
skipping to change at line 771 skipping to change at line 1339
<<" \"" <<StringUtility::cEscape(s) <<"\"\n"; <<" \"" <<StringUtility::cEscape(s) <<"\"\n";
if (markingEnvpAsInput_) { if (markingEnvpAsInput_) {
SymbolicExpr::Ptr anyPreviousCharIsNul; // is any previous char of this env an ASCII NUL character? SymbolicExpr::Ptr anyPreviousCharIsNul; // is any previous char of this env an ASCII NUL character?
for (size_t j = 0; j <= s.size(); ++j) { for (size_t j = 0; j <= s.size(); ++j) {
rose_addr_t charVa = strVa + j; rose_addr_t charVa = strVa + j;
uint8_t charVal = s[j]; uint8_t charVal = s[j];
BS::SValuePtr charSValue = ops->undefined_(8); BS::SValuePtr charSValue = ops->undefined_(8);
SymbolicExpr::Ptr charSymbolic = IS::SymbolicSemantics::SValue:: promote(charSValue)->get_expression(); SymbolicExpr::Ptr charSymbolic = IS::SymbolicSemantics::SValue:: promote(charSValue)->get_expression();
auto charEvent = ExecutionEvent::instanceWriteMemory(testCase(), auto charEvent = ExecutionEvent::instanceWriteMemory(testCase(),
nextLocation(), ip(), charVa, charVal); nextEventLocation(When::PRE),
ip(), charV
a, charVal);
inputVariables.insertEnvironmentVariable(charEvent, i, j, charSy mbolic); inputVariables.insertEnvironmentVariable(charEvent, i, j, charSy mbolic);
charSValue->comment(charEvent->name()); charSValue->comment(charEvent->name());
ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), charVa), charSValue, ops->boolean_(true)); ops->writeMemory(RegisterDescriptor(), ops->number_(SP.nBits(), charVa), charSValue, ops->boolean_(true));
ExecutionEventId charEventId = database()->id(charEvent); ExecutionEventId charEventId = database()->id(charEvent);
SAWYER_MESG(debug) <<" byte " <<j <<" @" <<StringUtility::a ddrToString(charVa) SAWYER_MESG(debug) <<" byte " <<j <<" @" <<StringUtility::a ddrToString(charVa)
<<"; symbolic = " <<(*charSValue + fmt) <<"; symbolic = " <<(*charSValue + fmt)
<<"; event = " <<charEventId <<"\n"; <<"; event = " <<charEventId <<"\n";
SymbolicExpr::Ptr currentCharIsNul = SymbolicExpr::makeEq(charSymbol ic, SymbolicExpr::makeIntegerConstant(8, 0)); SymbolicExpr::Ptr currentCharIsNul = SymbolicExpr::makeEq(charSymbol ic, SymbolicExpr::makeIntegerConstant(8, 0));
skipping to change at line 839 skipping to change at line 1408
size_t val = readMemoryUnsigned(va + wordSizeBytes, wordSizeBytes); size_t val = readMemoryUnsigned(va + wordSizeBytes, wordSizeBytes);
SAWYER_MESG(debug) <<" " <<nAuxvPairs <<": key=" <<StringUtility::add rToString(key) SAWYER_MESG(debug) <<" " <<nAuxvPairs <<": key=" <<StringUtility::add rToString(key)
<<", val=" <<StringUtility::addrToString(val) <<"\n"; <<", val=" <<StringUtility::addrToString(val) <<"\n";
if (0 == key) if (0 == key)
break; break;
++nAuxvPairs; ++nAuxvPairs;
} }
SAWYER_MESG(debug) <<" ]\n"; SAWYER_MESG(debug) <<" ]\n";
} }
ExecutionEvent::Ptr
LinuxI386::applySystemCallReturn(const P2::Partitioner &partitioner, const BS::R
iscOperatorsPtr &ops,
const std::string &syscallName, rose_addr_t sys
callVa) {
ASSERT_not_null(ops);
const RegisterDescriptor SYS_RET = systemCallReturnRegister();
// Get the concrete return value from the system call, and write it to the s
ymbolic state.
uint64_t retConcrete = debugger_->readRegister(SYS_RET).toInteger();
SAWYER_MESG(mlog[DEBUG]) <<" " <<syscallName <<" returned " <<StringUtility
::toHex2(retConcrete, SYS_RET.nBits()) <<"\n";
BS::SValuePtr retSValue = ops->number_(SYS_RET.nBits(), retConcrete);
ops->writeRegister(SYS_RET, retSValue);
// Create an execution event for the system call return value that we can re
play later in a different process.
auto event = ExecutionEvent::instanceWriteRegister(testCase(), nextLocation(
), syscallVa, SYS_RET, retConcrete);
event->name(syscallName + "-return");
return event;
}
#if 0 // [Robb Matzke 2021-05-27]
BS::SValuePtr
LinuxI386::createSystemCallReturnInput(const P2::Partitioner &partitioner, const
BS::RiscOperatorsPtr &ops_,
const std::string &syscallName, const Exe
cutionEvent::Ptr &retEvent) {
auto ops = Emulation::RiscOperators::promote(ops_);
ASSERT_not_null(ops);
const RegisterDescriptor SYS_RET = systemCallReturnRegister();
// Create a symolic variable to represent that there was a system call retur
n value that's being treated as a program
// input that could change in subsequent runs, and link this variable to the
execution event.
BS::SValuePtr variableSValue = ops->undefined_(SYS_RET.nBits());
SymbolicExpr::Ptr variableSymbolic = IS::SymbolicSemantics::SValue::promote(
variableSValue)->get_expression();
variableSValue->comment(retEvent->name());
ops->writeRegister(SYS_RET, variableSValue);
ops->inputVariables().insertSystemCallReturn(retEvent, variableSymbolic);
retEvent->name(syscallName + "-return");
return variableSValue;
}
#endif
uint64_t uint64_t
LinuxI386::systemCallFunctionNumber(const P2::Partitioner &partitioner, const BS ::RiscOperatorsPtr &ops) { LinuxI386::systemCallFunctionNumber(const P2::Partitioner &partitioner, const BS ::RiscOperatorsPtr &ops) {
ASSERT_not_null(ops); ASSERT_not_null(ops);
const RegisterDescriptor AX = partitioner.instructionProvider().registerDict ionary()->findOrThrow("eax"); const RegisterDescriptor AX = partitioner.instructionProvider().registerDict ionary()->findOrThrow("eax");
BS::SValuePtr retvalSValue = ops->readRegister(AX); BS::SValuePtr retvalSValue = ops->readRegister(AX);
ASSERT_require2(retvalSValue->isConcrete(), "non-concrete system call number s not handled yet"); ASSERT_require2(retvalSValue->isConcrete(), "non-concrete system call number s not handled yet");
return retvalSValue->toUnsigned().get(); return retvalSValue->toUnsigned().get();
} }
skipping to change at line 943 skipping to change at line 1474
LinuxI386::systemCallReturnValue(const P2::Partitioner &partitioner, const BS::R iscOperatorsPtr &ops, LinuxI386::systemCallReturnValue(const P2::Partitioner &partitioner, const BS::R iscOperatorsPtr &ops,
const BS::SValuePtr &retval) { const BS::SValuePtr &retval) {
ASSERT_not_null(ops); ASSERT_not_null(ops);
const RegisterDescriptor reg = systemCallReturnRegister(); const RegisterDescriptor reg = systemCallReturnRegister();
ops->writeRegister(reg, retval); ops->writeRegister(reg, retval);
return retval; return retval;
} }
void void
LinuxI386::systemCall(const P2::Partitioner &partitioner, const BS::RiscOperator sPtr &ops_) { LinuxI386::systemCall(const P2::Partitioner &partitioner, const BS::RiscOperator sPtr &ops_) {
auto ops = Emulation::RiscOperators::promote(ops_);
ASSERT_not_null(ops);
Sawyer::Message::Stream debug(mlog[DEBUG]);
// A system call has been encountered. The INT instruction has been processe d symbolically (basically a no-op other than // A system call has been encountered. The INT instruction has been processe d symbolically (basically a no-op other than
// to adjust the instruction pointer), and the concrete execution has steppe d into the system call but has not yet executed // to adjust the instruction pointer), and the concrete execution has steppe d into the system call but has not yet executed
// it (i.e., the subordinate process is in the syscall-enter-stop state). // it (i.e., the subordinate process is in the syscall-enter-stop state).
SyscallContext ctx(sharedFromThis(), ops_, partitioner, debugger_); auto ops = Emulation::RiscOperators::promote(ops_);
ASSERT_not_null(ops);
Sawyer::Message::Stream debug(mlog[DEBUG]);
const rose_addr_t ip = debugger_->executionAddress();
//------------------------------------- //-------------------------------------
// Create system call execution event. // Create system call execution event.
//------------------------------------- //-------------------------------------
// Gather info about the system call such as its arguments. On Linux, system calls have up to six arguments stored // Gather info about the system call such as its arguments. On Linux, system calls have up to six arguments stored
// in registers, so we just grab all six for now since we don't want to main tain a big switch statement to say how // in registers, so we just grab all six for now since we don't want to main tain a big switch statement to say how
// many arguments each system call actually uses. // many arguments each system call actually uses.
ctx.callSite = debugger_->executionAddress(); std::vector<uint64_t> argsConcrete;
uint64_t functionNumber = systemCallFunctionNumber(partitioner, ops); uint64_t functionNumber = systemCallFunctionNumber(partitioner, ops);
for (size_t i = 0; i < 6; ++i) { for (size_t i = 0; i < 6; ++i) {
BS::SValuePtr argSymbolic = systemCallArgument(partitioner, ops, i); BS::SValuePtr argSymbolic = systemCallArgument(partitioner, ops, i);
if (auto argConcrete = argSymbolic->toUnsigned()) { if (auto argConcrete = argSymbolic->toUnsigned()) {
ctx.argsConcrete.push_back(*argConcrete); argsConcrete.push_back(*argConcrete);
} else { } else {
ASSERT_not_implemented("non-concrete system call argument"); ASSERT_not_implemented("non-concrete system call argument");
} }
} }
if (debug) { if (debug) {
debug <<"syscall-" <<functionNumber <<", args = ("; debug <<" " <<syscallName(functionNumber) <<" system call (sys" <<funct
for (uint64_t arg: ctx.argsConcrete) ionNumber <<"), potential args:\n";
debug <<" " <<StringUtility::toHex(arg); for (uint64_t arg: argsConcrete)
debug <<" )\n"; debug <<" " <<StringUtility::toHex(arg) <<"\n";
} }
// The execution event records the system call number and arguments, but not any side effects (because side effects haven't // The execution event records the system call number and arguments, but not any side effects (because side effects haven't
// happened yet). Since the side effect events (created shortly) are general things like "write this value to this // happened yet). Since the side effect events (created shortly) are general things like "write this value to this
// register", the fact that they're preceded by this syscall event is what m arks them as being side effects of this system // register", the fact that they're preceded by this syscall event is what m arks them as being side effects of this system
// call. // call.
ctx.syscallEvent = ExecutionEvent::instanceSyscall(testCase(), nextLocation( auto syscallEvent = ExecutionEvent::instanceSyscall(testCase(), nextEventLoc
), ctx.callSite, functionNumber, ctx.argsConcrete); ation(When::PRE),
ctx.syscallEvent->name((boost::format("syscall-%d.%d") % functionNumber % sy ip, functionNumber, args
scallSequenceNumbers_[functionNumber]++).str()); Concrete);
ExecutionEventId syscallEventId = database()->id(ctx.syscallEvent); syscallEvent->name(syscallName(functionNumber) + "_" + boost::lexical_cast<s
SAWYER_MESG(debug) <<" created execution event " <<*syscallEventId <<" for td::string>(syscallEvent->location().primary()));
" <<ctx.syscallEvent->name() <<"\n"; database()->save(syscallEvent);
SAWYER_MESG(debug) <<" created " <<syscallEvent->printableName(database())
<<"\n";
//-------------------------------------
// Process the system call
//-------------------------------------
// Process the system call by invoking callbacks that the user can override. // Process the system call by invoking callbacks that the user can override.
if ((ctx.systemCall = systemCalls().getOrDefault(functionNumber))) { SyscallContext ctx(sharedFromThis(), ops, syscallEvent);
bool handled = ctx.systemCall->callbacks().apply(false, ctx); SyscallCallbacks callbacks = systemCalls().getOrDefault(functionNumber);
if (!handled) { bool handled = callbacks.apply(false, ctx);
mlog[ERROR] <<"syscall-" <<functionNumber <<" was not handled by any if (!handled) {
callback\n"; callbacks.append(LinuxI386SyscallUnimplemented::instance());
debugger_->stepIntoSyscall(); // after this, we're callbacks.apply(false, ctx);
in the syscall-exit-stop state
// Create an event and input variable for the system call return val
ue.
ctx.retEvent = applySystemCallReturn(partitioner, ops, ctx.syscallEv
ent->name(), ctx.callSite);
const RegisterDescriptor SYS_RET = systemCallReturnRegister();
ctx.retSValue = ops->undefined_(SYS_RET.nBits());
ops->writeRegister(SYS_RET, ctx.retSValue);
}
} else {
mlog[ERROR] <<"syscall-" <<functionNumber <<" has no declaration\n";
debugger_->stepIntoSyscall(); // after this, we're in
the syscall-exit-stop state
} }
ASSERT_not_null(ctx.returnEvent); // if the syscall didn't exit, then it must have returned
//------------------------------------ //------------------------------------
// Record any additional side effects // Record any additional side effects
//------------------------------------ //------------------------------------
if (ctx.retSValue) { // If there's a symbolic return value, then it must be the input variable fo
ASSERT_not_null(ctx.retEvent); r the return.
SymbolicExpr::Ptr retSymbolic = IS::SymbolicSemantics::SValue::promote(c if (ctx.symbolicReturn) {
tx.retSValue)->get_expression(); ASSERT_not_null(ctx.returnEvent);
ops->inputVariables().insertSystemCallReturn(ctx.retEvent, retSymbolic); ASSERT_require(ctx.symbolicReturn == ctx.returnEvent->inputVariable());
SAWYER_MESG(debug) <<" created input variable " <<*ctx.retSValue } else {
<<" for " <<ctx.retEvent->printableName(database()) < ASSERT_require(ctx.returnEvent->inputVariable() == nullptr);
<"\n";
} }
if (ctx.retEvent) { // Make sure all events have been written to the database.
ExecutionEventId retEventId = database()->id(ctx.retEvent); database()->save(ctx.syscallEvent);
SAWYER_MESG(debug) <<" created execution event " <<*retEventId database()->save(ctx.returnEvent); // should be in relatedE
<<" for " <<ctx.retEvent->name() <<"\n"; vents, but just in case...
for (const ExecutionEvent::Ptr &event: ctx.relatedEvents)
database()->save(event);
}
std::pair<ExecutionEvent::Ptr, SymbolicExpr::Ptr>
LinuxI386::sharedMemoryRead(const SharedMemoryCallbacks &callbacks, const P2::Pa
rtitioner &partitioner,
const BS::RiscOperators::Ptr &ops_, rose_addr_t addr
, size_t nBytes) {
auto ops = Emulation::RiscOperators::promote(ops_);
ASSERT_not_null(ops);
Sawyer::Message::Stream debug(mlog[DEBUG]);
const rose_addr_t ip = ops->currentInstruction()->get_address();
SAWYER_MESG(debug) <<" shared memory read at instruction " <<StringUtility:
:addrToString(ip)
<<" from memory address " <<StringUtility::addrToString(a
ddr)
<<" for " <<StringUtility::plural(nBytes, "bytes") <<"\n"
;
// Create an input variable for the value read from shared memory, and bind
it to a new event that indicates that this
// instruction is reading from shared memory.
auto valueRead = SymbolicExpr::makeIntegerVariable(8 * nBytes);
auto sharedMemoryEvent = ExecutionEvent::instanceSharedMemoryRead(testCase()
, nextEventLocation(When::PRE), ip, addr, nBytes);
sharedMemoryEvent->name("shm_read_" + StringUtility::addrToString(addr).subs
tr(2) +
"_" + boost::lexical_cast<std::string>(sharedMemoryE
vent->location().primary()));
ops->inputVariables().insertSharedMemoryRead(sharedMemoryEvent, valueRead);
database()->save(sharedMemoryEvent);
SAWYER_MESG(debug) <<" created input variable " <<*valueRead
<<" for " <<sharedMemoryEvent->printableName(database())
<<"\n";
// Invoke the callbacks
SharedMemoryContext ctx(sharedFromThis(), ops, sharedMemoryEvent);
bool handled = callbacks.apply(false, ctx);
if (!handled) {
mlog[ERROR] <<" shared memory read not handled by any callbacks; trea
ting it as normal memory\n";
return {ExecutionEvent::Ptr(), SymbolicExpr::Ptr()};
} else if (!ctx.valueRead) {
SAWYER_MESG(debug) <<" shared memory read did not return a special va
lue; doing a normal read\n";
} else {
SAWYER_MESG(debug) <<" shared memory read returns " <<*ctx.valueRead
<<"\n";
ASSERT_require(ctx.valueRead->nBits() == 8 * nBytes);
} }
// Post-callback actions
database()->save(sharedMemoryEvent); // just in case the user mod
ified it.
return {ctx.sharedMemoryEvent, ctx.valueRead};
} }
} // namespace } // namespace
} // namespace } // namespace
} // namespace } // namespace
#endif #endif
 End of changes. 61 change blocks. 
317 lines changed or deleted 909 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)