From e5accda99e40cbd09ed3fd192326bbb23480445b Mon Sep 17 00:00:00 2001 From: czxvan <2889259641@qq.com> Date: Wed, 28 Feb 2024 23:13:06 +0800 Subject: [PATCH] feat: add arm support --- arm/Makefile | 9 ++ arm/aflCall.c | 101 +++++++++++++ arm/argfd.c | 169 ++++++++++++++++++++++ arm/driver.c | 164 +++++++++++++++++++++ arm/drv.h | 26 ++++ arm/gen.py | 157 ++++++++++++++++++++ arm/parse.c | 93 ++++++++++++ arm/sysc.c | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++ arm/sysc.h | 18 +++ 9 files changed, 1125 insertions(+) create mode 100644 arm/Makefile create mode 100644 arm/aflCall.c create mode 100644 arm/argfd.c create mode 100644 arm/driver.c create mode 100644 arm/drv.h create mode 100755 arm/gen.py create mode 100644 arm/parse.c create mode 100644 arm/sysc.c create mode 100644 arm/sysc.h diff --git a/arm/Makefile b/arm/Makefile new file mode 100644 index 00000000..931ac18e --- /dev/null +++ b/arm/Makefile @@ -0,0 +1,9 @@ +CFLAGS= -g +CC=arm-linux-gnueabi-gcc +all : driver + +OBJS= aflCall.o driver.o parse.o sysc.o argfd.o +driver: $(OBJS) + $(CC) $(CFLAGS) -static -o $@ $(OBJS) +clean: + rm -f $(OBJS) $(HOBJS) driver diff --git a/arm/aflCall.c b/arm/aflCall.c new file mode 100644 index 00000000..3f43d843 --- /dev/null +++ b/arm/aflCall.c @@ -0,0 +1,101 @@ +/* + * AFL hypercalls + * + * Compile with -DTEST to take inputs from stdin without using hypercalls. + */ + +#include +#include +#include +#include +#include +#include "drv.h" + +int aflTestMode = 0; + +#define SZ 4096 +static u_long bufsz; +static char *buf; +static u_int32_t *arr; + +static void +aflInit(void) +{ + static int aflInit = 0; + char *pg; + + if(aflInit) + return; + + pg = mmap(NULL, SZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0); + if(pg == (void*)-1) { + perror("mmap"); + exit(1); + } + memset(pg, 0, SZ); // touch all the bits! + + arr = (u_int32_t *)pg; + buf = pg + 4 * sizeof arr[0]; + bufsz = SZ - 4 * sizeof arr[0]; + + aflInit = 1; +} + +static inline u_long +aflCall(u_long a0, u_long a1, u_long a2) +{ + u_long ret; + register long r0 asm ("r0") = a0; + register long r1 asm ("r1") = a1; + register long r2 asm ("r2") = a2; + asm("swi 0x4c4641" //we saw this in the Samsung Emulator too! + : "=r"(r0) + : "r"(r0), "r"(r1), "r"(r2) + ); + ret = (u_long)r0; + return ret; +} + +int +startForkserver(int ticks) +{ + aflInit(); + if(aflTestMode) + return 0; + return aflCall(1, ticks, 0); +} + +char * +getWork(u_long *sizep) +{ + aflInit(); + if(aflTestMode) + *sizep = read(0, buf, bufsz); + else + *sizep = aflCall(2, (u_long)buf, bufsz); + return buf; +} + +/* buf should point to u_int64_t[2] */ +int +startWork(u_int32_t start, u_int32_t end) +{ + aflInit(); + if(aflTestMode) + return 0; + arr[0] = start; + arr[1] = end; + arr[2] = 0; + arr[3] = 0; + return aflCall(3, (u_long)arr, 0); +} + +int +doneWork(int val) +{ + aflInit(); + if(aflTestMode) + return 0; + return aflCall(4, (u_long)val, 0); +} + diff --git a/arm/argfd.c b/arm/argfd.c new file mode 100644 index 00000000..81b67a84 --- /dev/null +++ b/arm/argfd.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include "drv.h" +#include "sysc.h" + +/* to ease compilation when memfd isnt supported on compilation host */ +#define MFD_CLOEXEC 0x0001U +#define MFD_ALLOW_SEALING 0x0002U +#define SYS_memfd_create 319 /* x86 */ +static int memfd_create(const char *name, unsigned int flags) { + return syscall(SYS_memfd_create, name, flags); +} + +int getStdFile(int typ) +{ + int fd, pipes[2]; + + fd = -1; + switch(typ) { +#define F(n, fn, flg) case n: fd = open(fn, flg); break; + F(0, "/", O_RDONLY); + F(1, "/proc/uptime", O_RDONLY); +#define S(n, a,b,c) case n: fd = socket(a, b, c); break; + S(2, AF_INET, SOCK_STREAM, 0); + S(3, AF_INET, SOCK_DGRAM, 0); + S(4, AF_UNIX, SOCK_STREAM, 0); + S(5, AF_UNIX, SOCK_DGRAM, 0); + + case 6: + if(pipe(pipes) == -1) return -1; + fd = pipes[0]; + break; + case 7: + if(pipe(pipes) == -1) return -1; + fd = pipes[1]; + break; + +#define EV(n, i, fl) case n: fd = eventfd(i, fl); break; + EV( 8, 0, 0); + EV( 9, 1, EFD_CLOEXEC); + EV(10, 2, EFD_NONBLOCK); + EV(11, 3, EFD_SEMAPHORE); + EV(12, 4, EFD_CLOEXEC | EFD_NONBLOCK) + EV(13, 5, EFD_CLOEXEC | EFD_SEMAPHORE) + EV(14, 6, EFD_NONBLOCK | EFD_SEMAPHORE) + EV(15, 7, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE) + +#define EP(n, x) case n: fd = epoll_create(x); break; + EP(16, 1); + EP(17, EPOLL_CLOEXEC); + +#define IN(n, x) case n: fd = inotify_init1(x); break; + IN(18, 0); + IN(19, IN_NONBLOCK); + IN(20, IN_CLOEXEC); + IN(21, IN_NONBLOCK | IN_CLOEXEC); + +#define MEM(n, nm, fl) case n: fd = memfd_create(nm, fl); break; + MEM(22, "memfd1", 0); + MEM(23, "memfd2", MFD_CLOEXEC); + MEM(24, "memfd3", MFD_ALLOW_SEALING); + MEM(25, "memfd4", MFD_CLOEXEC | MFD_ALLOW_SEALING); + +#define TIM(n, t, fl) case n: fd = timerfd_create(t, fl); break; + TIM(26, CLOCK_REALTIME, 0); + TIM(27, CLOCK_REALTIME, TFD_NONBLOCK); + TIM(28, CLOCK_REALTIME, TFD_CLOEXEC); + TIM(29, CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + TIM(30, CLOCK_MONOTONIC, 0); + TIM(31, CLOCK_MONOTONIC, TFD_NONBLOCK); + TIM(32, CLOCK_MONOTONIC, TFD_CLOEXEC); + TIM(33, CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + + S(34, AF_UNIX, SOCK_STREAM, 0); + S(35, AF_UNIX, SOCK_DGRAM, 0); + S(36, AF_UNIX, SOCK_SEQPACKET, 0); + S(37, AF_UNIX, SOCK_RAW, 0); + S(38, AF_UNIX, SOCK_RDM, 0); + S(39, AF_UNIX, SOCK_PACKET, 0); + S(40, AF_INET, SOCK_STREAM, 0); + S(41, AF_INET, SOCK_DGRAM, 0); + S(42, AF_INET, SOCK_SEQPACKET, 0); + S(43, AF_INET, SOCK_RAW, IPPROTO_RAW); + S(44, AF_INET, SOCK_RDM, 0); + S(45, AF_INET, SOCK_PACKET, 0); + S(46, AF_INET6, SOCK_STREAM, 0); + S(47, AF_INET6, SOCK_DGRAM, 0); + S(48, AF_INET6, SOCK_SEQPACKET, 0); + S(49, AF_INET6, SOCK_RAW, IPPROTO_RAW); + S(50, AF_INET6, SOCK_RDM, 0); + S(51, AF_INET6, SOCK_PACKET, 0); + S(52, AF_IPX, SOCK_STREAM, 0); + S(53, AF_IPX, SOCK_DGRAM, 0); + S(54, AF_IPX, SOCK_SEQPACKET, 0); + S(55, AF_IPX, SOCK_RAW, 0); + S(56, AF_IPX, SOCK_RDM, 0); + S(57, AF_IPX, SOCK_PACKET, 0); + S(58, AF_NETLINK, SOCK_STREAM, 0); + S(59, AF_NETLINK, SOCK_DGRAM, 0); + S(60, AF_NETLINK, SOCK_SEQPACKET, 0); + S(61, AF_NETLINK, SOCK_RAW, 0); + S(62, AF_NETLINK, SOCK_RDM, 0); + S(63, AF_NETLINK, SOCK_PACKET, 0); + S(64, AF_X25, SOCK_STREAM, 0); + S(65, AF_X25, SOCK_DGRAM, 0); + S(66, AF_X25, SOCK_SEQPACKET, 0); + S(67, AF_X25, SOCK_RAW, 0); + S(68, AF_X25, SOCK_RDM, 0); + S(69, AF_X25, SOCK_PACKET, 0); + S(70, AF_AX25, SOCK_STREAM, 0); + S(71, AF_AX25, SOCK_DGRAM, 0); + S(72, AF_AX25, SOCK_SEQPACKET, 0); + S(73, AF_AX25, SOCK_RAW, 0); + S(74, AF_AX25, SOCK_RDM, 0); + S(75, AF_AX25, SOCK_PACKET, 0); + S(76, AF_ATMPVC, SOCK_STREAM, 0); + S(77, AF_ATMPVC, SOCK_DGRAM, 0); + S(78, AF_ATMPVC, SOCK_SEQPACKET, 0); + S(79, AF_ATMPVC, SOCK_RAW, 0); + S(80, AF_ATMPVC, SOCK_RDM, 0); + S(81, AF_ATMPVC, SOCK_PACKET, 0); + S(82, AF_APPLETALK, SOCK_STREAM, 0); + S(83, AF_APPLETALK, SOCK_DGRAM, 0); + S(84, AF_APPLETALK, SOCK_SEQPACKET, 0); + S(85, AF_APPLETALK, SOCK_RAW, 0); + S(86, AF_APPLETALK, SOCK_RDM, 0); + S(87, AF_APPLETALK, SOCK_PACKET, 0); + S(88, AF_PACKET, SOCK_STREAM, 0); + S(89, AF_PACKET, SOCK_DGRAM, 0); + S(90, AF_PACKET, SOCK_SEQPACKET, 0); + S(91, AF_PACKET, SOCK_RAW, 0); + S(92, AF_PACKET, SOCK_RDM, 0); + S(93, AF_PACKET, SOCK_PACKET, 0); + S(94, AF_ALG, SOCK_STREAM, 0); + S(95, AF_ALG, SOCK_DGRAM, 0); + S(96, AF_ALG, SOCK_SEQPACKET, 0); + S(97, AF_ALG, SOCK_RAW, 0); + S(98, AF_ALG, SOCK_RDM, 0); + S(99, AF_ALG, SOCK_PACKET, 0); + +#define SP(n, f, ty, idx) case n: socketpair(f, ty, 0, pipes); fd = pipes[idx]; break + SP(100, AF_UNIX, SOCK_STREAM, 0); + SP(101, AF_UNIX, SOCK_STREAM, 1); + SP(102, AF_UNIX, SOCK_DGRAM, 0); + SP(103, AF_UNIX, SOCK_DGRAM, 1); + SP(104, AF_UNIX, SOCK_SEQPACKET, 0); + SP(105, AF_UNIX, SOCK_SEQPACKET, 1); + + default: + // XXX nonblocking sockets? + // XXX AF_NETLINK x (DGRAM,RAW) x protonr 0..22 + // XXX (INET, INET6) x SOCK_RAW x protonr 0..256 + // XXX PACKET X (DGRAM, RAW) x htons(rawtypes) + // XXX weird files from /dev, /proc, /sys, etc.. + return -1; + } + return fd; +} + diff --git a/arm/driver.c b/arm/driver.c new file mode 100644 index 00000000..2643897e --- /dev/null +++ b/arm/driver.c @@ -0,0 +1,164 @@ +/* + * Syscall driver + */ + +#include +#include +#include +#include +#include +#include + +#include "drv.h" +#include "sysc.h" + +#define MAXFILTCALLS 10 + +static void usage(char *prog) { + printf("usage: %s [-tvx] [-f nr]*\n", prog); + printf("\t\t-f nr\tFilter out cases that dont make this call. Can be repeated\n"); + printf("\t\t-t\ttest mode, dont use AFL hypercalls\n"); + printf("\t\t-T\tenable qemu's timer in forked children\n"); + printf("\t\t-v\tverbose mode\n"); + printf("\t\t-x\tdon't perform system call\n"); + exit(1); +} + +/* + * catch the driver if it dies and end the test successfully. + * The driver getting killed is good behavior, not a kernel flaw. + */ +static void watcher(void) { + int pid, status; + + if((pid = fork()) == 0) + return; + + waitpid(pid, &status, 0); + /* if we got here the driver died */ + doneWork(0); + exit(0); +} + +static int +parseU16(char *p, unsigned short *x) +{ + unsigned long val; + char *endp; + + val = strtoul(p, &endp, 10); + if(endp == p || *endp != 0 + || val < 0 || val >= 65536) + return -1; + *x = val; + return 0; +} + +/* return true if we should execute this call */ +static int +filterCalls(unsigned short *filtCalls, int nFiltCalls, struct sysRec *recs, int nrecs) +{ + int i, j, match; + + /* all records should have calls on the filtCalls list */ + for(i = 0; i < nrecs; i++) { + match = 0; + for(j = 0; j < nFiltCalls; j++) { + if(recs[i].nr == filtCalls[j]) + match = 1; + } + /* note: empty list is a match */ + if(!match && nFiltCalls > 0) + return 0; + } + return 1; +} + +int verbose = 0; + +int +main(int argc, char **argv) +{ + struct sysRec recs[3]; + struct slice slice; + unsigned short filtCalls[MAXFILTCALLS]; + char *prog, *buf; + u_long sz; + long x; + int opt, nrecs, nFiltCalls, parseOk; + int noSyscall = 0; + int enableTimer = 0; + + nFiltCalls = 0; + prog = argv[0]; + while((opt = getopt(argc, argv, "f:tTvx")) != -1) { + switch(opt) { + case 'f': + if(nFiltCalls >= MAXFILTCALLS) { + printf("too many -f args!\n"); + exit(1); + } + if(parseU16(optarg, &filtCalls[nFiltCalls]) == -1) { + printf("bad arg to -f: %s\n", optarg); + exit(1); + } + nFiltCalls++; + break; + case 't': + aflTestMode = 1; + break; + case 'T': + enableTimer = 1; + break; + case 'v': + verbose++; + break; + case 'x': + noSyscall = 1; + break; + case '?': + default: + usage(prog); + break; + } + } + argc -= optind; + argv += optind; + if(argc) + usage(prog); + + if(!aflTestMode) + watcher(); + startForkserver(enableTimer); + buf = getWork(&sz); + printf("got work: %ld - %.*s\n", sz, (int)sz, buf); + + /* trace our driver code while parsing workbuf */ + extern void _start(), __libc_start_main(); + startWork((u_long)_start, (u_long)__libc_start_main); + mkSlice(&slice, buf, sz); + parseOk = parseSysRecArr(&slice, 3, recs, &nrecs); + if(verbose) { + printf("read %ld bytes, parse result %d nrecs %d\n", sz, parseOk, (int)nrecs); + if(parseOk == 0) + showSysRecArr(recs, nrecs); + } + + if(parseOk == 0 && filterCalls(filtCalls, nFiltCalls, recs, nrecs)) { + /* trace kernel code while performing syscalls */ + startWork(0xc0000000L, 0xdfffffffL); + if(noSyscall) { + x = 0; + } else { + /* note: if this crashes, watcher will do doneWork for us */ + x = doSysRecArr(recs, nrecs); + } + if (verbose) printf("syscall returned %ld\n", x); + } else { + if (verbose) printf("Rejected by filter\n"); + } + fflush(stdout); + doneWork(0); + return 0; +} + diff --git a/arm/drv.h b/arm/drv.h new file mode 100644 index 00000000..de827cb8 --- /dev/null +++ b/arm/drv.h @@ -0,0 +1,26 @@ + +#include + +struct slice { + unsigned char *cur; + unsigned char *end; +}; + +/* aflCall.c */ +extern int aflTestMode; +int startForkserver(int ticks); +char *getWork(u_long *sizep); +int startWork(u_int32_t start, u_int32_t end); +int doneWork(int val); + +/* parse.c */ +void mkSlice(struct slice *b, void *base, size_t sz); +unsigned char *sliceBuf(struct slice *b); +size_t sliceSize(struct slice *b); +int getEOF(struct slice *b); +int getU8(struct slice *b, u_int8_t *x); +int getU16(struct slice *b, u_int16_t *x); +int getU32(struct slice *b, u_int32_t *x); +// int getU64(struct slice *b, u_int32_t *x); +int getDelimSlices(struct slice *b, char *delim, int delsz, size_t max, struct slice *x, size_t *nx); + diff --git a/arm/gen.py b/arm/gen.py new file mode 100755 index 00000000..bb1a7600 --- /dev/null +++ b/arm/gen.py @@ -0,0 +1,157 @@ +""" +Generate syscall input files in the driver's file format. +""" +import struct, sys, subprocess + +BUFDELIM = "\xa5\xc9" +CALLDELIM = "\xb7\xe3" + +class Buf(object) : + def __init__(self) : + self.buf = [] + self.pos = 0 + def add(self, x) : + #print repr(self), 'add', x.encode('hex') + self.buf.append(x) + def pack(self, fmt, *args) : + x = struct.pack(fmt, *args) + self.add(x) + def __str__(self) : + return ''.join(self.buf) + +class Num(object) : + def __init__(self, v) : + self.v = v + def mkArg(self, buf, xtra) : + buf.pack('!B', 0) + buf.pack('!L', self.v) +class Alloc(object) : + def __init__(self, sz) : + self.sz = sz + def mkArg(self, buf, xtra) : + buf.pack('!BI', 1, self.sz) +class String(object) : + def __init__(self, v) : + self.v = v + def Len(self) : + return Len(self) + def mkArgTyp(self, typ, buf, xtra) : + self.pos = xtra.pos + xtra.pos += 1 + buf.pack('!B', typ) + xtra.add(BUFDELIM) + xtra.add(self.v) + def mkArg(self, buf, xtra) : + self.mkArgTyp(2, buf, xtra) +def StringZ(v) : + return String(v + '\0') + +class Len(object) : + def mkArg(self, buf, xtra) : + buf.pack('!B', 3) +class File(String) : + def mkArg(self, buf, xtra) : + self.mkArgTyp(4, buf, xtra) +class StdFile(object) : + def __init__(self, v) : + self.v = v + def mkArg(self, buf, xtra) : + buf.pack('!BH', 5, self.v) +class Vec64(object) : + def __init__(self, *vs) : + assert len(vs) < 256 + self.v = vs + def mkArg(self, buf, xtra) : + buf.pack('!BB', 7, len(self.v)) + for x in self.v : + mkArg(buf, xtra, x) +class Filename(String) : + def mkArg(self, buf, xtra) : + self.mkArgTyp(8, buf, xtra) +class Pid(object) : + def __init__(self, v) : + self.v = v + def mkArg(self, buf, xtra) : + buf.pack('!BB', 9, self.v) +MyPid = Pid(0) +PPid = Pid(1) +ChildPid = Pid(2) + +class Ref(object) : + def __init__(self, nc, na) : + self.nc, self.na = nc,na + def mkArg(self, buf, xtra) : + buf.pack('!BBB', 10, self.nc, self.na) +class Vec32(object) : + def __init__(self, *vs) : + assert len(vs) < 256 + self.v = vs + def mkArg(self, buf, xtra) : + buf.pack('!BB', 11, len(self.v)) + for x in self.v : + mkArg(buf, xtra, x) + +def mkArg(buf, xtra, x) : + if isinstance(x, str) : + x = StringZ(x) + elif isinstance(x, int) or isinstance(x, long) : + x = Num(x) + x.mkArg(buf, xtra) + +def mkSyscall(nr, *args) : + args = list(args) + while len(args) < 6 : + args.append(0) + + buf = Buf() + xtra = Buf() + buf.pack('!H', nr) + for n,arg in enumerate(args) : + #print 'arg', n + mkArg(buf, xtra, arg) + return str(buf) + str(xtra) + +def mkSyscalls(*calls) : + r = [] + for call in calls : + r.append(mkSyscall(*call)) + return CALLDELIM.join(r) + +def writeFn(fn, buf) : + with file(fn, 'w') as f : + f.write(buf) + +def test(fn) : + # cleanup temp files made by driver + subprocess.call("rm -rf /tmp/file?", shell=True) + # hokey, but guarantees that fd=1 is not readable + st = subprocess.call("./driver -tv < %s > /tmp/.xxx" % fn, shell=True) + st = subprocess.call("egrep -q 'returned [^-]' /tmp/.xxx", shell=True) + return st == 0 + +if __name__ == '__main__' : + read = 3 + write = 4 + open = 5 + writev = 20 + execve = 0xb + buf = Alloc(1024) + l = Len() + fd = File('HELLO WORLD!\n') + + ex1 = (write, 1, 'hello World!\n', l) + writeFn('inputs/ex1', mkSyscalls(ex1)) + + ex2 = (read, fd, buf, l) + writeFn('inputs/ex2', mkSyscalls(ex2)) + + writeFn('inputs/ex3', mkSyscalls(ex1, ex2)) + writeFn('inputs/ex4', mkSyscalls((read, StdFile(1), buf, l))) + + iov = Vec64('test\n', l, 'vec\n', l) + writeFn('inputs/ex5', mkSyscalls((writev, 1, iov, 2))) + + writeFn('inputs/ex6', mkSyscalls((open, Filename("testing"), 0), (read, 3, buf, l))) + + writeFn('inputs/ex7', mkSyscalls((execve, Filename("#!/bin/sh\necho hi $FOO $@!\n"), Vec64("prog\0", "test\0", "this\0", 0), Vec64("FOO=bar\0", 0)))) + diff --git a/arm/parse.c b/arm/parse.c new file mode 100644 index 00000000..1374521a --- /dev/null +++ b/arm/parse.c @@ -0,0 +1,93 @@ +/* + * parsing primitives. + */ + +#define _GNU_SOURCE +#include +#include "drv.h" + +void mkSlice(struct slice *b, void *base, size_t sz) +{ + b->cur = (u_int8_t *)base; + b->end = b->cur + sz; +} + +unsigned char *sliceBuf(struct slice *b) +{ + return b->cur; +} + +size_t sliceSize(struct slice *b) +{ + return b->end - b->cur; +} + +int getEOF(struct slice *b) +{ + if(b->cur != b->end) + return -1; + return 0; +} + +int getU8(struct slice *b, u_int8_t *x) +{ + if(b->cur >= b->end) return -1; + *x = *b->cur; + b->cur++; + return 0; +} + +int getU16(struct slice *b, u_int16_t *x) +{ + u_int8_t h, l; + if(getU8(b, &h) == -1 + || getU8(b, &l) == -1) + return -1; + *x = (h << 8) | l; + return 0; +} + +int getU32(struct slice *b, u_int32_t *x) +{ + u_int16_t h, l; + if(getU16(b, &h) == -1 + || getU16(b, &l) == -1) + return -1; + *x = (h << 16) | l; + return 0; +} + +// int getU64(struct slice *b, u_int64_t *x) +// { +// u_int32_t h, l; +// if(getU32(b, &h) == -1 +// || getU32(b, &l) == -1) +// return -1; +// *x = ((u_int64_t)h << 32) | l; +// return 0; +// } + + +/* split a slice up into up to max slices */ +int getDelimSlices(struct slice *b, char *delim, int delsz, size_t max, struct slice *x, size_t *nx) +{ + unsigned char *ep; + size_t i; + + for(i = 0; i < max && b->cur != b->end; i++) { + ep = memmem(b->cur, b->end - b->cur, delim, delsz); + x[i].cur = b->cur; + if(ep) { + b->cur = ep + delsz; + } else { + b->cur = b->end; + ep = b->end; + } + x[i].end = ep; + } + + if(b->cur != b->end) + return -1; + *nx = i; + return 0; +} diff --git a/arm/sysc.c b/arm/sysc.c new file mode 100644 index 00000000..ad468692 --- /dev/null +++ b/arm/sysc.c @@ -0,0 +1,388 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drv.h" +#include "sysc.h" + +extern int verbose; + +/* internal syscall arg parsing state */ +#define NSLICES 7 +#define STKSZ 256 +struct parseState { + struct sysRec *calls; + int ncalls; + struct slice slices[NSLICES]; + u_int32_t sizeStk[STKSZ]; + size_t nslices, bufpos, stkpos; +}; + +static int parseArg(struct slice *b, struct parseState *st, u_int32_t *x); + +static int pushSize(struct parseState *st, u_int32_t sz) +{ + if(st->stkpos >= STKSZ) + return -1; + size_t stkpos = st->stkpos++; + st->sizeStk[stkpos] = sz; + return 0; +} + +static int popSize(struct parseState *st, u_int32_t *sz) +{ + if(st->stkpos == 0) + return -1; + size_t stkpos = --st->stkpos; + *sz = st->sizeStk[stkpos]; + return 0; +} + +static void dumpContents(unsigned char *buf, size_t sz) +{ + size_t i; + + if(verbose > 1) { + printf("contents: "); + for(i = 0; i < sz; i++) + printf("%02x", buf[i]); + printf("\n"); + } +} + +static int parseArgNum(struct slice *b, struct parseState *st, u_int32_t *x) +{ + if(getU32(b, x) == -1) + return -1; + if(verbose) printf("argNum %lx\n", (unsigned long)*x); + return 0; +} + +/* pass a buffer as an argument */ +static int parseArgAlloc(struct slice *b, struct parseState *st, u_int32_t *x) +{ + void *p; + u_int32_t sz; + + if(getU32(b, &sz) == -1) + return -1; + p = malloc(sz); /* note: we ignore memory leaks - exit/doneWork are perfect GCs */ + if(!p + || pushSize(st, sz) == -1) + return -1; + memset(p, 0, sz); + *x = (u_int32_t)(u_long)p; + if(verbose) printf("argAlloc %lx - allocated %x bytes\n", (unsigned long)*x, sz); + return 0; +} + +/* pass a buffer as an argument */ +static int parseArgBuf(struct slice *b, struct parseState *st, u_int32_t *x) +{ + if(st->bufpos >= st->nslices) + return -1; + size_t pos = st->bufpos++; + struct slice *bslice = st->slices + pos; + size_t sz = sliceSize(bslice); + if(pushSize(st, sz) == -1) + return -1; + *x = (u_int32_t)(u_long)sliceBuf(bslice); + + if(verbose) printf("argBuf %lx from %d bytes\n", (unsigned long)*x, sz); + dumpContents(sliceBuf(bslice), sz); + return 0; +} + +/* pass a buffer length as an argument */ +static int parseArgBuflen(struct slice *b, struct parseState *st, u_int32_t *x) +{ + if(popSize(st, x) == -1) + return -1; + if(verbose) printf("argBuflen %lx\n", (unsigned long)*x); + return 0; +} + +/* make a file with buffer contents and set arg to fd open to the start of that file */ +static int parseArgFile(struct slice *b, struct parseState *st, u_int32_t *x) +{ + static int num = 0; + char namebuf[128]; + int fd; + + if(st->bufpos >= st->nslices) + return -1; + size_t pos = st->bufpos++; + struct slice *bslice = st->slices + pos; + + snprintf(namebuf, sizeof namebuf - 1, "/tmp/file%d", num++); + fd = open(namebuf, O_RDWR | O_CREAT | O_TRUNC, 0777); + if(fd == -1 + || write(fd, sliceBuf(bslice), sliceSize(bslice)) == -1 + || lseek(fd, 0, SEEK_SET) == -1) { + perror(namebuf); + exit(1); + } + fchmod(fd, 0777); // just in case it previously existed with other mode + *x = fd; + if(verbose) printf("argFile %lx - %ld bytes from %s\n", (unsigned long)*x, (u_long)sliceSize(bslice), namebuf); + dumpContents(sliceBuf(bslice), sliceSize(bslice)); + return 0; +} + +static int parseArgStdFile(struct slice *b, struct parseState *st, u_int32_t *x) +{ + int fd; + unsigned short typ; + + if(getU16(b, &typ) == -1) + return -1; + fd = getStdFile(typ); + if(fd == -1) + return -1; + *x = fd; + if(verbose) printf("argStdFile %lx - type %d\n", (unsigned long)*x, typ); + return 0; +} + +static int parseArgVec64(struct slice *b, struct parseState *st, u_int32_t *x) +{ + u_int32_t *vec; + int i; + u_int8_t sz; + + if(getU8(b, &sz) == -1) + return -1; + vec = malloc(sz * sizeof vec[0]); /* note: we ignore memory leaks - exit/doneWork are perfect GCs */ + if(sz && !vec) + return -1; + if(verbose) printf("argVec32 %lx - size %d\n", (unsigned long)(u_long)vec, sz); + for(i = 0; i < sz; i++) { + if(verbose) printf("vec %d: ", i); + if(parseArg(b, st, &vec[i]) == -1) + return -1; + } + if(pushSize(st, sz) == -1) + return -1; + *x = (u_int32_t)(u_long)vec; + return 0; +} + +/* make a file with buffer contents and set arg to point to its filename */ +static int parseArgFilename(struct slice *b, struct parseState *st, u_int32_t *x) +{ + static int num = 0; + char namebuf[128]; + int fd; + + if(st->bufpos >= st->nslices) + return -1; + size_t pos = st->bufpos++; + struct slice *bslice = st->slices + pos; + + snprintf(namebuf, sizeof namebuf - 1, "/tmp/file%d", num++); + fd = open(namebuf, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if(fd == -1 + || write(fd, sliceBuf(bslice), sliceSize(bslice)) == -1 + || close(fd) == -1) { + perror(namebuf); + exit(1); + } + *x = (u_int32_t)(u_long)strdup(namebuf); /* note: we ignore memory leaks - exit/doneWork are perfect GCs */ + if(verbose) printf("argFilename %lx - %ld bytes from %s\n", (unsigned long)*x, (u_long)sliceSize(bslice), namebuf); + dumpContents(sliceBuf(bslice), sliceSize(bslice)); + return 0; +} + +static int +mkChild(u_int32_t *retPid) +{ + pid_t pid; + int i; + + fflush(stdout); + pid = fork(); + switch(pid) { + case -1: return -1; + case 0: + break; + default: + *retPid = pid; + return 0; + } + + /* child process */ + for(i = 0; i < 3; i++) + sleep(1); + exit(0); +} + +/* use a pid related to our process as an arg */ +static int parseArgPid(struct slice *b, struct parseState *st, u_int32_t *x) +{ + unsigned char typ; + + if(getU8(b, &typ) == -1) + return -1; + switch(typ) { + case 0: // my pid + *x = getpid(); + break; + case 1: // parent pid + *x = getppid(); + break; + case 2: // child pid + if(mkChild(x) == -1) + return -1; + break; + default: + return -1; + } + if(verbose) printf("argPid %lx - %d\n", (unsigned long)*x, typ); + return 0; +} + +/* Reference a previously defined argument as a new arg */ +static int parseArgRef(struct slice *b, struct parseState *st, u_int32_t *x) +{ + unsigned char ncall, narg; + + if(getU8(b, &ncall) == -1 + || getU8(b, &narg) == -1 + || ncall >= st->ncalls + || narg >= 6) + return -1; + *x = st->calls[ncall].args[narg]; + if(verbose) printf("argRef %lx - %d %d\n", (unsigned long)*x, ncall, narg); + return 0; +} + +static int parseArgVec32(struct slice *b, struct parseState *st, u_int32_t *x) +{ + u_int32_t elem; + u_int32_t *vec; + int i; + u_int8_t sz; + + if(getU8(b, &sz) == -1) + return -1; + vec = malloc(sz * sizeof vec[0]); /* note: we ignore memory leaks - exit/doneWork are perfect GCs */ + if(sz && !vec) + return -1; + if(verbose) printf("argVec32 %lx - size %d\n", (unsigned long)(u_long)vec, sz); + for(i = 0; i < sz; i++) { + if(verbose) printf("vec %d: ", i); + if(parseArg(b, st, &elem) == -1) + return -1; + vec[i] = elem; + } + if(pushSize(st, sz) == -1) + return -1; + *x = (u_int32_t)(u_long)vec; + return 0; +} + +static int parseArg(struct slice *b, struct parseState *st, u_int32_t *x) +{ + unsigned char typ; + + if(getU8(b, &typ) == -1) + return -1; + switch(typ) { + case 0: return parseArgNum(b, st, x); + case 1: return parseArgAlloc(b, st, x); + case 2: return parseArgBuf(b, st, x); + case 3: return parseArgBuflen(b, st, x); + case 4: return parseArgFile(b, st, x); + case 5: return parseArgStdFile(b, st, x); + case 7: return parseArgVec64(b, st, x); + case 8: return parseArgFilename(b, st, x); + case 9: return parseArgPid(b, st, x); + case 10: return parseArgRef(b, st, x); + case 11: return parseArgVec32(b, st, x); + default: return -1; + } +} + +int parseSysRec(struct sysRec *calls, int ncalls, struct slice *b, struct sysRec *x) +{ + struct parseState st; + int i; + + /* chop input into several slices */ + if(getDelimSlices(b, BUFDELIM, sizeof BUFDELIM-1, NSLICES, st.slices, &st.nslices) == -1 + || st.nslices < 1) + return -1; + + b = &st.slices[0]; + st.bufpos = 1; + st.stkpos = 0; + st.calls = calls; + st.ncalls = ncalls; + if(getU16(b, &x->nr) == -1) + return -1; + if(verbose) printf("call %d\n", x->nr); + for(i = 0; i < 6; i++) { + if(verbose) printf("arg %d: ", i); + if(parseArg(b, &st, &x->args[i]) == -1) + return -1; + } + return 0; +} + +int parseSysRecArr(struct slice *b, int maxRecs, struct sysRec *x, int *nRecs) +{ + struct slice slices[10]; + size_t i, nslices; + + if(maxRecs > 10) + maxRecs = 10; + if(getDelimSlices(b, CALLDELIM, sizeof CALLDELIM-1, maxRecs, slices, &nslices) == -1) + return -1; + + for(i = 0; i < nslices; i++) { + if(parseSysRec(x, i, slices + i, x + i) == -1) + return -1; + } + *nRecs = nslices; + return 0; +} + +void +showSysRec(struct sysRec *x) +{ + printf("syscall %d (%lx, %lx, %lx, %lx, %lx, %lx)\n", x->nr, (u_long)x->args[0], (u_long)x->args[1], (u_long)x->args[2], (u_long)x->args[3], (u_long)x->args[4], (u_long)x->args[5]); +} + +void +showSysRecArr(struct sysRec *x, int n) +{ + int i; + + for(i = 0; i < n; i++) + showSysRec(x + i); +} + +unsigned long +doSysRec(struct sysRec *x) +{ + /* XXX consider doing this in asm so we can use the real syscall entry instead of the syscall() function entry */ + return syscall(x->nr, x->args[0], x->args[1], x->args[2], x->args[3], x->args[4], x->args[5]); +} + +unsigned long +doSysRecArr(struct sysRec *x, int n) +{ + unsigned long ret; + int i; + + ret = 0; + for(i = 0; i < n; i++) + ret = doSysRec(x + i); + return ret; +} diff --git a/arm/sysc.h b/arm/sysc.h new file mode 100644 index 00000000..91c3c8bd --- /dev/null +++ b/arm/sysc.h @@ -0,0 +1,18 @@ + +#define BUFDELIM "\xa5\xc9" +#define CALLDELIM "\xb7\xe3" + +struct sysRec { + u_int16_t nr; + u_int32_t args[6]; +}; + +int parseSysRec(struct sysRec *calls, int ncalls, struct slice *b, struct sysRec *x); +int parseSysRecArr(struct slice *b, int maxRecs, struct sysRec *x, int *nRecs); +void showSysRec(struct sysRec *x); +void showSysRecArr(struct sysRec *x, int n); +unsigned long doSysRec(struct sysRec *x); +unsigned long doSysRecArr(struct sysRec *x, int n); + +int getStdFile(int typ); +