From 5c7a2419c421ee0e48a8a9504d9cdd24ae02fcc1 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 00:40:51 +0200 Subject: [PATCH 01/20] guest/linux/libcgc: decree syscall emulation for linux Signed-off-by: Vitaly Chipounov --- guest/linux/CMakeLists.txt | 4 ++ guest/linux/include/libcgc.h | 39 +++++++++++++ guest/linux/libcgc/CMakeLists.txt | 30 ++++++++++ guest/linux/libcgc/main.c | 95 +++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 guest/linux/include/libcgc.h create mode 100644 guest/linux/libcgc/CMakeLists.txt create mode 100644 guest/linux/libcgc/main.c diff --git a/guest/linux/CMakeLists.txt b/guest/linux/CMakeLists.txt index a2b34ab30..a092dc641 100644 --- a/guest/linux/CMakeLists.txt +++ b/guest/linux/CMakeLists.txt @@ -31,4 +31,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") add_subdirectory(cgccmd) add_subdirectory(s2e.so) +if(${BITS} EQUAL 32) +add_subdirectory(libcgc) +endif() + install(DIRECTORY scripts/ DESTINATION .) diff --git a/guest/linux/include/libcgc.h b/guest/linux/include/libcgc.h new file mode 100644 index 000000000..0b64f069d --- /dev/null +++ b/guest/linux/include/libcgc.h @@ -0,0 +1,39 @@ +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#ifndef LIBCGC_H + +#define LIBCGC_H + +#include +#include + +int transmit(int fd, const void *buf, size_t count, size_t *tx_bytes); +int receive(int fd, void *buf, size_t count, size_t *rx_bytes); +int fdwait(int nfds, fd_set *readfds, fd_set *writefds, const struct timeval *timeout, int *readyfds); +int allocate(size_t length, int is_X, void **addr); +int deallocate(void *addr, size_t length); + +// TODO: fix conflict with stdlib. +// int random(void *buf, size_t count, size_t *rnd_bytes); + +#endif \ No newline at end of file diff --git a/guest/linux/libcgc/CMakeLists.txt b/guest/linux/libcgc/CMakeLists.txt new file mode 100644 index 000000000..95e29dcbf --- /dev/null +++ b/guest/linux/libcgc/CMakeLists.txt @@ -0,0 +1,30 @@ +# S2E Selective Symbolic Execution Platform +# +# Copyright (c) 2024 Vitaly Chipounov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +add_library(cgc STATIC + main.c +) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE") + +install(TARGETS cgc LIBRARY DESTINATION .) diff --git a/guest/linux/libcgc/main.c b/guest/linux/libcgc/main.c new file mode 100644 index 000000000..33fe9704b --- /dev/null +++ b/guest/linux/libcgc/main.c @@ -0,0 +1,95 @@ +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#include + +#include +#include +#include +#include +#include + +int transmit(int fd, const void *buf, size_t count, size_t *tx_bytes) { + int ret = 0; + + ssize_t sz = write(fd, buf, count); + if (sz >= 0) { + if (tx_bytes) { + *tx_bytes = sz; + } + ret = 0; + goto out; + } + + ret = (int) sz; + +out: + return ret; +} + +int receive(int fd, void *buf, size_t count, size_t *rx_bytes) { + int ret = 0; + ssize_t sz = read(fd, buf, count); + if (sz >= 0) { + *rx_bytes = sz; + goto out; + } + ret = (int) sz; +out: + return ret; +} + +int fdwait(int nfds, fd_set *readfds, fd_set *writefds, const struct timeval *timeout, int *readyfds) { + struct timeval tm = *timeout; + int ret = select(nfds, readfds, writefds, NULL, &tm); + if (ret >= 0) { + *readyfds = ret; + ret = 0; + } + + return ret; +} + +int allocate(size_t length, int is_X, void **addr) { + int prot = PROT_READ | PROT_WRITE; + if (is_X) { + prot |= PROT_EXEC; + } + void *ret_addr = mmap(NULL, length, prot, MAP_ANON | MAP_PRIVATE, 0, 0); + if (ret_addr == MAP_FAILED) { + return -EFAULT; + } else { + *addr = ret_addr; + return 0; + } +} + +int deallocate(void *addr, size_t length) { + return munmap(addr, length); +} + +void delay(unsigned int msec) { + struct timeval timeout; + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; + fdwait(0, NULL, NULL, &timeout, NULL); +} From 5d05ce7ddacfd8cb5cbd24e3f727c423979812be Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 00:41:37 +0200 Subject: [PATCH 02/20] guest/linux/libpov: added pov negotiation library Signed-off-by: Vitaly Chipounov --- guest/linux/CMakeLists.txt | 1 + guest/linux/include/libpov.h | 109 +++++++++++++ guest/linux/libpov/CMakeLists.txt | 30 ++++ guest/linux/libpov/main.c | 246 ++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 guest/linux/include/libpov.h create mode 100644 guest/linux/libpov/CMakeLists.txt create mode 100644 guest/linux/libpov/main.c diff --git a/guest/linux/CMakeLists.txt b/guest/linux/CMakeLists.txt index a092dc641..797a3fe97 100644 --- a/guest/linux/CMakeLists.txt +++ b/guest/linux/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory(cgccmd) add_subdirectory(s2e.so) if(${BITS} EQUAL 32) +add_subdirectory(libpov) add_subdirectory(libcgc) endif() diff --git a/guest/linux/include/libpov.h b/guest/linux/include/libpov.h new file mode 100644 index 000000000..5463693f4 --- /dev/null +++ b/guest/linux/include/libpov.h @@ -0,0 +1,109 @@ +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +/// Adapted from https://github.com/CyberGrandChallenge/libpov +/// https://github.com/CyberGrandChallenge/libpov/blob/22648e2489145dfa431b0674a03959489a5c0fac/debian/copyright +/// Copyright: Under 17 U.S.C § 105 US Government Works are not subject to domestic copyright protection. +/// License: None + +#ifndef LIBPOV_H + +#define LIBPOV_H + +#include +#include + +#define NEG_FD 3 + +typedef struct type_request { + uint32_t povType; + struct { + uint32_t ipmask; + uint32_t regmask; + uint32_t regnum; + } type1; +} type_request; + +/* + * The following functions are available to POV authors to support + * POV type negotiations. + */ + +typedef struct type1_vals_ { + unsigned int ipval; + unsigned int regval; +} type1_vals; + +typedef struct type2_vals_ { + unsigned int region_addr; + unsigned int region_size; + unsigned int read_size; +} type2_vals; + +/* + * Negotiate a type 1 pov. Caller specifies an ip bit mask, a register bit mask + * and a general purpose register number (see the list below). + * + 0 - eax + 1 - ecx + 2 - edx + 3 - ebx + 4 - esp + 5 - ebp + 6 - esi + 7 - edi + * + * Returns 0 on success. On success, the t1vals structure holds required IP + * and register values that must be found when the target CB crashes. At the + * time of the crash the following must hold: + * (crash_eip & ipmask) == t1vals->ipval + * (crash_REG & regmask) == t1vals->regval + */ +int type1_negotiate(unsigned int ipmask, unsigned int regmask, unsigned int regnum, type1_vals *t1vals); + +/* + * Negotiate a type 2 pov. + * Returns 0 on success. On success, the t2vals structure holds the address + * (t2vals->region_addr) and size of a memory region (t2vals->region_size) + * from which the POV must leak a specific number of bytes (t2vals->read_size). + */ +int type2_negotiate(type2_vals *t2vals); + +/* + * Submit the len bytes in the val buffer as the results of a type 2 POV + * Returns 0 on success + */ +int type2_submit(const unsigned char *val, size_t len); + +int length_read(int fd, unsigned char *buf, unsigned int len); +int transmit_all(int fd, const void *buf, const size_t size); +void receive_all(int fd, void *buf, size_t count); +void receive_null(int fd, size_t count); +int buffered_receive(int fd, void *buf, size_t count, size_t *rx_bytes); +void delay(unsigned int msec); + +#define GET_BYTE(v, i) ((((uint32_t) (v)) >> ((i) *8)) & 0xFF) + +/// @brief Read the negotiation parameters from the POV binary. +int type_negotiate(int fd, type_request *req); + +#endif \ No newline at end of file diff --git a/guest/linux/libpov/CMakeLists.txt b/guest/linux/libpov/CMakeLists.txt new file mode 100644 index 000000000..5b8b55694 --- /dev/null +++ b/guest/linux/libpov/CMakeLists.txt @@ -0,0 +1,30 @@ +# S2E Selective Symbolic Execution Platform +# +# Copyright (c) 2024 Vitaly Chipounov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +add_library(pov STATIC + main.c +) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE") + +install(TARGETS pov LIBRARY DESTINATION .) diff --git a/guest/linux/libpov/main.c b/guest/linux/libpov/main.c new file mode 100644 index 000000000..169bcc187 --- /dev/null +++ b/guest/linux/libpov/main.c @@ -0,0 +1,246 @@ +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +/// Adapted from https://github.com/CyberGrandChallenge/libpov +/// https://github.com/CyberGrandChallenge/libpov/blob/22648e2489145dfa431b0674a03959489a5c0fac/debian/copyright +/// Copyright: Under 17 U.S.C § 105 US Government Works are not subject to domestic copyright protection. +/// License: None + +#include +#include +#include +#include + +#include +#include + +int type_negotiate(int fd, type_request *req) { + int res = read(fd, &req->povType, sizeof(req->povType)); + if (res != sizeof(req->povType)) { + return -1; + } + + if (req->povType == 2) { + return 0; + } + + if (req->povType == 1) { + res = read(fd, &req->type1.ipmask, sizeof(req->type1.ipmask)); + if (res != sizeof(req->type1.ipmask)) { + return -1; + } + + res = read(fd, &req->type1.regmask, sizeof(req->type1.regmask)); + if (res != sizeof(req->type1.regmask)) { + return -1; + } + + res = read(fd, &req->type1.regnum, sizeof(req->type1.regnum)); + if (res != sizeof(req->type1.regnum)) { + return -1; + } + } + + return 0; +} + +/* + * Negotiate a type 1 pov. Caller specifies an ip bit mask, a register bit mask + * and a general purpose register number (see the list below). + * + 0 - eax + 1 - ecx + 2 - edx + 3 - ebx + 4 - esp + 5 - ebp + 6 - esi + 7 - edi + * + * Returns 0 on success. On success, the t1vals structure holds required IP + * and register values that must be found when the target CB crashes. At the + * time of the crash the following must hold: + * (crash_eip & ipmask) == t1vals->ipval + * (crash_REG & regmask) == t1vals->regval + */ +int type1_negotiate(unsigned int ipmask, unsigned int regmask, unsigned int regnum, type1_vals *t1vals) { + uint32_t povType = 1; + if (transmit_all(NEG_FD, &povType, sizeof(povType)) || transmit_all(NEG_FD, &ipmask, sizeof(ipmask)) || + transmit_all(NEG_FD, ®mask, sizeof(regmask)) || transmit_all(NEG_FD, ®num, sizeof(regnum))) { + return -1; + } + if (length_read(NEG_FD, (unsigned char *) t1vals, sizeof(type1_vals)) != sizeof(type1_vals)) { + return -1; + } + return 0; +} + +/* + * Negotiate a type 2 pov. + * Returns 0 on success. On success, the t2vals structure holds the address + * (t2vals->region_addr) and size of a memory region (t2vals->region_size) + * from which the POV must leak a specific number of bytes (t2vals->read_size). + */ +int type2_negotiate(type2_vals *t2vals) { + uint32_t povType = 2; + if (transmit_all(NEG_FD, &povType, sizeof(povType))) { + return -1; + } + if (length_read(NEG_FD, (unsigned char *) t2vals, sizeof(type2_vals)) != sizeof(type2_vals)) { + return -1; + } + return 0; +} + +/* + * Submit the len bytes in the val buffer as the results of a type 2 POV + * Returns 0 on success + */ +int type2_submit(const unsigned char *val, size_t len) { + return transmit_all(NEG_FD, val, len); +} + +int length_read(int fd, unsigned char *buf, unsigned int len) { + unsigned int total = 0; + while (total < len) { + unsigned int need = len - total; + size_t rlen; + if (buf != NULL) { + // read directly into caller buffer + if (buffered_receive(fd, buf + total, need, &rlen) != 0 || rlen == 0) { + // error or eof but might have had some data + break; + } + } else { + // caller supplied no buffer so just read len bytes + // and discard + unsigned char dbuf[512]; + if (need > sizeof(dbuf)) { + need = sizeof(dbuf); + } + if (buffered_receive(fd, dbuf, need, &rlen) != 0 || rlen == 0) { + // error or eof but might have had some data + break; + } + } + total += rlen; + } + return (int) total; +} + +int transmit_all(int fd, const void *buf, const size_t size) { + size_t sent = 0; + size_t sent_now = 0; + int ret; + + if (!buf) + return 1; + + if (!size) + return 2; + + while (sent < size) { + ret = transmit(fd, sent + (char *) buf, size - sent, &sent_now); + if (ret != 0) { + return 3; + } + sent += sent_now; + } + + return 0; +} + +void receive_all(int fd, void *buf, size_t count) { + size_t total = 0; + while (total < count) { + size_t s = 0; + receive(fd, buf + total, count - total, &s); + total += s; + } +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +void receive_null(int fd, size_t count) { + uint8_t buf[256]; + while (count) { + size_t s = MIN(count, sizeof(buf)); + receive_all(fd, buf, s); + count -= s; + } +} + +typedef struct _read_buffer { + uint32_t iptr; + uint32_t eptr; + uint8_t buf[4096]; +} read_buffer; + +static read_buffer *ibufs[16]; + +int buffered_receive(int fd, void *buf, size_t count, size_t *rx_bytes) { + if (fd > 15) { + return receive(fd, buf, count, rx_bytes); + } + read_buffer *rb = ibufs[fd]; + if (rb == NULL) { + rb = (read_buffer *) malloc(sizeof(read_buffer)); + rb->iptr = rb->eptr = 0; + ibufs[fd] = rb; + } + + if (rx_bytes != NULL) { + *rx_bytes = 0; + } + + int res = 0; + while (1) { + uint32_t avail = rb->eptr - rb->iptr; + if (avail > 0) { + if (avail >= count) { + // we have enough data buffered to satisfy request + memcpy(buf, rb->buf + rb->iptr, count); + rb->iptr += count; + if (rx_bytes != NULL) { + *rx_bytes += count; + } + return 0; + } else { + // avail < len some data buffered but not enough + memcpy(buf, rb->buf + rb->iptr, avail); + buf = avail + (char *) buf; + count -= avail; + if (rx_bytes != NULL) { + *rx_bytes += avail; + } + } + } + size_t rxb; + rb->iptr = rb->eptr = 0; + res = receive(fd, rb->buf, sizeof(rb->buf), &rxb); + if (res != 0 || rxb == 0) { + break; + } + rb->eptr = rxb; + } + return res; +} From 241326bd580ea681e10da18cfa3d602eef2d8a01 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 27 Apr 2024 22:23:27 +0200 Subject: [PATCH 03/20] guest/linux/cgcload: added tool to run cgc binaries on linux Signed-off-by: Vitaly Chipounov --- guest/linux/CMakeLists.txt | 1 + guest/linux/cgcload/CMakeLists.txt | 31 +++ guest/linux/cgcload/cgc.h | 84 +++++++ guest/linux/cgcload/launcher.asm | 46 ++++ guest/linux/cgcload/linker.ld | 256 +++++++++++++++++++ guest/linux/cgcload/loader.c | 313 +++++++++++++++++++++++ guest/linux/cgcload/loader.h | 82 ++++++ guest/linux/cgcload/main.c | 387 +++++++++++++++++++++++++++++ 8 files changed, 1200 insertions(+) create mode 100644 guest/linux/cgcload/CMakeLists.txt create mode 100644 guest/linux/cgcload/cgc.h create mode 100644 guest/linux/cgcload/launcher.asm create mode 100644 guest/linux/cgcload/linker.ld create mode 100644 guest/linux/cgcload/loader.c create mode 100644 guest/linux/cgcload/loader.h create mode 100644 guest/linux/cgcload/main.c diff --git a/guest/linux/CMakeLists.txt b/guest/linux/CMakeLists.txt index 797a3fe97..5b42f80ee 100644 --- a/guest/linux/CMakeLists.txt +++ b/guest/linux/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(s2e.so) if(${BITS} EQUAL 32) add_subdirectory(libpov) add_subdirectory(libcgc) +add_subdirectory(cgcload) endif() install(DIRECTORY scripts/ DESTINATION .) diff --git a/guest/linux/cgcload/CMakeLists.txt b/guest/linux/cgcload/CMakeLists.txt new file mode 100644 index 000000000..9645e3a7c --- /dev/null +++ b/guest/linux/cgcload/CMakeLists.txt @@ -0,0 +1,31 @@ +# S2E Selective Symbolic Execution Platform +# +# Copyright (c) 2024 Vitaly Chipounov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") + +set(CMAKE_ASM_NASM_OBJECT_FORMAT elf32) +enable_language(ASM_NASM) + +add_executable(cgcload main.c loader.c launcher.asm) +target_link_options(cgcload PUBLIC -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) + +install(TARGETS cgcload RUNTIME DESTINATION .) \ No newline at end of file diff --git a/guest/linux/cgcload/cgc.h b/guest/linux/cgcload/cgc.h new file mode 100644 index 000000000..73be2f018 --- /dev/null +++ b/guest/linux/cgcload/cgc.h @@ -0,0 +1,84 @@ +/* + * linux/fs/binfmt_cgc.c + * Copyright (c) 2014 Jason L. Wright (jason@thought.net) + * + * Functions/module to load binaries targetting the DARPA Cyber Grand Challenge. + * CGCOS binaries most thoroughly resemble static ELF binaries, thus this + * code is derived from: + * + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ + +#ifndef __CGC_HDR__ + +#define __CGC_HDR__ + +#include + +/* The CGC Executable File Header */ +#define CI_NIDENT 16 +typedef struct CGC32_hdr { + uint8_t ci_mag0; /* 0x7f */ + uint8_t ci_mag1; /* 'C' */ + uint8_t ci_mag2; /* 'G' */ + uint8_t ci_mag3; /* 'C' */ + uint8_t ci_class; /* 1 */ + uint8_t ci_data; /* 1 */ + uint8_t ci_version; /* 1 */ + uint8_t ci_osabi; /* 'C' */ + uint8_t ci_abivers; /* 1 */ + uint8_t ci_pad[7]; + uint16_t c_type; /* Must be 2 for executable */ + uint16_t c_machine; /* Must be 3 for i386 */ + uint32_t c_version; /* Must be 1 */ + uint32_t c_entry; /* Entry point */ + uint32_t c_phoff; /* Program Header offset */ + uint32_t c_shoff; /* Section Header offset */ + uint32_t c_flags; /* Must be 0 */ + uint16_t c_ehsize; /* CGC header's size */ + uint16_t c_phentsize; /* Program header entry size */ + uint16_t c_phnum; /* # program header entries */ + uint16_t c_shentsize; /* Section header entry size */ + uint16_t c_shnum; /* # section header entries */ + uint16_t c_shstrndx; /* sect header # of str table */ +} CGC32_hdr; + +/* The CGC Executable Program Header */ +typedef struct CGC32_phdr { + uint32_t p_type; /* Section type */ +#define PT_NULL 0 /* Unused header */ +#define PT_LOAD 1 /* Segment is loaded into mem */ +#define PT_PHDR 6 /* Program header tbl itself */ +#define PT_CGCPOV2 0x6ccccccc /* CFE Type 2 PoV flag sect */ + uint32_t p_offset; /* Offset into the file */ + uint32_t p_vaddr; /* Virtial program address */ + uint32_t p_paddr; /* Set to zero */ + uint32_t p_filesz; /* Section bytes in the file */ + uint32_t p_memsz; /* Section bytes in memory */ + uint32_t p_flags; /* section flags */ +#define CPF_X (1 << 0) /* Mapped executable */ +#define CPF_W (1 << 1) /* Mapped writeable */ +#define CPF_R (1 << 2) /* Mapped readable */ + /* Acceptable flag combinations are: + * CPF_R + * CPF_R|CPF_W + * CPF_R|CPF_X + * CPF_R|CPF_W|CPF_X + */ + + uint32_t p_align; /* Bytes at which to align the + * section in memory. + * 0 or 1: no alignment + * 4: 32bit alignment + * 4096: page alignment + */ +} CGC32_Phdr; + +#endif \ No newline at end of file diff --git a/guest/linux/cgcload/launcher.asm b/guest/linux/cgcload/launcher.asm new file mode 100644 index 000000000..f297dc4ee --- /dev/null +++ b/guest/linux/cgcload/launcher.asm @@ -0,0 +1,46 @@ +section .text +global launch_binary ; Make the function global for linking from C. + + +; Initialize the execution environment and launch the binary. +; Set register values according to specification of the CGC ABI: +; https://github.com/CyberGrandChallenge/libcgc/blob/master/cgcabi.md +; +; Function declaration with parameters: +; void launch_binary(uintptr_t stack, uintptr_t magic, cgc_main_t entry) +launch_binary: + push ebp ; Save old base pointer + mov ebp, esp ; Set base pointer to current stack pointer + + ; Arguments are right above the saved EBP on the stack: + ; ebp + 8 -> stack + ; ebp + 12 -> magic + ; ebp + 16 -> entry + + ; Set up the stack pointer (ESP) + mov eax, [ebp + 8] ; Load 'stack' argument into eax + sub eax, 4 ; Adjust 'stack' by subtracting 4 + mov esp, eax ; Set 'stack' as the new stack pointer + + ; Set up other registers according to the specification + mov ecx, [ebp + 12] ; Load 'magic' into ECX + mov eax, [ebp + 16] ; Load 'entry' function pointer into EAX + + xor ebx, ebx ; Zero out EBX + xor edx, edx ; Zero out EDX + xor edi, edi ; Zero out EDI + xor esi, esi ; Zero out ESI + xor ebp, ebp ; Zero out EBP + + ; Set up flags register with specific flags (bit 9 - IF, Interrupt Enable Flag) + push dword 0x202 ; Push 0x202 onto the stack + popfd ; Pop it into EFLAGS, setting specific flags + + ; Call the function pointer in EAX + call eax ; Call 'entry' + + ; Restore the previous stack frame + pop ebp ; Restore the old base pointer + ret ; Return from the function + +section .note.GNU-stack noalloc noexec nowrite diff --git a/guest/linux/cgcload/linker.ld b/guest/linux/cgcload/linker.ld new file mode 100644 index 000000000..817a1a560 --- /dev/null +++ b/guest/linux/cgcload/linker.ld @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2013 Dependable Systems Laboratory, EPFL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386:i386") +ENTRY(_start) + +/* Script for -z combreloc -z separate-code */ +/* Copyright (C) 2014-2023 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +ENTRY(_start) +/* +SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); +*/ +SECTIONS +{ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0xbf000000)); . = SEGMENT_START("text-segment", 0xbf000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + *(.rel.ifunc) + } + .rel.plt : + { + *(.rel.plt) + PROVIDE_HIDDEN (__rel_iplt_start = .); + *(.rel.iplt) + PROVIDE_HIDDEN (__rel_iplt_end = .); + } + .relr.dyn : { *(.relr.dyn) } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(SORT(.text.sorted.*)) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RO { *(.sframe) *(.sframe.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RW { *(.sframe) *(.sframe.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 12 ? 12 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} \ No newline at end of file diff --git a/guest/linux/cgcload/loader.c b/guest/linux/cgcload/loader.c new file mode 100644 index 000000000..4d7fc053c --- /dev/null +++ b/guest/linux/cgcload/loader.c @@ -0,0 +1,313 @@ +/* + * linux/fs/binfmt_cgc.c + * Copyright (c) 2014 Jason L. Wright (jason@thought.net) + * + * Functions/module to load binaries targetting the DARPA Cyber Grand Challenge. + * CGCOS binaries most thoroughly resemble static ELF binaries, thus this + * code is derived from: + * + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ + +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "cgc.h" +#include "loader.h" + +static uintptr_t cgc_map(int filep, struct CGC32_phdr *phdr, int prot, int type) { + uintptr_t addr, zaddr; + uintptr_t lo, hi; + off_t off = 0; + unsigned long size = 0; + int ret = 0; + + if (phdr->p_filesz == 0 && phdr->p_memsz == 0) { + return 0; + } + + if (phdr->p_filesz > 0) { + off = CGC_PAGESTART(phdr->p_offset); + size = CGC_PAGEALIGN(phdr->p_filesz + CGC_PAGEOFFSET(phdr->p_vaddr)); + // Map in the part of the binary corresponding to filesz. + addr = (uintptr_t) mmap((void *) (uintptr_t) CGC_PAGESTART(phdr->p_vaddr), size, prot, type, filep, off); + if ((void *) addr == MAP_FAILED) { + return addr; + } + + lo = CGC_PAGEALIGN(phdr->p_vaddr + phdr->p_filesz); + hi = CGC_PAGEALIGN(phdr->p_vaddr + phdr->p_memsz); + } else { + // For 0 filesz, we have to include the first page as bss. + lo = CGC_PAGESTART(phdr->p_vaddr + phdr->p_filesz); + hi = CGC_PAGEALIGN(phdr->p_vaddr + phdr->p_memsz); + } + + // Map anon pages for the rest (no prefault). + if ((hi - lo) > 0) { + size = hi - lo; + zaddr = (uintptr_t) mmap((void *) lo, size, prot, type | MAP_ANONYMOUS, 0, 0); + if ((void *) zaddr == MAP_FAILED) { + return zaddr; + } + } + + lo = phdr->p_vaddr + phdr->p_filesz; + hi = CGC_PAGEALIGN(phdr->p_vaddr + phdr->p_memsz); + if ((hi - lo) > 0) { + uintptr_t plo = CGC_PAGESTART(lo); + ret = mprotect((void *) plo, hi - plo, PROT_READ | PROT_WRITE); + if (ret < 0) { + exit(-1); + } + + // Clear remainder of the page to avoid garbage data from the file. + memset((void *) lo, 0, hi - lo); + + ret = mprotect((void *) plo, hi - plo, prot); + if (ret < 0) { + exit(-1); + } + } + + return addr; +} + +int load_cgcos_binary(int fd, uint32_t *entry) { + int ret = -1; + struct CGC32_hdr hdr; + struct CGC32_phdr *phdrs = NULL; + unsigned int sz; + unsigned long start_code, end_code, start_data, end_data; + unsigned long bss, brk; + + ssize_t ssz = read(fd, &hdr, sizeof(hdr)); + if (ssz != sizeof(hdr)) { + fprintf(stderr, "could not read header\n"); + goto out; + } + + if (hdr.ci_mag0 != 0x7f || hdr.ci_mag1 != 'C' || hdr.ci_mag2 != 'G' || hdr.ci_mag3 != 'C' || hdr.ci_class != 1 || + hdr.ci_data != 1 || hdr.ci_version != 1 || hdr.ci_osabi != 'C' || hdr.ci_abivers != 1 || hdr.c_type != 2 || + hdr.c_machine != 3 || hdr.c_version != 1 || hdr.c_flags != 0 || hdr.c_phentsize != sizeof(struct CGC32_phdr) || + hdr.c_phnum < 1 || hdr.c_phnum > 65536U / sizeof(struct CGC32_phdr)) { + goto out; + } + + sz = hdr.c_phnum * sizeof(struct CGC32_phdr); + phdrs = malloc(sz); + if (!phdrs) { + goto out; + } + + ssz = read(fd, phdrs, sz); + if (ssz != sz) { + goto out; + } + + bss = brk = 0; + start_code = ~0UL; + end_code = start_data = end_data = 0; + + for (int i = 0; i < hdr.c_phnum; i++) { + struct CGC32_phdr *phdr = &phdrs[i]; + + int prot, flags; + unsigned long k; + + switch (phdr->p_type) { + case PT_NULL: + case PT_LOAD: + case PT_PHDR: + case PT_CGCPOV2: + break; + default: + fprintf(stderr, "invalid phdr->p_type 0x%x\n", phdr->p_type); + ret = -ENOEXEC; + goto out; + } + + if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) { + continue; + } + + prot = 0; + if (phdr->p_flags & CPF_R) { + prot |= PROT_READ; + } else { + ret = -EINVAL; + goto out; + } + + if (phdr->p_flags & CPF_W) { + prot |= PROT_WRITE; + } + if (phdr->p_flags & CPF_X) { + prot |= PROT_EXEC; + } + + flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED; + + if (phdr->p_vaddr < start_code) { + start_code = phdr->p_vaddr; + } + if (start_data < phdr->p_vaddr) { + start_data = phdr->p_vaddr; + } + + /* + * Check to see if the section's size will overflow the + * allowed task size. Note that p_filesz must always be + * <= p_memsz so it is only necessary to check p_memsz. + */ + if (BAD_ADDR(phdr->p_vaddr) || phdr->p_filesz > phdr->p_memsz || phdr->p_memsz > TASK_SIZE || + TASK_SIZE - phdr->p_memsz < phdr->p_vaddr) { + /* set_brk can never work. avoid overflows. */ + ret = -EINVAL; + goto out; + } + + k = cgc_map(fd, phdr, prot, flags); + if (BAD_ADDR(k)) { + ret = IS_ERR((void *) k) ? PTR_ERR((void *) k) : -EINVAL; + goto out; + } + + k = phdr->p_vaddr + phdr->p_filesz; + if (k > bss) { + bss = k; + } + + if ((phdr->p_flags & CPF_X) && end_code < k) { + end_code = k; + } + + if (end_data < k) { + end_data = k; + } + + k = phdr->p_vaddr + phdr->p_memsz; + if (k > brk) { + brk = k; + } + } + + bss = brk; + + *entry = hdr.c_entry; + + ret = 0; + +out: + return ret; +} + +int init_magic_page(uintptr_t start, size_t size) { + char *magic = mmap((void *) start, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); + if (magic != (void *) CGC_MAGIC_PAGE) { + goto out; + } + + for (int i = 0; i < size; ++i) { + magic[i] = rand(); + } + + strcpy(magic, "cgc{flag}"); + + if (mprotect(magic, size, PROT_READ) < 0) { + goto out; + } + + return 0; + +out: + if (magic) { + munmap(magic, size); + } + + return -1; +} + +/// @brief CGC stack has to be RWX and at a specific location. +int init_stack(uintptr_t top, uintptr_t size) { + uintptr_t base = top - size; + void *stack; + + stack = + mmap((void *) base, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); + + if ((void *) stack == MAP_FAILED) { + return -1; + } + + if (stack != (void *) base) { + munmap((void *) base, size); + return -2; + } + + return 0; +} + +/// @brief Initializes syscall interception. This requires a recent kernel. +/// Parameters specify the exclusion range. Syscalls will not be intercepted there. +int init_intercept(sigsys_handler_t handler, char *intercept_flag, uintptr_t exclusion_start, + uintptr_t exclusion_size) { + int ret = -1; + struct sigaction new_action = {0}; + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = handler; + sigemptyset(&new_action.sa_mask); + + ret = sigaction(SIGSYS, &new_action, 0); + if (ret < 0) { + fprintf(stderr, "could not set sigaction\n"); + goto out; + } + + ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, exclusion_start, exclusion_size, intercept_flag); + if (ret < 0) { + fprintf(stderr, "could not set prctl\n"); + goto out; + } + + ret = 0; + +out: + return ret; +} diff --git a/guest/linux/cgcload/loader.h b/guest/linux/cgcload/loader.h new file mode 100644 index 000000000..496f499fa --- /dev/null +++ b/guest/linux/cgcload/loader.h @@ -0,0 +1,82 @@ +/* + * linux/fs/binfmt_cgc.c + * Copyright (c) 2014 Jason L. Wright (jason@thought.net) + * + * Functions/module to load binaries targetting the DARPA Cyber Grand Challenge. + * CGCOS binaries most thoroughly resemble static ELF binaries, thus this + * code is derived from: + * + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ + +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#ifndef __CGC_LOADER_H__ +#define __CGC_LOADER_H__ + +#include +#include +#include + +#define TASK_SIZE 0xC0000000 +#define PAGE_SIZE 0x1000 + +// The stack must be outside of the exclusion range so that +// the binary can use syscalls in its shellcode. +#define STACK_TOP 0xaaaab000 +#define STACK_SIZE 0x800000 + +#define INTERCEPT_EXCLUSION_START 0xb0000000 +#define INTERCEPT_EXCLUSION_SIZE 0x10000000 + +#define BAD_ADDR(x) ((unsigned long) (x) >= TASK_SIZE) + +#define CGC_MAGIC_PAGE 0x4347c000 +#define CGC_MIN_PAGE_SIZE 4096 +#define CGC_MIN_ALIGN CGC_MIN_PAGE_SIZE + +#define CGC_PAGESTART(_v) ((_v) & ~(CGC_MIN_ALIGN - 1)) +#define CGC_PAGEOFFSET(_v) ((_v) & (CGC_MIN_ALIGN - 1)) +#define CGC_PAGEALIGN(_v) (((_v) + CGC_MIN_ALIGN - 1) & ~(CGC_MIN_ALIGN - 1)) + +#define ERR_PTR(err) ((void *) ((long) (err))) +#define PTR_ERR(ptr) ((long) (ptr)) +#define IS_ERR(ptr) ((unsigned long) (ptr) > (unsigned long) (-1000)) + +typedef int (*cgc_main_t)(); +typedef void (*sigsys_handler_t)(int num, siginfo_t *info, void *ucontext); + +void launch_binary(uintptr_t stack, uintptr_t magic, cgc_main_t entry); +int init_intercept(sigsys_handler_t handler, char *intercept_flag, uintptr_t exclusion_start, uintptr_t exclusion_size); +int init_stack(uintptr_t top, uintptr_t size); +int init_magic_page(uintptr_t start, size_t size); +int load_cgcos_binary(int fd, uint32_t *entry); + +#endif \ No newline at end of file diff --git a/guest/linux/cgcload/main.c b/guest/linux/cgcload/main.c new file mode 100644 index 000000000..b974f8228 --- /dev/null +++ b/guest/linux/cgcload/main.c @@ -0,0 +1,387 @@ +/* + * linux/fs/binfmt_cgc.c + * Copyright (c) 2014 Jason L. Wright (jason@thought.net) + * + * Functions/module to load binaries targetting the DARPA Cyber Grand Challenge. + * CGCOS binaries most thoroughly resemble static ELF binaries, thus this + * code is derived from: + * + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ + +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "loader.h" + +#include +#include + +static char s_intercept = SYSCALL_DISPATCH_FILTER_BLOCK; +static bool s_enable_seeds = false; +static bool s_enable_symbex = false; + +enum cgc_syscall_id_t { + CGC_SYSCALL_NULL, + CGC_SYSCALL_TERMINATE, + CGC_SYSCALL_TRANSMIT, + CGC_SYSCALL_RECEIVE, + CGC_SYSCALL_FDWAIT, + CGC_SYSCALL_ALLOCATE, + CGC_SYSCALL_DEALLOCATE, + CGC_SYSCALL_RANDOM +}; + +int handle_null() { + return 0; +} + +void handle_terminate(uint32_t exit_code) { + exit(exit_code); +} + +int handle_transmit(int fd, const char *buf, size_t len, size_t *written) { + ssize_t sz = 0; + int ret = 0; + size_t orig_len = len; // remember original symbolic size + + if (s_enable_symbex) { + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_HANDLE_SYMBOLIC_TRANSMIT_BUFFER; + cmd.SymbolicBuffer.ptr_addr = (uintptr_t) buf; + cmd.SymbolicBuffer.size_addr = (uintptr_t) &len; + + __s2e_touch_buffer(buf, sizeof(*buf)); + __s2e_touch_buffer(&len, sizeof(len)); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + } + + sz = write(fd, buf, len); + if (sz >= 0) { + if (written) { + *written = sz; + } + ret = 0; + } else { + ret = (int) sz; + goto out; + } + + if (s_enable_symbex) { + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_WRITE_DATA; + cmd.WriteData.fd = fd; + cmd.WriteData.buffer = (uintptr_t) buf; + cmd.WriteData.buffer_size_addr = (uintptr_t) &sz; + cmd.WriteData.size_expr_addr = (uintptr_t) &orig_len; + + __s2e_touch_buffer(buf, sz); + __s2e_touch_buffer(&sz, sizeof(sz)); + __s2e_touch_buffer(&orig_len, sizeof(orig_len)); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + } + +out: + return ret; +} + +int handle_receive_internal(int fd, char *buf, size_t len, size_t *read_count) { + int ret = 0; + ssize_t sz = read(fd, buf, len); + if (sz >= 0) { + *read_count = sz; + goto out; + } + ret = (int) sz; +out: + return ret; +} + +int handle_receive(int fd, char *buf, size_t len, size_t *read_count) { + int ret = 0; + if (!s_enable_symbex) { + return handle_receive_internal(fd, buf, len, read_count); + } + + if (s_enable_seeds) { + ret = handle_receive_internal(fd, buf, len, read_count); + if (ret < 0) { + return ret; + } + + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_READ_DATA_POST; + cmd.DataPost.fd = fd; + cmd.DataPost.buffer = (uintptr_t) buf; + cmd.DataPost.buffer_size = *read_count; + + __s2e_touch_buffer(buf, *read_count); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + } else { + size_t orig_len = len; // remember original symbolic size + + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_HANDLE_SYMBOLIC_RECEIVE_BUFFER; + cmd.SymbolicBuffer.ptr_addr = (uintptr_t) buf; + cmd.SymbolicBuffer.size_addr = (uintptr_t) &len; + + __s2e_touch_buffer(buf, sizeof(*buf)); + __s2e_touch_buffer(&len, sizeof(len)); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + + memset(&cmd, 0, sizeof(cmd)); + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_READ_DATA; + cmd.Data.fd = fd; + cmd.Data.buffer = (uintptr_t) buf; + cmd.Data.buffer_size = len; + cmd.Data.size_expr_addr = (uintptr_t) &orig_len; + cmd.Data.result_addr = (uintptr_t) read_count; + + __s2e_touch_buffer(buf, len); + __s2e_touch_buffer(&orig_len, sizeof(orig_len)); + __s2e_touch_buffer(&ret, sizeof(ret)); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + } + + return ret; +} + +int handle_fdwait(int nfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout, int *readyfds) { + int invoke_orig = 1; + int ret = 0, res = 0; + + if (s_enable_symbex) { + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_FD_WAIT; + cmd.FDWait.has_timeout = timeout != NULL; + cmd.FDWait.tv_sec = timeout->tv_sec; + cmd.FDWait.tv_nsec = timeout->tv_usec; + cmd.FDWait.nfds = nfds; + cmd.FDWait.invoke_orig = invoke_orig; + cmd.FDWait.result = nfds; + + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + + invoke_orig = cmd.FDWait.invoke_orig; + + res = cmd.FDWait.result; + } + + if (invoke_orig) { + res = select(nfds, readfds, writefds, NULL, timeout); + if (res >= 0) { + res = 0; + *readyfds = res; + goto out; + } + } + + ret = res; +out: + return ret; +} + +int handle_allocate(size_t len, int exec, void **addr) { + int prot = PROT_READ | PROT_WRITE; + if (exec) { + prot |= PROT_EXEC; + } + void *ret_addr = mmap(NULL, len, prot, MAP_ANON | MAP_PRIVATE, 0, 0); + if (ret_addr == MAP_FAILED) { + return -EFAULT; + } else { + *addr = ret_addr; + return 0; + } +} + +int handle_deallocate(void *ptr, size_t len) { + return munmap(ptr, len); +} + +int handle_random(char *buffer, size_t count, size_t *rnd_out) { + for (size_t i = 0; i < count; ++i) { + buffer[i] = (char) random(); + } + *rnd_out = count; + + if (s_enable_symbex) { + struct S2E_DECREEMON_COMMAND cmd = {0}; + cmd.version = S2E_DECREEMON_COMMAND_VERSION; + cmd.Command = DECREE_RANDOM; + cmd.Random.buffer = (uintptr_t) buffer; + cmd.Random.buffer_size = count; + + __s2e_touch_buffer(buffer, count); + s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); + } + + return 0; +} + +/// @brief This is the syscall signal handler. +/// When the CGC binary executes a syscall instruction (int 0x80), +/// the kernel sends a SIGSYS signal that is handled here. +// Helper functions for each syscall +static void sigsys_handler(int num, siginfo_t *info, void *ucontext) { + ucontext_t *ctx = (ucontext_t *) ucontext; + enum cgc_syscall_id_t sys_num = (enum cgc_syscall_id_t) ctx->uc_mcontext.gregs[REG_EAX]; + + // Syscall parameters + uint32_t p1 = ctx->uc_mcontext.gregs[REG_EBX]; + uint32_t p2 = ctx->uc_mcontext.gregs[REG_ECX]; + uint32_t p3 = ctx->uc_mcontext.gregs[REG_EDX]; + uint32_t p4 = ctx->uc_mcontext.gregs[REG_ESI]; + uint32_t p5 = ctx->uc_mcontext.gregs[REG_EDI]; + + // Disable syscall interception for native syscall invocation + s_intercept = SYSCALL_DISPATCH_FILTER_ALLOW; + + int result = 0; + switch (sys_num) { + case CGC_SYSCALL_NULL: + result = handle_null(); + break; + case CGC_SYSCALL_TERMINATE: + handle_terminate(p1); + break; + case CGC_SYSCALL_TRANSMIT: + result = handle_transmit(p1, (char *) p2, p3, (size_t *) p4); + break; + case CGC_SYSCALL_RECEIVE: + result = handle_receive(p1, (char *) p2, p3, (size_t *) p4); + break; + case CGC_SYSCALL_FDWAIT: + result = handle_fdwait(p1, (fd_set *) p2, (fd_set *) p3, (struct timeval *) p4, (int *) p5); + break; + case CGC_SYSCALL_ALLOCATE: + result = handle_allocate(p1, p2, (void **) p3); + break; + case CGC_SYSCALL_DEALLOCATE: + result = handle_deallocate((void *) p1, p2); + break; + case CGC_SYSCALL_RANDOM: + result = handle_random((char *) p1, p2, (size_t *) p3); + break; + default: + exit(-1000); // Unknown syscall, force exit + } + + // Set result in EAX register after handling syscall + ctx->uc_mcontext.gregs[REG_EAX] = result; + + // Re-enable syscall interception + s_intercept = SYSCALL_DISPATCH_FILTER_BLOCK; +} + +int main(int argc, const char **argv) { + int fd = 0; + int ret = -1; + uint32_t entry = 0; + + const char *cmd = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--enable-seeds") == 0) { + s_enable_seeds = true; + } else if (strcmp(argv[i], "--enable-s2e") == 0) { + s_enable_symbex = true; + } else { + // Assume any other argument is potentially a filename + cmd = argv[i]; + break; + } + } + + if (cmd == NULL) { + fprintf(stderr, "Usage: %s [--enable-seeds|--enable-s2e] /path/to/cgc/binary\n", argv[0]); + goto err; + } + + fd = open(cmd, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "could not open %s\n", cmd); + goto err; + } + + ret = load_cgcos_binary(fd, &entry); + if (ret < 0) { + fprintf(stderr, "could not load binary"); + goto err; + } + + ret = init_magic_page(CGC_MAGIC_PAGE, CGC_MIN_PAGE_SIZE); + if (ret < 0) { + fprintf(stderr, "could not init magic page\n"); + goto err; + } + + ret = init_stack(STACK_TOP, STACK_SIZE); + if (ret < 0) { + fprintf(stderr, "could not init stack\n"); + goto err; + } + + ret = init_intercept(sigsys_handler, &s_intercept, INTERCEPT_EXCLUSION_START, INTERCEPT_EXCLUSION_SIZE); + if (ret < 0) { + fprintf(stderr, "could not init syscall interception\n"); + goto err; + } + + launch_binary(STACK_TOP, CGC_MAGIC_PAGE, (cgc_main_t) entry); + ret = 0; + +err: + if (fd >= 0) { + close(fd); + } + + return ret; +} From 29521220639498f311875f92f4660c727864d6ca Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 00:42:09 +0200 Subject: [PATCH 04/20] guest/linux/povtest: added tool to test generated POVs Signed-off-by: Vitaly Chipounov --- guest/linux/CMakeLists.txt | 1 + guest/linux/povtest/CMakeLists.txt | 27 +++ guest/linux/povtest/main.c | 353 +++++++++++++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 guest/linux/povtest/CMakeLists.txt create mode 100644 guest/linux/povtest/main.c diff --git a/guest/linux/CMakeLists.txt b/guest/linux/CMakeLists.txt index 5b42f80ee..67aa8a14f 100644 --- a/guest/linux/CMakeLists.txt +++ b/guest/linux/CMakeLists.txt @@ -35,6 +35,7 @@ if(${BITS} EQUAL 32) add_subdirectory(libpov) add_subdirectory(libcgc) add_subdirectory(cgcload) +add_subdirectory(povtest) endif() install(DIRECTORY scripts/ DESTINATION .) diff --git a/guest/linux/povtest/CMakeLists.txt b/guest/linux/povtest/CMakeLists.txt new file mode 100644 index 000000000..d75f7448d --- /dev/null +++ b/guest/linux/povtest/CMakeLists.txt @@ -0,0 +1,27 @@ +# S2E Selective Symbolic Execution Platform +# +# Copyright (c) 2024 Vitaly Chipounov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +add_executable(povtest main.c) + +target_link_libraries(povtest pov cgc) + +install(TARGETS povtest RUNTIME DESTINATION .) \ No newline at end of file diff --git a/guest/linux/povtest/main.c b/guest/linux/povtest/main.c new file mode 100644 index 000000000..ffa57d130 --- /dev/null +++ b/guest/linux/povtest/main.c @@ -0,0 +1,353 @@ +/// S2E Selective Symbolic Execution Platform +/// +/// Copyright (c) 2024 Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// TODO: randomize these values. +static type_request s_pov_req; +static type1_vals s_t1 = {.ipval = 0xdeadbed, .regval = 0xbadf00d}; + +static const char *s_t2_flag = "cgc{flag}"; +#define s_t2_size 0x10 +static uint8_t s_t2_buffer[s_t2_size]; +static type2_vals s_t2 = {.read_size = s_t2_size, .region_addr = 0x4347c000, .region_size = 0x1000}; + +static int s_has_segfault; +static uintptr_t s_segfault_addr; +struct user_regs_struct s_segfault_regs; + +static uint32_t get_reg(const struct user_regs_struct *regs, int regnum) { + switch (regnum) { + case 0: + return regs->eax; + case 1: + return regs->ecx; + case 2: + return regs->edx; + case 3: + return regs->ebx; + case 4: + return regs->esp; + case 5: + return regs->ebp; + case 6: + return regs->esi; + case 7: + return regs->edi; + default: + fprintf(stderr, "Invalid reg %d\n", regnum); + } + + return 0; +} + +static int validate_type1() { + if (!s_has_segfault) { + fprintf(stderr, "Binary has not crashed\n"); + return -1; + } + + uint32_t expected_eip = s_t1.ipval & s_pov_req.type1.ipmask; + uint32_t actual_eip = s_segfault_regs.eip & s_pov_req.type1.ipmask; + if (expected_eip != actual_eip) { + fprintf(stderr, "Unexpected IP at crash: expected=%#x actual=%#x\n", expected_eip, actual_eip); + return -1; + } + + uint32_t actual_reg = get_reg(&s_segfault_regs, s_pov_req.type1.regnum); + actual_reg = actual_reg & s_pov_req.type1.regmask; + uint32_t expected_reg = s_t1.regval & s_pov_req.type1.regmask; + if (actual_reg != expected_reg) { + fprintf(stderr, "Unexpected reg value at crash: expected=%#x actual=%#x\n", expected_reg, actual_reg); + return -1; + } + + return 0; +} + +static int validate_type2() { + if (strcmp((const char *) s_t2_buffer, s_t2_flag) == 0) { + return 0; + } + + fprintf(stderr, "Unexpected buffer content\n"); + return -1; +} + +static int validate_pov() { + switch (s_pov_req.povType) { + case 1: { + return validate_type1(); + } break; + + case 2: { + return validate_type2(); + } break; + default: + fprintf(stderr, "POV not negotiated\n"); + return -1; + } + + return 0; +} + +static void *negotiation_thread(void *pipeptr) { + int fd = *(int *) pipeptr; + + printf("Starting negotation thread fd=%d\n", fd); + + int ret = type_negotiate(fd, &s_pov_req); + if (ret < 0) { + fprintf(stderr, "Could not negotiate POV\n"); + return NULL; + } + + if (s_pov_req.povType == 1) { + printf("Negotiating type 1 pov\n"); + int ret = write(fd, &s_t1, sizeof(s_t1)); + if (ret != sizeof(s_t1)) { + fprintf(stderr, "Could not write negotiated values to POV\n"); + return NULL; + } + } else if (s_pov_req.povType == 2) { + printf("Negotiating type 2 pov\n"); + + int ret = write(fd, &s_t2, sizeof(s_t2)); + if (ret != sizeof(s_t2)) { + fprintf(stderr, "Could not write negotiated values to POV\n"); + return NULL; + } + + ret = read(fd, s_t2_buffer, s_t2_size); + if (ret != s_t2_size) { + fprintf(stderr, "Could not read type2 pov data"); + return NULL; + } + } else { + fprintf(stderr, "Invalid pov type %d\n", s_pov_req.povType); + } + + return NULL; +} + +void print_registers(struct user_regs_struct *regs) { + printf("Registers:\n"); + printf("EBX: %lx\n", regs->ebx); + printf("ECX: %lx\n", regs->ecx); + printf("EDX: %lx\n", regs->edx); + printf("ESI: %lx\n", regs->esi); + printf("EDI: %lx\n", regs->edi); + printf("EBP: %lx\n", regs->ebp); + printf("EAX: %lx\n", regs->eax); + printf("ORIG_EAX: %lx\n", regs->orig_eax); + printf("EIP: %lx\n", regs->eip); + printf("ESP: %lx\n", regs->esp); +} + +static int intercept_segfault_in_cb(int pid) { + int status; + while (1) { + siginfo_t info = {0}; + + if (waitpid(pid, &status, 0) < 0) { + // This happens when the process is terminated. + return 0; + } + + if (WIFEXITED(status)) { + // Child process exited normally + break; + } + + if (WIFSTOPPED(status)) { + int sig = WSTOPSIG(status); + switch (sig) { + case SIGTRAP: + // Do not pass this signal to the child. + sig = 0; + break; + + case SIGBUS: + case SIGSEGV: { + s_has_segfault = 1; + + if (ptrace(PTRACE_GETREGS, pid, NULL, &s_segfault_regs) == -1) { + fprintf(stderr, "PTRACE_GETREGS failed"); + return -1; + } + + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &info) == -1) { + fprintf(stderr, "PTRACE_GETSIGINFO failed"); + return -1; + } + + printf("SEGFAULT at %p\n", info.si_addr); + s_segfault_addr = (uintptr_t) info.si_addr; + + print_registers(&s_segfault_regs); + } break; + + default: + break; + } + + if (ptrace(PTRACE_CONT, pid, NULL, sig) == -1) { + fprintf(stderr, "PTRACE_CONT failed"); + return -1; + } + } + } + + return 0; +} + +int main(int argc, char **argv) { + if (argc < 3) { + fprintf(stderr, "Usage: %s --pov=/path/to/pov/binary -- /path/to/cgcload /path/to/challenge/binary\n", argv[0]); + exit(EXIT_FAILURE); + } + + char *pov_path = NULL; + char **cgcload_args = NULL; + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strncmp(argv[i], "--pov=", 6) == 0) { + pov_path = argv[i] + 6; + } else if (strcmp(argv[i], "--") == 0) { + cgcload_args = &argv[i + 1]; + break; + } + } + + if (pov_path == NULL || cgcload_args == NULL || cgcload_args[0] == NULL) { + fprintf(stderr, "Invalid arguments\n"); + exit(EXIT_FAILURE); + } + + if (access(pov_path, F_OK) != 0) { + fprintf(stderr, "%s does not exist\n", pov_path); + exit(EXIT_FAILURE); + } + + if (access(cgcload_args[0], F_OK) != 0) { + fprintf(stderr, "%s does not exist\n", cgcload_args[0]); + exit(EXIT_FAILURE); + } + + int pipe1[2]; + int pipe2[2]; + int pipeneg[2]; + + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pipeneg) < 0) { + fprintf(stderr, "Could not create pov negotation socket\n"); + exit(EXIT_FAILURE); + } + + if (pipe(pipe1) == -1 || pipe(pipe2) == -1) { + fprintf(stderr, "Could not create pipes\n"); + exit(EXIT_FAILURE); + } + + pthread_t tid; + if (pthread_create(&tid, NULL, negotiation_thread, &pipeneg[0]) < 0) { + fprintf(stderr, "Could not create thread\n"); + exit(EXIT_FAILURE); + } + + pid_t pov_pid = fork(); + if (pov_pid == -1) { + exit(EXIT_FAILURE); + } + + if (pov_pid == 0) { + close(pipe2[1]); // Close write end of pipe1 + close(pipe1[0]); // Close read end of pipe2 + + dup2(pipe2[0], STDIN_FILENO); + dup2(pipe1[1], STDOUT_FILENO); + + dup2(pipeneg[1], NEG_FD); + + execl(pov_path, pov_path, NULL); + exit(EXIT_FAILURE); + } + + pid_t cgcload_pid = fork(); + if (cgcload_pid == -1) { + exit(EXIT_FAILURE); + } + + if (cgcload_pid == 0) { + // Allow parent to trace this process + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) { + fprintf(stderr, "Could not init ptrace inside cgcload\n"); + exit(EXIT_FAILURE); + } + + close(pipe1[1]); // Close write end of pipe1 + close(pipe2[0]); // Close read end of pipe2 + + dup2(pipe1[0], STDIN_FILENO); + dup2(pipe2[1], STDOUT_FILENO); + + execv(cgcload_args[0], cgcload_args); + exit(EXIT_FAILURE); + } + + if (intercept_segfault_in_cb(cgcload_pid) < 0) { + fprintf(stderr, "Failed to intercept segfault in CB\n"); + exit(EXIT_FAILURE); + } + + if (waitpid(pov_pid, NULL, 0) == -1) { + fprintf(stderr, "Failed waiting for pov binary\n"); + exit(EXIT_FAILURE); + } + + if (pthread_join(tid, NULL) < 0) { + fprintf(stderr, "Could not join negotiation thread\n"); + exit(EXIT_FAILURE); + } + + if (validate_pov() < 0) { + fprintf(stderr, "Could not validate pov\n"); + exit(EXIT_FAILURE); + } + + printf("POV SUCCESS\n"); + + return 0; +} From b1130f440ab5e452b7eb9e56fd4a812760b0c97e Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 5 May 2024 12:34:17 +0200 Subject: [PATCH 05/20] guest/linux: removed cgccmd Signed-off-by: Vitaly Chipounov --- guest/common/include/s2e/cgc_interface.h | 47 -------- guest/linux/CMakeLists.txt | 1 - guest/linux/cgccmd/CMakeLists.txt | 30 ----- guest/linux/cgccmd/cgccmd.c | 139 ----------------------- 4 files changed, 217 deletions(-) delete mode 100644 guest/common/include/s2e/cgc_interface.h delete mode 100644 guest/linux/cgccmd/CMakeLists.txt delete mode 100644 guest/linux/cgccmd/cgccmd.c diff --git a/guest/common/include/s2e/cgc_interface.h b/guest/common/include/s2e/cgc_interface.h deleted file mode 100644 index 14ca7f4ca..000000000 --- a/guest/common/include/s2e/cgc_interface.h +++ /dev/null @@ -1,47 +0,0 @@ -/// S2E Selective Symbolic Execution Platform -/// -/// Copyright (c) 2016 Cyberhaven -/// -/// Permission is hereby granted, free of charge, to any person obtaining a copy -/// of this software and associated documentation files (the "Software"), to deal -/// in the Software without restriction, including without limitation the rights -/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -/// copies of the Software, and to permit persons to whom the Software is -/// furnished to do so, subject to the following conditions: -/// -/// The above copyright notice and this permission notice shall be included in all -/// copies or substantial portions of the Software. -/// -/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -/// SOFTWARE. - -#ifndef S2E_DECREE_INTERFACE_H -#define S2E_DECREE_INTERFACE_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum S2E_CGCINT_COMMANDS { - CGCINT_SET_SEED_ID, -}; - -struct S2E_CGCINT_COMMAND { - enum S2E_CGCINT_COMMANDS Command; - union { - uint64_t SeedId; - }; -} __attribute__((packed)); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/guest/linux/CMakeLists.txt b/guest/linux/CMakeLists.txt index 67aa8a14f..e5593f1d5 100644 --- a/guest/linux/CMakeLists.txt +++ b/guest/linux/CMakeLists.txt @@ -28,7 +28,6 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") add_subdirectory(function_models) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") -add_subdirectory(cgccmd) add_subdirectory(s2e.so) if(${BITS} EQUAL 32) diff --git a/guest/linux/cgccmd/CMakeLists.txt b/guest/linux/cgccmd/CMakeLists.txt deleted file mode 100644 index dacd45224..000000000 --- a/guest/linux/cgccmd/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# S2E Selective Symbolic Execution Platform -# -# Copyright (c) 2017 Dependable Systems Laboratory, EPFL -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -add_executable(cgccmd cgccmd.c) -target_link_options(cgccmd PUBLIC ${COMPAT_LD_FLAGS}) - -install(TARGETS cgccmd RUNTIME DESTINATION .) - -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - add_dependencies(cgccmd glibc-compat-main) -endif() \ No newline at end of file diff --git a/guest/linux/cgccmd/cgccmd.c b/guest/linux/cgccmd/cgccmd.c deleted file mode 100644 index a16f7f911..000000000 --- a/guest/linux/cgccmd/cgccmd.c +++ /dev/null @@ -1,139 +0,0 @@ -/// S2E Selective Symbolic Execution Platform -/// -/// Copyright (c) 2016 Cyberhaven -/// -/// Permission is hereby granted, free of charge, to any person obtaining a copy -/// of this software and associated documentation files (the "Software"), to deal -/// in the Software without restriction, including without limitation the rights -/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -/// copies of the Software, and to permit persons to whom the Software is -/// furnished to do so, subject to the following conditions: -/// -/// The above copyright notice and this permission notice shall be included in all -/// copies or substantial portions of the Software. -/// -/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -/// SOFTWARE. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -typedef int (*cmd_handler_t)(const char **args); - -typedef struct _cmd_t { - char *name; - cmd_handler_t handler; - unsigned args_count; - char *description; -} cmd_t; - -static pid_t s2e_decree_gettid(void) { - return syscall(SYS_gettid); -} - -static int handler_concolic(const char **args) { - struct S2E_DECREEMON_COMMAND cmd = {0}; - - cmd.version = S2E_DECREEMON_COMMAND_VERSION; - cmd.CurrentTask.tgid = getpid(); - cmd.CurrentTask.pid = s2e_decree_gettid(); - strncpy(cmd.currentName, "cgccmd", sizeof(cmd.currentName)); - - int enable = !strcmp(args[0], "on"); - if (enable) { - cmd.Command = DECREE_CONCOLIC_ON; - } else { - cmd.Command = DECREE_CONCOLIC_OFF; - } - - s2e_begin_atomic(); - s2e_disable_all_apic_interrupts(); - int ret = s2e_invoke_plugin("DecreeMonitor", &cmd, sizeof(cmd)); - s2e_enable_all_apic_interrupts(); - s2e_end_atomic(); - - return ret; -} - -static int handler_set_seed_id(const char **args) { - struct S2E_CGCINT_COMMAND cmd = {0}; - cmd.Command = CGCINT_SET_SEED_ID; - cmd.SeedId = strtoll(args[0], NULL, 10); - - s2e_begin_atomic(); - s2e_disable_all_apic_interrupts(); - int ret = s2e_invoke_plugin("CGCInterface", &cmd, sizeof(cmd)); - s2e_enable_all_apic_interrupts(); - s2e_end_atomic(); - - return ret; -} - -#define COMMAND(c, args, desc) \ - { #c, handler_##c, args, desc } - -static cmd_t s_commands[] = {COMMAND(concolic, 1, - "Turns on/off concolic execution on the current path " - "(cb-test specific)"), - COMMAND(set_seed_id, 1, "Sets the seed id for the current path"), - {NULL, NULL, 0, NULL}}; - -static void print_commands(void) { - unsigned i = 0; - printf("%-15s %s %s\n\n", "Command name", "Argument count", "Description"); - while (s_commands[i].handler) { - printf("%-15s %d %s\n", s_commands[i].name, s_commands[i].args_count, s_commands[i].description); - ++i; - } -} - -static int find_command(const char *cmd) { - unsigned i = 0; - while (s_commands[i].handler) { - if (!strcmp(s_commands[i].name, cmd)) { - return i; - } - ++i; - } - return -1; -} - -int main(int argc, const char **argv) { - if (argc < 2) { - print_commands(); - return -1; - } - - const char *cmd = argv[1]; - int cmd_index = find_command(cmd); - - if (cmd_index == -1) { - printf("Command %s not found\n", cmd); - return -1; - } - - argc -= 2; - ++argv; - ++argv; - - if (argc != s_commands[cmd_index].args_count) { - printf("Invalid number of arguments supplied (received %d, expected %d)\n", argc, - s_commands[cmd_index].args_count); - return -1; - } - - return s_commands[cmd_index].handler(argv); -} From 0f8acf35ed33d6f3b3fbe9ce2052e82b35b470b3 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:06:34 +0200 Subject: [PATCH 06/20] plugins: pass full guest data to onSegFault signal Signed-off-by: Vitaly Chipounov --- .../ExecutionTracers/TestCaseGenerator.cpp | 14 +++++++--- .../ExecutionTracers/TestCaseGenerator.h | 3 +- .../OSMonitors/Linux/BaseLinuxMonitor.h | 2 +- .../Plugins/OSMonitors/Linux/LinuxMonitor.cpp | 2 +- .../s2e/Plugins/Searchers/SeedScheduler.cpp | 4 +-- .../src/s2e/Plugins/Searchers/SeedScheduler.h | 3 +- .../Plugins/Support/WebServiceInterface.cpp | 3 +- .../s2e/Plugins/Support/WebServiceInterface.h | 3 +- .../PovGenerationPolicy.cpp | 28 +++++++++---------- .../PovGenerationPolicy.h | 3 +- 10 files changed, 35 insertions(+), 30 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.cpp b/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.cpp index 7dc785476..6908d14af 100644 --- a/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.cpp +++ b/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.cpp @@ -157,16 +157,22 @@ void TestCaseGenerator::disable() { } void TestCaseGenerator::onWindowsUserCrash(S2EExecutionState *state, const WindowsUserModeCrash &desc) { - onSegFault(state, desc.Pid, desc.ExceptionAddress); + S2E_LINUXMON_COMMAND_SEG_FAULT data = {0}; + data.pc = desc.ExceptionAddress; + + onSegFault(state, desc.Pid, data); } void TestCaseGenerator::onWindowsKernelCrash(S2EExecutionState *state, const vmi::windows::BugCheckDescription &desc) { - onSegFault(state, 0, state->pc); + S2E_LINUXMON_COMMAND_SEG_FAULT data = {0}; + data.pc = state->pc; + + onSegFault(state, 0, data); } -void TestCaseGenerator::onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc) { +void TestCaseGenerator::onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data) { std::stringstream ss; - ss << "crash:" << hexval(pid) << ":" << hexval(pc); + ss << "crash:" << hexval(pid) << ":" << hexval(data.pc); generateTestCases(state, ss.str(), TC_FILE); } diff --git a/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.h b/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.h index 7b22fd96a..507141a59 100644 --- a/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.h +++ b/libs2eplugins/src/s2e/Plugins/ExecutionTracers/TestCaseGenerator.h @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -256,7 +257,7 @@ class TestCaseGenerator : public Plugin, public IPluginInvoker { void onStateFork(S2EExecutionState *state, const std::vector &newStates, const std::vector> &newConditions); void onStateKill(S2EExecutionState *state); - void onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc); + void onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data); void onWindowsUserCrash(S2EExecutionState *state, const WindowsUserModeCrash &desc); void onWindowsKernelCrash(S2EExecutionState *state, const vmi::windows::BugCheckDescription &desc); diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h index 927ed1478..104fcb3a0 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h @@ -148,7 +148,7 @@ class BaseLinuxMonitor : public OSMonitor, public IPluginInvoker { public: /// Emitted when a segment fault occurs in the kernel - sigc::signal onSegFault; + sigc::signal onSegFault; /// /// Create a new monitor for the Linux kernel diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp index 0b82b6eb9..31a875b26 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp @@ -92,7 +92,7 @@ void LinuxMonitor::handleSegfault(S2EExecutionState *state, const S2E_LINUXMON_C state->disassemble(getDebugStream(state), cmd.SegFault.pc, 256); - onSegFault.emit(state, cmd.CurrentTask.tgid, cmd.SegFault.pc); + onSegFault.emit(state, cmd.CurrentTask.tgid, cmd.SegFault); if (m_terminateOnSegfault) { getDebugStream(state) << "Terminating state: received segfault\n"; diff --git a/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.cpp b/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.cpp index 3989f35ae..684b126b1 100644 --- a/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.cpp +++ b/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.cpp @@ -54,8 +54,6 @@ void SeedScheduler::initialize() { if (auto lm = dynamic_cast(monitor)) { lm->onSegFault.connect(sigc::mem_fun(*this, &SeedScheduler::onSegFault)); - } else if (auto dm = dynamic_cast(monitor)) { - dm->onSegFault.connect(sigc::mem_fun(*this, &SeedScheduler::onSegFault)); } else if (dynamic_cast(monitor)) { WindowsCrashMonitor *cmon = s2e()->getPlugin(); if (!cmon) { @@ -232,7 +230,7 @@ void SeedScheduler::onSeed(const seeds::Seed &seed, seeds::SeedEvent event) { } } -void SeedScheduler::onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t address) { +void SeedScheduler::onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data) { m_timeOfLastCrash = std::chrono::steady_clock::now(); } diff --git a/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.h b/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.h index d04229ee4..38c643701 100644 --- a/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.h +++ b/libs2eplugins/src/s2e/Plugins/Searchers/SeedScheduler.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -76,7 +77,7 @@ class SeedScheduler : public Plugin { void onSeed(const Seed &seed, SeedEvent event); void onNewBlockCovered(S2EExecutionState *state); - void onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t address); + void onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data); void onWindowsUserCrash(S2EExecutionState *state, const WindowsUserModeCrash &desc); void onWindowsKernelCrash(S2EExecutionState *state, const vmi::windows::BugCheckDescription &desc); diff --git a/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.cpp b/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.cpp index a5460880a..af0321dc1 100644 --- a/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.cpp +++ b/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.cpp @@ -168,7 +168,8 @@ void WebServiceInterface::onEngineShutdown() { sendStats(); } -void WebServiceInterface::onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc) { +void WebServiceInterface::onSegFault(S2EExecutionState *state, uint64_t pid, + const S2E_LINUXMON_COMMAND_SEG_FAULT &data) { ++m_segFaults; } diff --git a/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.h b/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.h index 75263eef6..cbd1c2b6b 100644 --- a/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.h +++ b/libs2eplugins/src/s2e/Plugins/Support/WebServiceInterface.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ class WebServiceInterface : public Plugin { void onProcessForkComplete(bool isChild); void onStateKill(S2EExecutionState *state); void onSeed(const seeds::Seed &seed, seeds::SeedEvent event); - void onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc); + void onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data); void onWindowsUserCrash(S2EExecutionState *state, const WindowsUserModeCrash &desc); void onWindowsKernelCrash(S2EExecutionState *state, const vmi::windows::BugCheckDescription &desc); diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.cpp index ec5f2f511..078a1c874 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.cpp @@ -55,13 +55,10 @@ void PovGenerationPolicy::initialize() { fsigc::signal_base::HIGH_PRIORITY); // Detecting platform - m_decreeMonitor = s2e()->getPlugin(); m_linuxMonitor = s2e()->getPlugin(); m_windowsCrashMonitor = s2e()->getPlugin(); - if (m_decreeMonitor) { - m_decreeMonitor->onSegFault.connect(sigc::mem_fun(*this, &PovGenerationPolicy::onSegFault)); - } else if (m_linuxMonitor) { + if (m_linuxMonitor) { m_linuxMonitor->onSegFault.connect(sigc::mem_fun(*this, &PovGenerationPolicy::onSegFault)); } else if (m_windowsCrashMonitor) { m_windowsCrashMonitor->onUserModeCrash.connect(sigc::mem_fun(*this, &PovGenerationPolicy::onSegFaultWinUser)); @@ -131,26 +128,27 @@ void PovGenerationPolicy::onPovReadyHandler(S2EExecutionState *state, const PovO } void PovGenerationPolicy::onSegFaultWinUser(S2EExecutionState *state, const WindowsUserModeCrash &crash) { - onSegFault(state, crash.Pid, crash.ExceptionAddress); + S2E_LINUXMON_COMMAND_SEG_FAULT data = {0}; + data.pc = crash.ExceptionAddress; + + onSegFault(state, crash.Pid, data); } void PovGenerationPolicy::onSegFaultWinKernel(S2EExecutionState *state, const vmi::windows::BugCheckDescription &crash) { // TODO: implement this properly, we don't have an easy way // to get the required information for now. - onSegFault(state, 0, 0); + S2E_LINUXMON_COMMAND_SEG_FAULT data = {0}; + onSegFault(state, 0, data); } -void PovGenerationPolicy::onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc) { +void PovGenerationPolicy::onSegFault(S2EExecutionState *state, uint64_t pid, + const S2E_LINUXMON_COMMAND_SEG_FAULT &data) { if (!m_process->isTrackedPid(state, pid)) { - // Only dump memory on decree, because it has relatively small binaries - if (m_decreeMonitor) { - std::stringstream ss; - m_decreeMonitor->dumpUserspaceMemory(state, ss); - getWarningsStream(state) << ss.str(); - } + std::stringstream ss; + getWarningsStream(state) << ss.str(); - state->disassemble(getWarningsStream(state) << "\n", pc, 64); + state->disassemble(getWarningsStream(state) << "\n", data.pc, 64); s2e_assert(state, false, "Untracked pid=" << hexval(pid) << " segfaulted"); } @@ -165,7 +163,7 @@ void PovGenerationPolicy::onSegFault(S2EExecutionState *state, uint64_t pid, uin // XXX: it's more useful to report the address of the last instructions PovOptions opts; - opts.m_faultAddress = pc; + opts.m_faultAddress = data.pc; onPovReadyHandler(state, opts, "", true); } diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.h b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.h index 11e407851..d9443d7b0 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.h +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/PovGenerationPolicy.h @@ -46,7 +46,6 @@ class PovGenerationPolicy : public Plugin { S2E_PLUGIN private: - DecreeMonitor *m_decreeMonitor; LinuxMonitor *m_linuxMonitor; WindowsCrashMonitor *m_windowsCrashMonitor; @@ -69,7 +68,7 @@ class PovGenerationPolicy : public Plugin { void onPovReadyHandler(S2EExecutionState *state, const PovOptions &opt, const std::string &recipeName, bool isCrash); - void onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc); + void onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data); void onSegFaultWinUser(S2EExecutionState *state, const WindowsUserModeCrash &crash); From 0449e20e357e014cefb2350c5d33948d9bf3d122 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:07:12 +0200 Subject: [PATCH 07/20] plugins/memutils: fixed build error Signed-off-by: Vitaly Chipounov --- libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h index 995346292..476cdf44a 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h @@ -95,7 +95,7 @@ class MemUtils : public Plugin { /// \param prevItem Used to automatically merge sequence spanning 2 memory pages. /// If the function is called with bitmask 111000 and then 0111, it will update previously /// found sequence to have size 4. - void findSequencesOfSymbolicData(const BitArrayPtr &concreteMask, uint64_t baseAddr, AddrSize *prevItem, + void findSequencesOfSymbolicData(const klee::BitArrayPtr &concreteMask, uint64_t baseAddr, AddrSize *prevItem, std::vector &sequences); /// \brief Find contiguous chunks of symbolic data in selected memory pages From 327d957a05da1e28c162ffda1f69f4296975fb55 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:07:38 +0200 Subject: [PATCH 08/20] MemoryMap: removed decree support Signed-off-by: Vitaly Chipounov --- .../Plugins/OSMonitors/Support/MemoryMap.cpp | 31 ------------------- .../Plugins/OSMonitors/Support/MemoryMap.h | 1 - 2 files changed, 32 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.cpp index 222d83a44..99a11ac91 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.cpp @@ -31,7 +31,6 @@ #include #include -#include #include #include @@ -205,13 +204,6 @@ void MemoryMap::initialize() { return; } - // Register Decree events - DecreeMonitor *decree = dynamic_cast(m_monitor); - if (decree) { - decree->onUpdateMemoryMap.connect(sigc::mem_fun(*this, &MemoryMap::onDecreeUpdateMemoryMap)); - return; - } - // Register Linux events LinuxMonitor *linmon = dynamic_cast(m_monitor); if (linmon) { @@ -281,29 +273,6 @@ void MemoryMap::onLinuxMemoryUnmap(S2EExecutionState *state, uint64_t pid, uint6 plgState->removeRegion(pid, start, end); } -void MemoryMap::onDecreeUpdateMemoryMap(S2EExecutionState *state, uint64_t pid, const S2E_DECREEMON_VMA &vma) { - if (!m_proc->isTrackedPid(state, pid)) { - return; - } - - MemoryMapRegionType type = MM_NONE; - - if (vma.flags & S2E_DECREEMON_VM_READ) { - type |= MM_READ; - } - - if (vma.flags & S2E_DECREEMON_VM_WRITE) { - type |= MM_WRITE; - } - - if (vma.flags & S2E_DECREEMON_VM_EXEC) { - type |= MM_EXEC; - } - - DECLARE_PLUGINSTATE(MemoryMapState, state); - plgState->addRegion(pid, vma.start, vma.end, type); -} - void MemoryMap::onProcessUnload(S2EExecutionState *state, uint64_t pageDir, uint64_t pid, uint64_t returnCode) { DECLARE_PLUGINSTATE(MemoryMapState, state); plgState->removePid(pid); diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.h index 2d7895445..b4483cb9f 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemoryMap.h @@ -117,7 +117,6 @@ class MemoryMap : public Plugin { void updateMemoryStats(S2EExecutionState *state); - void onDecreeUpdateMemoryMap(S2EExecutionState *state, uint64_t pid, const s2e::plugins::S2E_DECREEMON_VMA &vma); void onLinuxMemoryMap(S2EExecutionState *state, uint64_t pid, uint64_t addr, uint64_t size, uint64_t prot); void onLinuxMemoryUnmap(S2EExecutionState *state, uint64_t pid, uint64_t addr, uint64_t size); From c5f61e54698f9c20fc1cc923059b82e8765a6179 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:09:13 +0200 Subject: [PATCH 09/20] DecreeMonitor: removed generic OS monitoring support This is now handled by LinuxMonitor. Signed-off-by: Vitaly Chipounov --- .../include/s2e/monitors/commands/decree.h | 17 -- .../OSMonitors/Linux/DecreeMonitor.cpp | 278 ++++++------------ .../Plugins/OSMonitors/Linux/DecreeMonitor.h | 51 ++-- .../VulnerabilityAnalysis/CGCInterface.cpp | 6 +- .../VulnerabilityAnalysis/CGCInterface.h | 5 +- .../VulnerabilityAnalysis/Recipe/Recipe.cpp | 10 +- 6 files changed, 113 insertions(+), 254 deletions(-) diff --git a/guest/common/include/s2e/monitors/commands/decree.h b/guest/common/include/s2e/monitors/commands/decree.h index f67245683..32cdad699 100644 --- a/guest/common/include/s2e/monitors/commands/decree.h +++ b/guest/common/include/s2e/monitors/commands/decree.h @@ -40,8 +40,6 @@ extern "C" { #define S2E_DECREEMON_COMMAND_VERSION 0x202301082207ULL // date +%Y%m%d%H%M enum S2E_DECREEMON_COMMANDS { - DECREE_SEGFAULT, - DECREE_PROCESS_LOAD, DECREE_READ_DATA, DECREE_WRITE_DATA, DECREE_FD_WAIT, @@ -54,13 +52,7 @@ enum S2E_DECREEMON_COMMANDS { DECREE_HANDLE_SYMBOLIC_TRANSMIT_BUFFER, DECREE_HANDLE_SYMBOLIC_RECEIVE_BUFFER, DECREE_HANDLE_SYMBOLIC_RANDOM_BUFFER, - DECREE_COPY_TO_USER, - DECREE_UPDATE_MEMORY_MAP, DECREE_SET_CB_PARAMS, - DECREE_INIT, - DECREE_KERNEL_PANIC, - DECREE_MODULE_LOAD, - DECREE_TASK_SWITCH }; struct S2E_DECREEMON_COMMAND_READ_DATA { @@ -187,27 +179,18 @@ struct S2E_DECREEMON_COMMAND_KERNEL_PANIC { struct S2E_DECREEMON_COMMAND { uint64_t version; enum S2E_DECREEMON_COMMANDS Command; - struct S2E_LINUXMON_TASK CurrentTask; union { - struct S2E_LINUXMON_COMMAND_PROCESS_LOAD ProcessLoad; - struct S2E_LINUXMON_COMMAND_MODULE_LOAD ModuleLoad; struct S2E_DECREEMON_COMMAND_READ_DATA Data; struct S2E_DECREEMON_COMMAND_WRITE_DATA WriteData; struct S2E_DECREEMON_COMMAND_FD_WAIT FDWait; - struct S2E_DECREEMON_COMMAND_SEG_FAULT SegFault; struct S2E_DECREEMON_COMMAND_RANDOM Random; struct S2E_DECREEMON_COMMAND_READ_DATA_POST DataPost; struct S2E_DECREEMON_COMMAND_GET_CFG_BOOL GetCfgBool; struct S2E_DECREEMON_COMMAND_HANDLE_SYMBOLIC_SIZE SymbolicSize; struct S2E_DECREEMON_COMMAND_HANDLE_SYMBOLIC_BUFFER SymbolicBuffer; - struct S2E_DECREEMON_COMMAND_COPY_TO_USER CopyToUser; - struct S2E_DECREEMON_COMMAND_UPDATE_MEMORY_MAP UpdateMemoryMap; struct S2E_DECREEMON_COMMAND_SET_CB_PARAMS CbParams; struct S2E_DECREEMON_COMMAND_INIT Init; - struct S2E_DECREEMON_COMMAND_KERNEL_PANIC Panic; - struct S2E_LINUXMON_COMMAND_TASK_SWITCH TaskSwitch; }; - char currentName[32]; // not NULL terminated } __attribute__((packed)); #ifdef __cplusplus diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp index 61a1166fd..2dddf5c5e 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp @@ -43,7 +43,7 @@ using namespace klee; namespace s2e { namespace plugins { -S2E_DEFINE_PLUGIN(DecreeMonitor, "DecreeMonitor S2E plugin", "OSMonitor", "BaseInstructions", "Vmi"); +S2E_DEFINE_PLUGIN(DecreeMonitor, "DecreeMonitor S2E plugin", "", "BaseInstructions", "LinuxMonitor", "Vmi"); namespace decree { @@ -87,13 +87,12 @@ void DecreeMonitor::initialize() { exit(-1); } - // XXX: fix me. Very basic plugins like monitors probably - // shouldn't call other plugins. m_seedSearcher = s2e()->getPlugin(); - // XXX: fix this circular dependency. m_detector = s2e()->getPlugin(); + m_monitor = s2e()->getPlugin(); + ConfigFile *cfg = s2e()->getConfig(); m_symbolicReadLimitCount = cfg->getInt(getConfigKey() + ".symbolicReadLimitCount", 16 * 1024 * 1024); @@ -105,7 +104,6 @@ void DecreeMonitor::initialize() { m_invokeOriginalSyscalls = cfg->getBool(getConfigKey() + ".invokeOriginalSyscalls", false); m_printOpcodeOffsets = cfg->getBool(getConfigKey() + ".printOpcodeOffsets", false); - m_terminateOnSegfault = cfg->getBool(getConfigKey() + ".terminateOnSegfault", true); m_terminateProcessGroupOnSegfault = cfg->getBool(getConfigKey() + ".terminateProcessGroupOnSegfault", false); m_concolicMode = cfg->getBool(getConfigKey() + ".concolicMode", false); m_logWrittenData = cfg->getBool(getConfigKey() + ".logWrittenData", true); @@ -120,14 +118,10 @@ void DecreeMonitor::initialize() { m_timeToFirstSegfault = -1; time(&m_startTime); - m_commandSize = sizeof(S2E_DECREEMON_COMMAND); - m_commandVersion = S2E_DECREEMON_COMMAND_VERSION; - - s2e()->getCorePlugin()->onInitializationComplete.connect( - sigc::mem_fun(*this, &DecreeMonitor::onInitializationComplete)); + m_monitor->onSegFault.connect(sigc::mem_fun(*this, &DecreeMonitor::onSegFault)); } -class DecreeMonitorState : public BaseLinuxMonitorState { +class DecreeMonitorState : public PluginState { public: /* How many bytes (symbolic or concrete) were read by each pid */ std::map m_readBytesCount; @@ -162,11 +156,6 @@ class DecreeMonitorState : public BaseLinuxMonitorState { } }; -void DecreeMonitor::onInitializationComplete(S2EExecutionState *state) { - // Initialize the plugin state before BaseLinuxMonitor tries to access it. - getPluginState(state, &DecreeMonitorState::factory); -} - unsigned DecreeMonitor::getSymbolicReadsCount(S2EExecutionState *state) const { DECLARE_PLUGINSTATE_CONST(DecreeMonitorState, state); return plgState->m_totalReadBytesCount; @@ -618,57 +607,6 @@ void DecreeMonitor::handleSymbolicRandomBuffer(S2EExecutionState *state, uint64_ handleSymbolicBuffer(state, pid, SYMBUFF_RANDOM, d.ptr_addr, d.size_addr); } -void DecreeMonitor::handleCopyToUser(S2EExecutionState *state, uint64_t pid, - const S2E_DECREEMON_COMMAND_COPY_TO_USER &d) { - if (!d.done) { - return; - } - - if (d.ret != 0) { - getDebugStream(state) << "copy_to_user returned " << d.ret << "\n"; - return; - } - - for (unsigned i = 0; i < d.count; ++i) { - ref value = state->mem()->read(d.user_addr + i, Expr::Int8); - if (!value) { - getDebugStream(state) << "could not read address " << hexval(d.user_addr + i) << "\n"; - continue; - } - - if (isa(value)) { - g_s2e->getCorePlugin()->onConcreteDataMemoryAccess.emit(state, d.user_addr + i, - cast(value)->getZExtValue(), 1, - MEM_TRACE_FLAG_WRITE | MEM_TRACE_FLAG_PLUGIN); - } else { - g_s2e->getCorePlugin()->onAfterSymbolicDataMemoryAccess.emit( - state, ConstantExpr::create(d.user_addr + i, Expr::Int64), ConstantExpr::create(-1, Expr::Int64), value, - MEM_TRACE_FLAG_WRITE | MEM_TRACE_FLAG_PLUGIN); - } - } -} - -/// \brief Handle UPDATE_MEMORY_MAP command -/// -/// Parses command arguments and emits onUpdateMemoryMap event. -/// -/// \param state current state -/// \param pid PID of related process -/// \param d command data -void DecreeMonitor::handleUpdateMemoryMap(S2EExecutionState *state, uint64_t pid, - const S2E_DECREEMON_COMMAND_UPDATE_MEMORY_MAP &d) { - getDebugStream(state) << "New memory map for pid=" << hexval(pid) << "\n"; - - S2E_DECREEMON_VMA buf[d.count]; - bool ok = state->mem()->read(d.buffer, buf, sizeof(buf)); - s2e_assert(state, ok, "Failed to read memory"); - - for (unsigned i = 0; i < d.count; i++) { - getDebugStream(state) << " " << buf[i] << "\n"; - onUpdateMemoryMap.emit(state, pid, buf[i]); - } -} - /// /// \brief Read and writes CB parameters /// @@ -726,16 +664,6 @@ void DecreeMonitor::handleSetParams(S2EExecutionState *state, uint64_t pid, S2E_ memcpy(d.cgc_seed, newSeed, d.cgc_seed_len); } -void DecreeMonitor::handleInit(S2EExecutionState *state, const S2E_DECREEMON_COMMAND_INIT &d) { - getDebugStream(state) << "handleInit: page_offset=" << hexval(d.page_offset) << "\n"; - - m_kernelStartAddress = d.page_offset; - - completeInitialization(state); - - loadKernelImage(state, d.start_kernel); -} - void DecreeMonitor::printOpcodeOffsets(S2EExecutionState *state) { getDebugStream(state) << "S2E_DECREEMON_COMMAND offsets:\n"; @@ -770,10 +698,6 @@ void DecreeMonitor::printOpcodeOffsets(S2EExecutionState *state) { PRINTOFF(FDWait.invoke_orig); PRINTOFF(FDWait.result); - PRINTOFF(SegFault.pc); - PRINTOFF(SegFault.address); - PRINTOFF(SegFault.fault); - PRINTOFF(Random.buffer); PRINTOFF(Random.buffer_size); @@ -784,105 +708,110 @@ void DecreeMonitor::printOpcodeOffsets(S2EExecutionState *state) { PRINTOFF(SymbolicBuffer.ptr_addr); PRINTOFF(SymbolicBuffer.size_addr); +} - PRINTOFF(CopyToUser.user_addr); - PRINTOFF(CopyToUser.addr); - PRINTOFF(CopyToUser.count); - PRINTOFF(CopyToUser.done); - PRINTOFF(CopyToUser.ret); +void DecreeMonitor::onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data) { + if (m_firstSegfault) { + time_t now; + time(&now); + m_timeToFirstSegfault = difftime(now, m_startTime); + m_firstSegfault = false; + } - PRINTOFF(UpdateMemoryMap.count); - PRINTOFF(UpdateMemoryMap.buffer); + getWarningsStream(state) << "received segfault" + << " pagedir=" << hexval(state->regs()->getPageDir()) << " pid=" << hexval(pid) + << " pc=" << hexval(data.pc) << " addr=" << hexval(data.address) << "\n"; - PRINTOFF(currentName); -} + // Don't switch state until it finishes and gets killed by bootstrap + // Need to print a message here to avoid confusion and needless debugging, + // wondering why the searcher doesn't work anymore. + getDebugStream(state) << "Blocking searcher until state is terminated\n"; + state->setStateSwitchForbidden(true); + + state->disassemble(getDebugStream(state), data.pc, 256); -void DecreeMonitor::handleTaskSwitch(S2EExecutionState *state, const S2E_DECREEMON_COMMAND &cmd) { - BaseLinuxMonitor::handleTaskSwitch(state, cmd.CurrentTask, cmd.TaskSwitch); + if (m_terminateProcessGroupOnSegfault) { + getWarningsStream(state) << "Terminating process group: received segfault\n"; + killpg(0, SIGTERM); + } } -void DecreeMonitor::handleCommand(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize, void *cmd) { - S2E_DECREEMON_COMMAND &command = *(S2E_DECREEMON_COMMAND *) cmd; - std::string currentName(command.currentName, strnlen(command.currentName, sizeof(command.currentName))); - - bool processSyscall = true; - if (m_detector && !m_detector->isTrackedPid(state, command.CurrentTask.pid)) { - if (command.Command != DECREE_TASK_SWITCH) { - processSyscall = false; - getDebugStream(state) << "Pid " << hexval(command.CurrentTask.pid) << " is not tracked. " - << "Skipping syscall " << command.Command << " processing.\n"; +void DecreeMonitor::handleOpcodeInvocation(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize) { + uint64_t commandSize = sizeof(S2E_DECREEMON_COMMAND); + uint64_t commandVersion = S2E_DECREEMON_COMMAND_VERSION; + uint8_t cmd[guestDataSize]; + memset(cmd, 0, guestDataSize); + + // Validate the size of the instruction + s2e_assert(state, guestDataSize == commandSize, + "Invalid command size " << guestDataSize << " != " << commandSize + << " from pagedir=" << hexval(state->regs()->getPageDir()) + << " pc=" << hexval(state->regs()->getPc())); + + // Read any symbolic bytes + std::ostringstream symbolicBytes; + for (unsigned i = 0; i < guestDataSize; ++i) { + ref t = state->mem()->read(guestDataPtr + i); + if (t && !isa(t)) { + symbolicBytes << " " << hexval(i, 2) << "\n"; } } - switch (command.Command) { - case DECREE_SEGFAULT: { - if (m_firstSegfault) { - time_t now; - time(&now); - m_timeToFirstSegfault = difftime(now, m_startTime); - m_firstSegfault = false; - } + if (symbolicBytes.str().length()) { + getWarningsStream(state) << "Command has symbolic bytes at " << symbolicBytes.str() << "\n"; + } - getWarningsStream(state) << "received segfault" - << " type=" << command.SegFault.fault - << " pagedir=" << hexval(state->regs()->getPageDir()) - << " pid=" << hexval(command.CurrentTask.pid) - << " pc=" << hexval(command.SegFault.pc) - << " addr=" << hexval(command.SegFault.address) << " name=" << currentName << "\n"; + // Read the instruction + bool ok = state->mem()->read(guestDataPtr, cmd, guestDataSize); + s2e_assert(state, ok, "Failed to read instruction memory"); - // Dont switch state until it finishes and gets killed by bootstrap - // Need to print a message here to avoid confusion and needless debugging, - // wondering why the searcher doesn't work anymore. - getDebugStream(state) << "Blocking searcher until state is terminated\n"; - state->setStateSwitchForbidden(true); + // Validate the instruction's version - state->disassemble(getDebugStream(state), command.SegFault.pc, 256); + // The version field comes always first in all commands + uint64_t version = *(uint64_t *) cmd; - onSegFault.emit(state, command.CurrentTask.pid, command.SegFault.pc); + if (version != commandVersion) { + std::ostringstream os; - if (m_terminateProcessGroupOnSegfault) { - getWarningsStream(state) << "Terminating process group: received segfault\n"; - killpg(0, SIGTERM); - } + for (unsigned i = 0; i < guestDataSize; ++i) { + os << hexval(cmd[i]) << " "; + } - if (m_terminateOnSegfault) { - getDebugStream(state) << "Terminating state: received segfault\n"; - s2e()->getExecutor()->terminateState(*state, "Segfault"); - } - } break; + getWarningsStream(state) << "Command bytes: " << os.str() << "\n"; - case DECREE_PROCESS_LOAD: { - handleProcessLoad(state, command.CurrentTask.pid, command.ProcessLoad); - } break; + s2e_assert(state, false, + "Invalid command version " << hexval(version) << " != " << hexval(commandVersion) + << " from pagedir=" << hexval(state->regs()->getPageDir()) + << " pc=" << hexval(state->regs()->getPc())); + } + handleCommand(state, guestDataPtr, guestDataSize, cmd); +} + +void DecreeMonitor::handleCommand(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize, void *cmd) { + S2E_DECREEMON_COMMAND &command = *(S2E_DECREEMON_COMMAND *) cmd; + + auto pid = m_monitor->getPid(state); + + switch (command.Command) { case DECREE_READ_DATA: { - if (processSyscall) { - handleReadData(state, command.CurrentTask.pid, command.Data); - } + handleReadData(state, pid, command.Data); } break; case DECREE_READ_DATA_POST: { - if (processSyscall) { - handleReadDataPost(state, command.CurrentTask.pid, command.DataPost); - } + handleReadDataPost(state, pid, command.DataPost); } break; case DECREE_WRITE_DATA: { - if (processSyscall) { - handleWriteData(state, command.CurrentTask.pid, command.WriteData); - } + handleWriteData(state, pid, command.WriteData); } break; case DECREE_FD_WAIT: { - if (processSyscall) { - handleFdWait(state, command, guestDataPtr); - } + handleFdWait(state, command, guestDataPtr); } break; case DECREE_RANDOM: { - if (processSyscall) { - handleRandom(state, command.CurrentTask.pid, command.Random); - } + handleRandom(state, pid, command.Random); } break; case DECREE_CONCOLIC_ON: { @@ -900,49 +829,29 @@ void DecreeMonitor::handleCommand(S2EExecutionState *state, uint64_t guestDataPt } break; case DECREE_GET_CFG_BOOL: { - handleGetCfgBool(state, command.CurrentTask.pid, command.GetCfgBool); + handleGetCfgBool(state, pid, command.GetCfgBool); bool ok = state->mem()->write(guestDataPtr, &command, sizeof(command)); s2e_assert(state, ok, "Failed to write memory"); } break; case DECREE_HANDLE_SYMBOLIC_ALLOCATE_SIZE: { - if (processSyscall) { - handleSymbolicAllocateSize(state, command.CurrentTask.pid, command.SymbolicSize); - } + handleSymbolicAllocateSize(state, pid, command.SymbolicSize); } break; case DECREE_HANDLE_SYMBOLIC_TRANSMIT_BUFFER: { - if (processSyscall) { - handleSymbolicTransmitBuffer(state, command.CurrentTask.pid, command.SymbolicBuffer); - } + handleSymbolicTransmitBuffer(state, pid, command.SymbolicBuffer); } break; case DECREE_HANDLE_SYMBOLIC_RECEIVE_BUFFER: { - if (processSyscall) { - handleSymbolicReceiveBuffer(state, command.CurrentTask.pid, command.SymbolicBuffer); - } + handleSymbolicReceiveBuffer(state, pid, command.SymbolicBuffer); } break; case DECREE_HANDLE_SYMBOLIC_RANDOM_BUFFER: { - if (processSyscall) { - handleSymbolicRandomBuffer(state, command.CurrentTask.pid, command.SymbolicBuffer); - } - } break; - - case DECREE_COPY_TO_USER: { - if (processSyscall) { - handleCopyToUser(state, command.CurrentTask.pid, command.CopyToUser); - } - } break; - - case DECREE_UPDATE_MEMORY_MAP: { - if (processSyscall) { - handleUpdateMemoryMap(state, command.CurrentTask.pid, command.UpdateMemoryMap); - } + handleSymbolicRandomBuffer(state, pid, command.SymbolicBuffer); } break; case DECREE_SET_CB_PARAMS: { - handleSetParams(state, command.CurrentTask.pid, command.CbParams); + handleSetParams(state, pid, command.CbParams); if (!state->mem()->write(guestDataPtr, &command, guestDataSize)) { // Do not kill the state in case of an error here. This would prevent // any exploration at all. @@ -952,23 +861,6 @@ void DecreeMonitor::handleCommand(S2EExecutionState *state, uint64_t guestDataPt s2e_warn_assert(state, false, "Could not write new seed params"); } } break; - - case DECREE_INIT: { - handleInit(state, command.Init); - } break; - - case DECREE_KERNEL_PANIC: { - handleKernelPanic(state, command.Panic.message, command.Panic.message_size); - } break; - - case DECREE_MODULE_LOAD: { - handleModuleLoad(state, command.CurrentTask.pid, command.ModuleLoad); - break; - } - - case DECREE_TASK_SWITCH: { - handleTaskSwitch(state, command); - } break; } } diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h index a6c1cc3fe..e00e6b2de 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h @@ -24,9 +24,15 @@ #ifndef S2E_PLUGINS_DECREE_MONITOR_H #define S2E_PLUGINS_DECREE_MONITOR_H -#include +#include +#include + +#include +#include +#include +#include -#include +#include #include #include @@ -58,12 +64,6 @@ template T &operator<<(T &stream, const S2E_DECREEMON_VMA &v) { template T &operator<<(T &stream, const S2E_DECREEMON_COMMANDS &c) { switch (c) { - case DECREE_SEGFAULT: - stream << "SEGFAULT"; - break; - case DECREE_PROCESS_LOAD: - stream << "PROCESS_LOAD"; - break; case DECREE_READ_DATA: stream << "READ_DATA"; break; @@ -100,12 +100,6 @@ template T &operator<<(T &stream, const S2E_DECREEMON_COMMANDS &c) case DECREE_HANDLE_SYMBOLIC_RANDOM_BUFFER: stream << "HANDLE_SYMBOLIC_RANDOM_BUFFER"; break; - case DECREE_COPY_TO_USER: - stream << "COPY_TO_USER"; - break; - case DECREE_UPDATE_MEMORY_MAP: - stream << "UPDATE_MEMORY_MAP"; - break; case DECREE_SET_CB_PARAMS: stream << "SET_CB_PARAMS"; break; @@ -116,13 +110,13 @@ template T &operator<<(T &stream, const S2E_DECREEMON_COMMANDS &c) return stream; } -class DecreeMonitor : public BaseLinuxMonitor { +class DecreeMonitor : public Plugin, public IPluginInvoker { S2E_PLUGIN friend class DecreeMonitorState; public: - DecreeMonitor(S2E *s2e) : BaseLinuxMonitor(s2e) { + DecreeMonitor(S2E *s2e) : Plugin(s2e) { } void initialize(); @@ -132,11 +126,12 @@ class DecreeMonitor : public BaseLinuxMonitor { } private: + Vmi *m_vmi; + MemoryMap *m_map; MemUtils *m_memutils; BaseInstructions *m_base; seeds::SeedSearcher *m_seedSearcher; - - // XXX: circular dependency + LinuxMonitor *m_monitor; ProcessExecutionDetector *m_detector; llvm::DenseMap m_functionsMap; @@ -220,13 +215,6 @@ class DecreeMonitor : public BaseLinuxMonitor { > onSymbolicBuffer; - /// \brief onUpdateMemoryMap is emitted when the memory layout - /// of the guest process changes - /// - /// Currently event is emitted after process is loaded, and also - /// after allocate and deallocate syscalls. - sigc::signal onUpdateMemoryMap; - bool getFaultAddress(S2EExecutionState *state, uint64_t siginfo_ptr, uint64_t *address); void getPreFeedData(S2EExecutionState *state, uint64_t pid, uint64_t count, std::vector &data); @@ -234,7 +222,7 @@ class DecreeMonitor : public BaseLinuxMonitor { klee::ref makeSymbolicRead(S2EExecutionState *state, uint64_t pid, uint64_t fd, uint64_t buf, uint64_t count, klee::ref countExpr); - virtual void handleCommand(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize, void *cmd); + virtual void handleOpcodeInvocation(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize); unsigned getSymbolicReadsCount(S2EExecutionState *state) const; @@ -242,10 +230,12 @@ class DecreeMonitor : public BaseLinuxMonitor { static bool isWriteFd(uint32_t fd); private: - void onInitializationComplete(S2EExecutionState *state); - target_ulong getTaskStructPtr(S2EExecutionState *state); + void onSegFault(S2EExecutionState *state, uint64_t pid, const S2E_LINUXMON_COMMAND_SEG_FAULT &data); + + void handleCommand(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize, void *cmd); + uint64_t getMaxValue(S2EExecutionState *state, klee::ref value); void handleSymbolicSize(S2EExecutionState *state, uint64_t pid, uint64_t safeLimit, klee::ref size, uint64_t sizeAddr); @@ -267,12 +257,7 @@ class DecreeMonitor : public BaseLinuxMonitor { const S2E_DECREEMON_COMMAND_HANDLE_SYMBOLIC_BUFFER &d); void handleSymbolicRandomBuffer(S2EExecutionState *state, uint64_t pid, const S2E_DECREEMON_COMMAND_HANDLE_SYMBOLIC_BUFFER &d); - void handleCopyToUser(S2EExecutionState *state, uint64_t pid, const S2E_DECREEMON_COMMAND_COPY_TO_USER &d); - void handleUpdateMemoryMap(S2EExecutionState *state, uint64_t pid, - const S2E_DECREEMON_COMMAND_UPDATE_MEMORY_MAP &d); void handleSetParams(S2EExecutionState *state, uint64_t pid, S2E_DECREEMON_COMMAND_SET_CB_PARAMS &d); - void handleInit(S2EExecutionState *state, const S2E_DECREEMON_COMMAND_INIT &d); - void handleTaskSwitch(S2EExecutionState *state, const S2E_DECREEMON_COMMAND &cmd); }; } // namespace plugins diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp index f17c83b28..b4dc77799 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp @@ -53,7 +53,8 @@ S2E_DEFINE_PLUGIN(CGCInterface, "CGC interface plugin", "", "ModuleExecutionDete "ControlFlowGraph", "SeedSearcher", "CallSiteMonitor", "TranslationBlockCoverage"); void CGCInterface::initialize() { - m_monitor = s2e()->getPlugin(); + m_monitor = s2e()->getPlugin(); + m_decree = s2e()->getPlugin(); m_detector = s2e()->getPlugin(); m_procDetector = s2e()->getPlugin(); m_povGenerator = s2e()->getPlugin(); @@ -81,7 +82,7 @@ void CGCInterface::initialize() { m_povGenerator->onRandomInputFork.connect(sigc::mem_fun(*this, &CGCInterface::onRandomInputFork), fsigc::signal_base::HIGHEST_PRIORITY); - m_monitor->onRandom.connect(sigc::mem_fun(*this, &CGCInterface::onRandom)); + m_decree->onRandom.connect(sigc::mem_fun(*this, &CGCInterface::onRandom)); m_exploitGenerator->onPovReady.connect(sigc::mem_fun(*this, &CGCInterface::onPovReady)); @@ -100,7 +101,6 @@ void CGCInterface::initialize() { } void CGCInterface::onRandom(S2EExecutionState *state, uint64_t pid, const std::vector> &data) { - std::string name; if (!m_monitor->getProcessName(state, pid, name)) { return; diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h index 31908a572..8c0e4ed2d 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -71,7 +73,8 @@ class CGCInterface : public Plugin { using seconds = std::chrono::seconds; friend class CGCInterfaceState; - DecreeMonitor *m_monitor; + LinuxMonitor *m_monitor; + DecreeMonitor *m_decree; ModuleExecutionDetector *m_detector; ProcessExecutionDetector *m_procDetector; pov::DecreePovGenerator *m_povGenerator; diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp index 313fa0773..5d3ccdb0f 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp @@ -119,13 +119,9 @@ void Recipe::initialize() { void Recipe::injectSegFault(S2EExecutionState *state) { LinuxMonitor *linuxMonitor = dynamic_cast(m_monitor); if (linuxMonitor) { - linuxMonitor->onSegFault.emit(state, m_monitor->getPid(state), state->regs()->getPc()); - return; - } - - DecreeMonitor *decreeMonitor = dynamic_cast(m_monitor); - if (decreeMonitor) { - decreeMonitor->onSegFault.emit(state, m_monitor->getPid(state), state->regs()->getPc()); + S2E_LINUXMON_COMMAND_SEG_FAULT data = {0}; + data.pc = state->regs()->getPc(); + linuxMonitor->onSegFault.emit(state, m_monitor->getPid(state), data); return; } From 9ed30d6d749ffe90260755b4fb40eff7dae4d4b7 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:46:05 +0200 Subject: [PATCH 10/20] LinuxMonitor: moved kill process group option from DecreeMonitor Signed-off-by: Vitaly Chipounov --- .../OSMonitors/Linux/BaseLinuxMonitor.h | 1 + .../Plugins/OSMonitors/Linux/DecreeMonitor.cpp | 18 ------------------ .../Plugins/OSMonitors/Linux/DecreeMonitor.h | 1 - .../Plugins/OSMonitors/Linux/LinuxMonitor.cpp | 6 ++++++ 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h index 104fcb3a0..8923a2166 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/BaseLinuxMonitor.h @@ -98,6 +98,7 @@ class BaseLinuxMonitor : public OSMonitor, public IPluginInvoker { /// Terminate if a segment fault occurs bool m_terminateOnSegfault; + bool m_terminateProcessGroupOnSegfault; uint64_t m_commandVersion; uint64_t m_commandSize; diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp index 2dddf5c5e..526ce251b 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.cpp @@ -104,7 +104,6 @@ void DecreeMonitor::initialize() { m_invokeOriginalSyscalls = cfg->getBool(getConfigKey() + ".invokeOriginalSyscalls", false); m_printOpcodeOffsets = cfg->getBool(getConfigKey() + ".printOpcodeOffsets", false); - m_terminateProcessGroupOnSegfault = cfg->getBool(getConfigKey() + ".terminateProcessGroupOnSegfault", false); m_concolicMode = cfg->getBool(getConfigKey() + ".concolicMode", false); m_logWrittenData = cfg->getBool(getConfigKey() + ".logWrittenData", true); m_handleSymbolicAllocateSize = cfg->getBool(getConfigKey() + ".handleSymbolicAllocateSize", false); @@ -717,23 +716,6 @@ void DecreeMonitor::onSegFault(S2EExecutionState *state, uint64_t pid, const S2E m_timeToFirstSegfault = difftime(now, m_startTime); m_firstSegfault = false; } - - getWarningsStream(state) << "received segfault" - << " pagedir=" << hexval(state->regs()->getPageDir()) << " pid=" << hexval(pid) - << " pc=" << hexval(data.pc) << " addr=" << hexval(data.address) << "\n"; - - // Don't switch state until it finishes and gets killed by bootstrap - // Need to print a message here to avoid confusion and needless debugging, - // wondering why the searcher doesn't work anymore. - getDebugStream(state) << "Blocking searcher until state is terminated\n"; - state->setStateSwitchForbidden(true); - - state->disassemble(getDebugStream(state), data.pc, 256); - - if (m_terminateProcessGroupOnSegfault) { - getWarningsStream(state) << "Terminating process group: received segfault\n"; - killpg(0, SIGTERM); - } } void DecreeMonitor::handleOpcodeInvocation(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize) { diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h index e00e6b2de..77ed5249d 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/DecreeMonitor.h @@ -144,7 +144,6 @@ class DecreeMonitor : public Plugin, public IPluginInvoker { uint64_t m_symbolicReadLimitCount; uint64_t m_maxReadLimitCount; - bool m_terminateProcessGroupOnSegfault; bool m_concolicMode; bool m_logWrittenData; bool m_handleSymbolicAllocateSize; diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp index 31a875b26..66b230580 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Linux/LinuxMonitor.cpp @@ -62,6 +62,7 @@ void LinuxMonitor::initialize() { } m_terminateOnSegfault = cfg->getBool(getConfigKey() + ".terminateOnSegfault", true); + m_terminateProcessGroupOnSegfault = cfg->getBool(getConfigKey() + ".terminateProcessGroupOnSegfault", false); m_terminateOnTrap = cfg->getBool(getConfigKey() + ".terminateOnTrap", true); m_commandSize = sizeof(S2E_LINUXMON_COMMAND); @@ -98,6 +99,11 @@ void LinuxMonitor::handleSegfault(S2EExecutionState *state, const S2E_LINUXMON_C getDebugStream(state) << "Terminating state: received segfault\n"; s2e()->getExecutor()->terminateState(*state, "Segfault"); } + + if (m_terminateProcessGroupOnSegfault) { + getWarningsStream(state) << "Terminating process group: received segfault\n"; + killpg(0, SIGTERM); + } } void LinuxMonitor::handleProcessExit(S2EExecutionState *state, const S2E_LINUXMON_COMMAND &cmd) { From 535733a650db797666bc553241219b4bc6f4e99f Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 19 May 2024 00:46:43 +0200 Subject: [PATCH 11/20] DecreePovGenerator: removed xml format from decree pov generator It's obsolete, the tool chain doesn't work anymore. The C format is much more flexible. Signed-off-by: Vitaly Chipounov --- .../VulnerabilityAnalysis/CGCInterface.cpp | 26 +- .../VulnerabilityAnalysis/CGCInterface.h | 4 +- .../DecreePovGenerator.cpp | 347 ++++-------------- .../DecreePovGenerator.h | 11 +- 4 files changed, 92 insertions(+), 296 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp index b4dc77799..b557ae862 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.cpp @@ -236,8 +236,8 @@ std::string CGCInterface::constraintsToJsonFile(S2EExecutionState *state) { } // The server will decide what to do with the test case (verify, send to db, etc.) -void CGCInterface::sendTestcase(S2EExecutionState *state, const std::string &xmlPovPath, const std::string &cPovPath, - TestCaseType tcType, const PovOptions &opt, const std::string &recipeName) { +void CGCInterface::sendTestcase(S2EExecutionState *state, const std::string &cPovPath, TestCaseType tcType, + const PovOptions &opt, const std::string &recipeName) { // This ensures that we generate unique file names for coverage, constraints, etc. // This is important, because sendTestcase may be called several times for the same // state and files could be overwritten before the service had a chance to read them. @@ -297,7 +297,6 @@ void CGCInterface::sendTestcase(S2EExecutionState *state, const std::string &xml data.push_back(std::make_pair("fault_address", QOBJECT(qnum_from_int(opt.m_faultAddress)))); - data.push_back(std::make_pair("xml_testcase_filename", QOBJECT(qstring_from_str(xmlPovPath.c_str())))); data.push_back(std::make_pair("c_testcase_filename", QOBJECT(qstring_from_str(cPovPath.c_str())))); data.push_back(std::make_pair("pov_type", QOBJECT(qnum_from_int(opt.m_type)))); @@ -311,32 +310,27 @@ void CGCInterface::sendTestcase(S2EExecutionState *state, const std::string &xml testCaseIndex++; } -static bool GetXmlCFiles(const std::vector &filePaths, std::string &xmlFilePath, std::string &cFilePath) { - uint8_t mask = 0; - +static bool GetCFiles(const std::vector &filePaths, std::string &cFilePath) { for (const auto &fp : filePaths) { - if (fp.find(".xml") != std::string::npos) { - xmlFilePath = fp; - mask |= 1; - } else if (fp.find(".c") != std::string::npos) { + if (fp.find(".c") != std::string::npos) { cFilePath = fp; - mask |= 2; + return true; } } - return mask == 3; + return false; } void CGCInterface::onPovReady(S2EExecutionState *state, const PovOptions &opt, const std::string &recipeName, const std::vector &filePaths, TestCaseType tcType) { - std::string xmlFilePath, cFilePath; - if (!GetXmlCFiles(filePaths, xmlFilePath, cFilePath)) { - getWarningsStream(state) << "Could not find xml/c files in the generated files\n"; + std::string cFilePath; + if (!GetCFiles(filePaths, cFilePath)) { + getWarningsStream(state) << "Could not find c files in the generated files\n"; return; } - sendTestcase(state, xmlFilePath, cFilePath, tcType, opt, recipeName); + sendTestcase(state, cFilePath, tcType, opt, recipeName); } bool CGCInterface::updateCoverage(S2EExecutionState *state) { diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h index 8c0e4ed2d..9c215440f 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/CGCInterface.h @@ -122,8 +122,8 @@ class CGCInterface : public Plugin { void onPovReady(S2EExecutionState *state, const PovOptions &opt, const std::string &recipeName, const std::vector &filePaths, TestCaseType tcType); - void sendTestcase(S2EExecutionState *state, const std::string &xmlPovPath, const std::string &cPovPath, - TestCaseType tcType, const PovOptions &opt, const std::string &recipeName = ""); + void sendTestcase(S2EExecutionState *state, const std::string &cPovPath, TestCaseType tcType, const PovOptions &opt, + const std::string &recipeName = ""); void constraintsToJson(S2EExecutionState *state, std::stringstream &output); std::string constraintsToJsonFile(S2EExecutionState *state); diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp index 4cbcee2b1..75c9f83c5 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp @@ -77,15 +77,10 @@ class POVStaticEntry { virtual ~POVStaticEntry() { } - virtual void getXmlString(std::stringstream &ss) const {}; virtual void getCString(std::stringstream &ss) const {}; - virtual void getString(std::stringstream &ss, bool xmlFormat) const { - if (xmlFormat) { - getXmlString(ss); - } else { - getCString(ss); - } + virtual void getString(std::stringstream &ss) const { + getCString(ss); } }; @@ -95,18 +90,12 @@ class POVEntry { virtual ~POVEntry() { } - virtual void getXmlString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, - bool debug) const {}; virtual void getCString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, bool debug) const {}; - virtual void getString(std::stringstream &ss, bool xmlFormat, const Assignment &solution, - const VariableRemapping &remapping, bool debug) const { - if (xmlFormat) { - getXmlString(ss, solution, remapping, debug); - } else { - getCString(ss, solution, remapping, debug); - } + virtual void getString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, + bool debug) const { + getCString(ss, solution, remapping, debug); } }; @@ -134,17 +123,9 @@ class POVFork : public POVEntry { return new POVFork(*this); } - void getXmlString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, - bool debug) const { - bool outcome = evalBool(solution, m_condition); - - ss << "\n\n"; - } - void getCString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, bool debug) const { - // not needed in C pov + ss << "// Fork " << hexval(m_pc) << "\n\n"; } }; @@ -172,52 +153,13 @@ class POVRandom : public POVEntry { return new POVRandom(*this); } - void getXmlString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, - bool debug) const { - ss << "\n\n"; - } - - void getCString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, - bool debug) const { - // not needed in C pov - } -}; - -class POVDeclaration : public POVStaticEntry { -private: - std::string m_name; - std::string m_var; - unsigned m_begin; - unsigned m_end; - -public: - POVDeclaration(const std::string &newVarName, const std::string &existingVar, unsigned b, unsigned e) - : m_name(newVarName), m_var(existingVar), m_begin(b), m_end(e) { - } - - POVStaticEntry *clone() { - return new POVDeclaration(*this); - } - - void getXmlString(std::stringstream &ss) const { - ss << "\n" - << " " << m_name << "\n" - << " \n" - << " \n" - << " " << m_var << "\n" - << " " << m_begin << "\n" - << " " << (m_end + 1) << "\n" - << " \n" - << " \n" - << "\n"; - } - - void getCString(std::stringstream &ss) const { - s2e_warn_assert(nullptr, false, "POVDeclaration must not be used in C PoV"); + ss << " */\n\n"; } }; @@ -277,15 +219,15 @@ class POVEntryWrite : public POVEntryReadWrite { return "g_var_" + name; } - void getString(std::stringstream &ss, bool xmlFormat, const Assignment &solution, - const VariableRemapping &remapping, bool debug) const { + void getString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, + bool debug) const { uint32_t concreteSize = evalInt(solution, m_sizeExpr); s2e_assert(nullptr, concreteSize <= m_input.size(), "Symbolic size expression is solved to have invalid concrete value"); - ss << (xmlFormat ? "\n" : "\n"); + ss << "\n"; if (!concreteSize) { ss << "\n"; @@ -295,19 +237,15 @@ class POVEntryWrite : public POVEntryReadWrite { const int allocThreshold = 4096; bool allocBuf = concreteSize > allocThreshold; - if (xmlFormat) { - ss << "\n"; + ss << " do {\n"; + ss << " size_t count = " << concreteSize << ";\n"; + if (allocBuf) { + ss << " uint8_t *buf = nullptr;\n"; + ss << " allocate(count, 0, (void**) &buf);\n"; } else { - ss << " do {\n"; - ss << " size_t count = " << concreteSize << ";\n"; - if (allocBuf) { - ss << " uint8_t *buf = nullptr;\n"; - ss << " allocate(count, 0, (void**) &buf);\n"; - } else { - ss << " uint8_t buf[count];\n"; - } - ss << " uint8_t *p = buf;\n"; + ss << " uint8_t buf[count];\n"; } + ss << " uint8_t *p = buf;\n"; unsigned count = 0; @@ -319,28 +257,15 @@ class POVEntryWrite : public POVEntryReadWrite { auto remappedVar = remapping.find(name); if (remappedVar != remapping.end()) { - if (xmlFormat) { - ss << " " << remappedVar->second << ""; - - // This is for the fuzzer. It wants concrete value for the nonce. - // This is useful because DecreeMonitor uses a fixed rng seed. - // Comment location is important. - ss << " "; - } else { - ss << " *p++ = " << getCVarName(remappedVar->second) << ";"; - } + ss << " *p++ = " << getCVarName(remappedVar->second) << ";"; } else { - if (xmlFormat) { - ss << " " << charval(byte) << ""; - } else { - ss << " *p++ = " << cbyte(byte) << ";"; - } + ss << " *p++ = " << cbyte(byte) << ";"; } if (debug && !isa(e)) { - ss << (xmlFormat ? " " : ""); + ss << ""; } ss << "\n"; @@ -351,15 +276,11 @@ class POVEntryWrite : public POVEntryReadWrite { } } - if (xmlFormat) { - ss << "\n"; - } else { - ss << " transmit_all(STDOUT, buf, count);\n"; - if (allocBuf) { - ss << " deallocate(buf, count);\n"; - } - ss << " } while (0);\n"; + ss << " transmit_all(STDOUT, buf, count);\n"; + if (allocBuf) { + ss << " deallocate(buf, count);\n"; } + ss << " } while (0);\n"; ss << "\n"; @@ -393,14 +314,14 @@ class POVEntryRead : public POVEntryReadWrite { return false; } - void getString(std::stringstream &ss, bool xmlFormat, const Assignment &solution, - const VariableRemapping &remapping, bool debug) const { + void getString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, + bool debug) const { uint32_t concreteSize = evalInt(solution, m_sizeExpr); s2e_assert(nullptr, concreteSize <= m_output.size(), "Symbolic size expression is solved to have invalid concrete value"); if (debug) { - ss << (xmlFormat ? "\n" : "\n"); + ss << "\n"; } - ss << (xmlFormat ? "\n" : "\n"); + ss << "\n"; if (!concreteSize) { ss << "\n"; @@ -426,11 +347,7 @@ class POVEntryRead : public POVEntryReadWrite { } if (!hasNonces()) { - if (xmlFormat) { - ss << "" << concreteSize << "\n"; - } else { - ss << " receive_null(STDIN, " << concreteSize << ");\n"; - } + ss << " receive_null(STDIN, " << concreteSize << ");\n"; ss << "\n"; return; } @@ -442,26 +359,10 @@ class POVEntryRead : public POVEntryReadWrite { ref re = dyn_cast(e); auto &root = re->getUpdates()->getRoot(); - if (xmlFormat) { - ss << "\n"; - ss << " 1\n"; - ss << " " << root->getName() << " "; - - // This comment is required by the fuzzer - ss << " "; - - ss << " \n"; - ss << "\n"; - } else { - ss << " uint8_t g_var_" << root->getName() << " = 0;\n"; - ss << " receive_all(STDIN, &g_var_" << root->getName() << ", 1);\n"; - } + ss << " uint8_t g_var_" << root->getName() << " = 0;\n"; + ss << " receive_all(STDIN, &g_var_" << root->getName() << ", 1);\n"; } else { - if (xmlFormat) { - ss << "1\n"; - } else { - ss << " receive_null(STDIN, 1);\n"; - } + ss << " receive_null(STDIN, 1);\n"; } count++; @@ -488,10 +389,6 @@ class POVEntryDelay : public POVStaticEntry { return new POVEntryDelay(*this); } - void getXmlString(std::stringstream &ss) const { - ss << "" << m_timeout << "\n\n"; - } - void getCString(std::stringstream &ss) const { ss << " delay(" << m_timeout << ");\n\n"; } @@ -536,10 +433,10 @@ class DecreePovGeneratorState : public PluginState { m_entries.push_back(entry); } - void getString(std::stringstream &ss, bool xmlFormat, const Assignment &solution, - const VariableRemapping &remapping, bool debug) const { + void getString(std::stringstream &ss, const Assignment &solution, const VariableRemapping &remapping, + bool debug) const { for (unsigned i = 0; i < m_entries.size(); ++i) { - m_entries[i]->getString(ss, xmlFormat, solution, remapping, debug); + m_entries[i]->getString(ss, solution, remapping, debug); } } @@ -689,15 +586,6 @@ class DecreePovGeneratorState : public PluginState { } }; -const std::string DecreePovGenerator::XML_HEADER = "\n" - "\n" - "\n" - " service\n" - " \n\n"; - -const std::string DecreePovGenerator::XML_FOOTER = " \n" - "\n"; - const std::string DecreePovGenerator::C_HEADER = "#include \n" "\n" @@ -1024,7 +912,7 @@ void DecreePovGenerator::unmergeSelects(S2EExecutionState *state, const Assignme } } -std::string DecreePovGenerator::generatePoV(bool xmlFormat, uint64_t seedIndex, const DecreePovGeneratorState *plgState, +std::string DecreePovGenerator::generatePoV(uint64_t seedIndex, const DecreePovGeneratorState *plgState, const PovOptions &opt, const VariableRemapping &remapping, const Assignment &solution, const ConstraintManager &constraints) { // TODO: check if this is really needed @@ -1032,24 +920,23 @@ std::string DecreePovGenerator::generatePoV(bool xmlFormat, uint64_t seedIndex, std::stringstream ss; - ss << (xmlFormat ? XML_HEADER : C_HEADER); + ss << C_HEADER; if ((int) seedIndex != -1) { - ss << (xmlFormat ? "\n" : "\n"); + ss << "\n"; ss << "\n"; } - generateNegotiate(ss, xmlFormat, opt); - plgState->getString(ss, xmlFormat, solution, remapping, getLogLevel() <= LOG_DEBUG); - delay.getString(ss, xmlFormat); - generateReadSecret(ss, xmlFormat, opt); - ss << (xmlFormat ? XML_FOOTER : C_FOOTER); + generateNegotiate(ss, opt); + plgState->getString(ss, solution, remapping, getLogLevel() <= LOG_DEBUG); + delay.getString(ss); + generateReadSecret(ss, opt); + ss << C_FOOTER; return ss.str(); } -void DecreePovGenerator::generatePoV(S2EExecutionState *state, const PovOptions &opt, std::string &xmlPov, - std::string &cPov) { +void DecreePovGenerator::generatePoV(S2EExecutionState *state, const PovOptions &opt, std::string &cPov) { DECLARE_PLUGINSTATE_CONST(DecreePovGeneratorState, state); auto solution = Assignment::create(); @@ -1071,140 +958,56 @@ void DecreePovGenerator::generatePoV(S2EExecutionState *state, const PovOptions seedIndex = m_seedSearcher->getSubtreeSeedIndex(state); } - xmlPov = generatePoV(true, seedIndex, plgState, opt, remapping, *solution, state->constraints()); - cPov = generatePoV(false, seedIndex, plgState, opt, remapping, *solution, state->constraints()); + cPov = generatePoV(seedIndex, plgState, opt, remapping, *solution, state->constraints()); } bool DecreePovGenerator::generatePoV(S2EExecutionState *state, const PovOptions &opt, const std::string &filePrefix, std::vector &filePaths) { - std::string xmlPov, cPov; - generatePoV(state, opt, xmlPov, cPov); - if (!xmlPov.length() && !cPov.length()) { + std::string cPov; + generatePoV(state, opt, cPov); + if (!cPov.length()) { getWarningsStream(state) << "Failed to generate PoV\n"; return false; } - auto xmlFn = writeToFile(state, opt, filePrefix, "xml", xmlPov.c_str(), xmlPov.size()); - filePaths.push_back(xmlFn); - auto cFn = writeToFile(state, opt, filePrefix, "c", cPov.c_str(), cPov.size()); filePaths.push_back(cFn); return true; } -void DecreePovGenerator::generateNegotiate(std::stringstream &ss, bool xmlFormat, const PovOptions &opt) { +void DecreePovGenerator::generateNegotiate(std::stringstream &ss, const PovOptions &opt) { if (opt.m_type == POV_GENERAL) { /** * This is useful to generate crash povs, that don't have an exploit. * Masks should have enough bits so that cb-test does not complain. */ - if (xmlFormat) { - ss << "\n"; - ss << "\n"; - ss << " \n"; - ss << " " << 0xffffffff << "\n"; - ss << " " << 0xffffffff << "\n"; - ss << " " << 0 << "\n"; - ss << " \n"; - ss << "\n"; - } else { - ss << " // dummy negotiation\n"; - ss << " type1_vals g_neg_t1 = { 0 };\n"; - ss << " type1_negotiate(0xffffffff, 0xffffffff, 0, &g_neg_t1);\n"; - } + ss << " // dummy negotiation\n"; + ss << " type1_vals g_neg_t1 = { 0 };\n"; + ss << " type1_negotiate(0xffffffff, 0xffffffff, 0, &g_neg_t1);\n"; } else if (opt.m_type == POV_TYPE1) { - if (xmlFormat) { - ss << "\n"; - ss << " \n"; - ss << " " << hexval(opt.m_ipMask) << "\n"; - ss << " " << hexval(opt.m_regMask) << "\n"; - ss << " " << opt.m_regNum << "\n"; - ss << " \n"; - ss << "\n\n"; - - /** - * Slice variables into bytes so that it is easier to - * reuse them in the pov (read elements often have only one byte). - */ - - for (unsigned i = 0; i < 4; ++i) { - std::stringstream ss1; - ss1 << VARNAME_PC << "[" << i << "]"; - POVDeclaration decl(ss1.str(), "TYPE1_IP", i, i); - decl.getString(ss, true); - } - - for (unsigned i = 0; i < 4; ++i) { - std::stringstream ss1; - ss1 << VARNAME_GP << "[" << i << "]"; - POVDeclaration decl(ss1.str(), "TYPE1_REG", i, i); - decl.getString(ss, true); - } - } else { - ss << " type1_vals g_neg_t1 = { 0 };\n"; - ss << " type1_negotiate(" << hexval(opt.m_ipMask) << ", " << hexval(opt.m_regMask) << ", " << opt.m_regNum - << ", " - << "&g_neg_t1);\n"; - } + ss << " type1_vals g_neg_t1 = { 0 };\n"; + ss << " type1_negotiate(" << hexval(opt.m_ipMask) << ", " << hexval(opt.m_regMask) << ", " << opt.m_regNum + << ", " + << "&g_neg_t1);\n"; } else if (opt.m_type == POV_TYPE2) { - if (xmlFormat) { - ss << "\n"; - ss << " \n"; - ss << "\n\n"; - - /** - * Slice variables into bytes so that it is easier to - * reuse them in the pov (read elements often have only one byte). - */ - - for (unsigned i = 0; i < 4; ++i) { - std::stringstream ss1; - ss1 << VARNAME_ADDR << "[" << i << "]"; - POVDeclaration decl(ss1.str(), "TYPE2_ADDR", i, i); - decl.getString(ss, true); - } - - for (unsigned i = 0; i < 4; ++i) { - std::stringstream ss1; - ss1 << VARNAME_SIZE << "[" << i << "]"; - POVDeclaration decl(ss1.str(), "TYPE2_LENGTH", i, i); - decl.getString(ss, true); - } - } else { - ss << " type2_vals g_neg_t2 = { 0 };\n"; - ss << " type2_negotiate(&g_neg_t2);\n"; - } + ss << " type2_vals g_neg_t2 = { 0 };\n"; + ss << " type2_negotiate(&g_neg_t2);\n"; } ss << "\n"; } -void DecreePovGenerator::generateReadSecret(std::stringstream &ss, bool xmlFormat, const PovOptions &opt) { +void DecreePovGenerator::generateReadSecret(std::stringstream &ss, const PovOptions &opt) { if (opt.m_type == POV_TYPE2) { - if (xmlFormat) { - ss << "\n"; - ss << "" << opt.m_bytesBeforeSecret << "\n"; - ss << "\n"; - ss << "\n"; - ss << " TYPE2_LENGTH\n"; - ss << " \n"; - ss << " TYPE2_VALUE\n"; - ss << " \n"; - ss << " \n"; - ss << "\n"; - ss << "\n"; - ss << "TYPE2_VALUE\n"; - } else { - ss << " // skip bytes before secret\n"; - ss << " receive_null(STDIN, " << opt.m_bytesBeforeSecret << ");\n"; - ss << "\n"; - ss << " do {\n"; - ss << " uint8_t data[g_neg_t2.read_size];\n"; - ss << " receive_all(STDIN, data, sizeof(data));\n"; - ss << " transmit_all(NEG_FD, data, sizeof(data));\n"; - ss << " } while (0);\n"; - } + ss << " // skip bytes before secret\n"; + ss << " receive_null(STDIN, " << opt.m_bytesBeforeSecret << ");\n"; + ss << "\n"; + ss << " do {\n"; + ss << " uint8_t data[g_neg_t2.read_size];\n"; + ss << " receive_all(STDIN, data, sizeof(data));\n"; + ss << " transmit_all(NEG_FD, data, sizeof(data));\n"; + ss << " } while (0);\n"; } ss << " \n"; diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.h b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.h index 39d182669..4eb7b5b0f 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.h +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.h @@ -53,18 +53,17 @@ class DecreePovGenerator : public PovGenerator { AddressSet m_povAddresses; // addresses at which POVs were generated unsigned m_numPOVs; // number of POVs generated so far - static const std::string XML_HEADER, XML_FOOTER; static const std::string C_HEADER, C_FOOTER; DecreeMonitor *m_monitor; ProcessExecutionDetector *m_detector; ModuleExecutionDetector *m_modules; seeds::SeedSearcher *m_seedSearcher; - std::string generatePoV(bool xmlFormat, uint64_t seedIndex, const DecreePovGeneratorState *plgState, - const PovOptions &opt, const VariableRemapping &remapping, const klee::Assignment &solution, + std::string generatePoV(uint64_t seedIndex, const DecreePovGeneratorState *plgState, const PovOptions &opt, + const VariableRemapping &remapping, const klee::Assignment &solution, const klee::ConstraintManager &constraints); - void generatePoV(S2EExecutionState *state, const PovOptions &opt, std::string &xmlPov, std::string &cPov); + void generatePoV(S2EExecutionState *state, const PovOptions &opt, std::string &cPov); public: DecreePovGenerator(S2E *s2e); @@ -98,8 +97,8 @@ class DecreePovGenerator : public PovGenerator { void onConcreteRead(S2EExecutionState *state, uint64_t pid, uint64_t fd, const std::vector &data); - void generateNegotiate(std::stringstream &ss, bool xmlFormat, const PovOptions &opt); - void generateReadSecret(std::stringstream &ss, bool xmlFormat, const PovOptions &opt); + void generateNegotiate(std::stringstream &ss, const PovOptions &opt); + void generateReadSecret(std::stringstream &ss, const PovOptions &opt); }; } // namespace pov } // namespace plugins From f9ce4cebc93b18ab1747cd01773334619e6ae349 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 00:42:41 +0200 Subject: [PATCH 12/20] DecreePovGenerator: fixed C template for new libpov Signed-off-by: Vitaly Chipounov --- .../DecreePovGenerator.cpp | 100 +++--------------- 1 file changed, 12 insertions(+), 88 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp index 75c9f83c5..be7c948f2 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp @@ -276,7 +276,7 @@ class POVEntryWrite : public POVEntryReadWrite { } } - ss << " transmit_all(STDOUT, buf, count);\n"; + ss << " transmit_all(STDOUT_FILENO, buf, count);\n"; if (allocBuf) { ss << " deallocate(buf, count);\n"; } @@ -347,7 +347,7 @@ class POVEntryRead : public POVEntryReadWrite { } if (!hasNonces()) { - ss << " receive_null(STDIN, " << concreteSize << ");\n"; + ss << " receive_null(STDIN_FILENO, " << concreteSize << ");\n"; ss << "\n"; return; } @@ -360,9 +360,9 @@ class POVEntryRead : public POVEntryReadWrite { auto &root = re->getUpdates()->getRoot(); ss << " uint8_t g_var_" << root->getName() << " = 0;\n"; - ss << " receive_all(STDIN, &g_var_" << root->getName() << ", 1);\n"; + ss << " receive_all(STDIN_FILENO, &g_var_" << root->getName() << ", 1);\n"; } else { - ss << " receive_null(STDIN, 1);\n"; + ss << " receive_null(STDIN_FILENO, 1);\n"; } count++; @@ -586,88 +586,12 @@ class DecreePovGeneratorState : public PluginState { } }; -const std::string DecreePovGenerator::C_HEADER = - "#include \n" - "\n" - "typedef unsigned int uint32_t;\n" - "typedef unsigned char uint8_t;\n" - "\n" - "#define MIN(a, b) ( (a) < (b) ? (a) : (b) )\n" - "#define GET_BYTE(v, i) ( ( ((uint32_t)(v)) >> ((i) * 8) ) & 0xFF )\n" - "\n" - "#define NEG_FD 3\n" - "\n" - "typedef struct type1_vals_ {\n" - " uint32_t ipval;\n" - " uint32_t regval;\n" - "} type1_vals;\n" - "\n" - "typedef struct type2_vals_ {\n" - " uint32_t region_addr;\n" - " uint32_t region_size;\n" - " uint32_t read_size;\n" - "} type2_vals;\n" - "\n" - "void transmit_all(int fd, void *buf, size_t count) {\n" - " size_t total = 0;\n" - " while (total < count) {\n" - " size_t s = 0;\n" - " transmit(fd, buf + total, count - total, &s);\n" - " total += s;\n" - " }\n" - "}\n" - "\n" - "void receive_all(int fd, void *buf, size_t count) {\n" - " size_t total = 0;\n" - " while (total < count) {\n" - " size_t s = 0;\n" - " receive(fd, buf + total, count - total, &s);\n" - " total += s;\n" - " }\n" - "}\n" - "\n" - "void receive_null(int fd, size_t count) {\n" - " uint8_t buf[256];\n" - " while (count) {\n" - " size_t s = MIN(count, sizeof(buf));\n" - " receive_all(fd, buf, s);\n" - " count -= s;\n" - " }\n" - "}\n" - "\n" - "void type1_negotiate(uint32_t ipmask, uint32_t regmask, uint32_t regnum, type1_vals *t1vals)\n" - "{\n" - " uint32_t povType = 1;\n" - " transmit_all(NEG_FD, &povType, sizeof(povType));\n" - " transmit_all(NEG_FD, &ipmask, sizeof(ipmask));\n" - " transmit_all(NEG_FD, ®mask, sizeof(regmask));\n" - " transmit_all(NEG_FD, ®num, sizeof(regnum));\n" - " receive_all(NEG_FD, t1vals, sizeof(*t1vals));\n" - "}\n" - "\n" - "void type2_negotiate(type2_vals *t2vals)\n" - "{\n" - " uint32_t povType = 2;\n" - " transmit_all(NEG_FD, &povType, sizeof(povType));\n" - " receive_all(NEG_FD, t2vals, sizeof(*t2vals));\n" - "}\n" - "\n" - "void delay(uint32_t msec) {\n" - " struct timeval timeout;\n" - " timeout.tv_sec = msec / 1000;\n" - " timeout.tv_usec = (msec % 1000) * 1000;\n" - " fdwait(STDIN, nullptr, nullptr, &timeout, nullptr);\n" - "}\n" - "\n" - "void memset(void *b, int c, size_t len)\n" - "{\n" - " for (int i = 0; i < len; i++ ) {\n" - " ((unsigned char*)b)[i] = c;\n" - " }\n" - "}\n" - "\n" - "int main(void)\n" - "{\n"; +const std::string DecreePovGenerator::C_HEADER = "#include \n" + "#include \n" + "#include \n" + "\n" + "int main(void)\n" + "{\n"; const std::string DecreePovGenerator::C_FOOTER = "}\n"; @@ -1001,11 +925,11 @@ void DecreePovGenerator::generateNegotiate(std::stringstream &ss, const PovOptio void DecreePovGenerator::generateReadSecret(std::stringstream &ss, const PovOptions &opt) { if (opt.m_type == POV_TYPE2) { ss << " // skip bytes before secret\n"; - ss << " receive_null(STDIN, " << opt.m_bytesBeforeSecret << ");\n"; + ss << " receive_null(STDIN_FILENO, " << opt.m_bytesBeforeSecret << ");\n"; ss << "\n"; ss << " do {\n"; ss << " uint8_t data[g_neg_t2.read_size];\n"; - ss << " receive_all(STDIN, data, sizeof(data));\n"; + ss << " receive_all(STDIN_FILENO, data, sizeof(data));\n"; ss << " transmit_all(NEG_FD, data, sizeof(data));\n"; ss << " } while (0);\n"; } From 1a41f26128f3abfda514d3712e7822c1b44bfca1 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 13:09:08 +0200 Subject: [PATCH 13/20] DecreePovGenerator: treat crashes without POV as Type1 POV with null mask Signed-off-by: Vitaly Chipounov --- .../Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp index be7c948f2..d46106070 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.cpp @@ -902,13 +902,12 @@ bool DecreePovGenerator::generatePoV(S2EExecutionState *state, const PovOptions void DecreePovGenerator::generateNegotiate(std::stringstream &ss, const PovOptions &opt) { if (opt.m_type == POV_GENERAL) { - /** - * This is useful to generate crash povs, that don't have an exploit. - * Masks should have enough bits so that cb-test does not complain. - */ + // This is useful to generate crash povs that don't have an exploit. + // The masks contain 0 because the PoV cannot control the registers. + // Note that this does not match the original CGC specs. ss << " // dummy negotiation\n"; ss << " type1_vals g_neg_t1 = { 0 };\n"; - ss << " type1_negotiate(0xffffffff, 0xffffffff, 0, &g_neg_t1);\n"; + ss << " type1_negotiate(0x0, 0x0, 0, &g_neg_t1);\n"; } else if (opt.m_type == POV_TYPE1) { ss << " type1_vals g_neg_t1 = { 0 };\n"; ss << " type1_negotiate(" << hexval(opt.m_ipMask) << ", " << hexval(opt.m_regMask) << ", " << opt.m_regNum From 400dbe2aaf59177f475646c4fa061f45e0c3d6b6 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 13:06:02 +0200 Subject: [PATCH 14/20] RegionMap: added getter for interval map Signed-off-by: Vitaly Chipounov --- libs2eplugins/src/s2e/Plugins/OSMonitors/Support/RegionMap.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/RegionMap.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/RegionMap.h index 8d98ab6d7..a9b3405a7 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/RegionMap.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/RegionMap.h @@ -42,6 +42,10 @@ template class RegionMap { using RM = llvm::IntervalMapWrapper; public: + const llvm::IntervalMapWrapper &map() const { + return m_map; + } + void add(uint64_t start, uint64_t end, T value) { assert(start < end); m_map.insert(start, end - 1, value); From 01c0d9eea0e329aab2448d1eefb4bedc65b53c5c Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 13:07:03 +0200 Subject: [PATCH 15/20] MemUtils: optimized page finder Return regions instead of individual pages. Signed-off-by: Vitaly Chipounov --- .../src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp | 6 ++---- libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp index 7dd2eada5..f1107f790 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp @@ -164,7 +164,7 @@ void MemUtils::findSequencesOfSymbolicData(S2EExecutionState *state, uint64_t pi } void MemUtils::findMemoryPages(S2EExecutionState *state, uint64_t pid, bool mustBeWritable, bool mustBeExecutable, - std::unordered_set &pages) { + RegionMap &pages) { auto lambda = [&](uint64_t start, uint64_t end, MemoryMapRegionType type) { bool doAdd = false; @@ -177,9 +177,7 @@ void MemUtils::findMemoryPages(S2EExecutionState *state, uint64_t pid, bool must } if (doAdd) { - for (uint64_t s = start; s < end; s += TARGET_PAGE_SIZE) { - pages.insert(s & TARGET_PAGE_MASK); - } + pages.add(start, end, type); } return true; diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h index 476cdf44a..62644dd18 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -116,7 +117,7 @@ class MemUtils : public Plugin { std::vector &symbolicSequences); void findMemoryPages(S2EExecutionState *state, uint64_t pid, bool mustBeWritable, bool mustBeExecutable, - std::unordered_set &pages); + RegionMap &pages); }; } // namespace plugins } // namespace s2e From 42e614e0a8276aba9442ebf1ed52cef6e4726bab Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 13:07:38 +0200 Subject: [PATCH 16/20] MemUtils: support read-only pages in page finder Signed-off-by: Vitaly Chipounov --- libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp index f1107f790..313a7bb8f 100644 --- a/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp +++ b/libs2eplugins/src/s2e/Plugins/OSMonitors/Support/MemUtils.cpp @@ -168,6 +168,10 @@ void MemUtils::findMemoryPages(S2EExecutionState *state, uint64_t pid, bool must auto lambda = [&](uint64_t start, uint64_t end, MemoryMapRegionType type) { bool doAdd = false; + if (!mustBeWritable && (type & MM_READ)) { + doAdd = true; + } + if (mustBeWritable && (type & MM_WRITE)) { doAdd = true; } From eac745cf7e61e27b4a864b52e680464cccf9084f Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 13:08:32 +0200 Subject: [PATCH 17/20] Recipe: optimized page lookup Signed-off-by: Vitaly Chipounov --- .../VulnerabilityAnalysis/Recipe/Recipe.cpp | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp index 5d3ccdb0f..5ae308ad0 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp @@ -361,24 +361,31 @@ void Recipe::tryRecipesOnICTI(S2EExecutionState *state, const ref ®Expr void Recipe::suppressExecutionWithInvalidAddress(S2EExecutionState *state, ref addr, bool isWrite, int accessSize) { - std::unordered_set validPages; + RegionMap validPages; m_memutils->findMemoryPages(state, m_monitor->getPid(state), isWrite, false, validPages); // Allow read/write to one page below stack uint64_t stackPage = state->regs()->getSp() & TARGET_PAGE_MASK; - if (validPages.find(stackPage) != validPages.end() && stackPage > TARGET_PAGE_SIZE) { - validPages.insert(stackPage - TARGET_PAGE_SIZE); + uint64_t stackStart, stackEnd; + MemoryMapRegionType stackType; + if (validPages.lookup(stackPage, stackStart, stackEnd, stackType)) { + validPages.add(stackStart - TARGET_PAGE_SIZE, stackStart, stackType); } // Address is valid if access does not cause segfault // 0 || ( min0 <= addr && addr <= max0) || ( min1 <= addr && addr <= max1) || ... ref addrIsValid = E_CONST(0, Expr::Bool); - foreach2 (it, validPages.begin(), validPages.end()) { + + RegionMapIteratorCb lambda = [&](uint64_t start, uint64_t end, MemoryMapRegionType type) { // TODO: max = (*it + (TARGET_PAGE_SIZE - accessSize)) for last page of contiguous region - ref min = E_CONST(*it, state->getPointerWidth()); - ref max = E_CONST(*it + (TARGET_PAGE_SIZE - 1), state->getPointerWidth()); + ref min = E_CONST(start, state->getPointerWidth()); + ref max = E_CONST(end - 1, state->getPointerWidth()); addrIsValid = E_OR(addrIsValid, E_AND(E_GE(addr, min), E_LE(addr, max))); - } + + return true; + }; + + validPages.iterate(lambda); S2EExecutor::StatePair sp = s2e()->getExecutor()->forkCondition(state, addrIsValid); @@ -389,8 +396,11 @@ void Recipe::suppressExecutionWithInvalidAddress(S2EExecutionState *state, refdump(state); getDebugStream(invalidState) << "Valid pages:\n"; - for (auto v : validPages) { - getDebugStream(invalidState) << " " << hexval(v) << "\n"; + + const auto ®map = validPages.map(); + for (auto rit = regmap.begin(); rit != regmap.end(); ++rit) { + getDebugStream(invalidState) << " " << hexval(rit.start()) << "-" << hexval(rit.stop()) << " - " << *rit + << "\n"; } ref eval = invalidState->concolics->evaluate(addr); @@ -987,7 +997,7 @@ bool Recipe::getCurrentModule(S2EExecutionState *state, uint64_t eip, ModuleDesc bool Recipe::applyPreconditions(S2EExecutionState *state, PovType type, const StateConditions &sc, const Preconditions &p, RecipeConditions &recipeConditions) { - std::unordered_set executablePages; + RegionMap executablePages; m_memutils->findMemoryPages(state, m_monitor->getPid(state), false, true, executablePages); Preconditions simple; @@ -1001,7 +1011,7 @@ bool Recipe::applyPreconditions(S2EExecutionState *state, PovType type, const St ref reg = getRegExpr(state, sc, it->left->reg()); uint64_t regVal = dyn_cast(reg)->getZExtValue(); - if (executablePages.find(regVal & TARGET_PAGE_MASK) == executablePages.end()) { + if (executablePages.lookup(regVal & TARGET_PAGE_MASK) == MM_NONE) { getDebugStream(state) << "Precondition " << *it << " is not satisfiable\n"; return false; } From 7e978ad1b35ec2e02140b9ad6e28722d4b959e10 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 20:44:41 +0200 Subject: [PATCH 18/20] RecipeDescriptor: fixed plugin lookup Signed-off-by: Vitaly Chipounov --- .../src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp | 2 +- .../VulnerabilityAnalysis/Recipe/RecipeDescriptor.cpp | 6 +++--- .../Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp index 5ae308ad0..b75c357e7 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/Recipe.cpp @@ -1078,7 +1078,7 @@ bool Recipe::tryRecipes(S2EExecutionState *state, const StateConditions &sc, Rec } } - if (!recipe.isUsable(state, m_monitor)) { + if (!recipe.isUsable(state)) { getDebugStream(state) << "Recipe " << recipeName << " is not compatible with the current state\n"; continue; } diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.cpp b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.cpp index b115d91fc..07ae66065 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.cpp +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.cpp @@ -348,7 +348,7 @@ bool RecipeDescriptor::mustTryRecipe(const RecipeDescriptor &recipe, const std:: return false; } -bool RecipeDescriptor::isUsable(S2EExecutionState *state, OSMonitor *monitor) const { +bool RecipeDescriptor::isUsable(S2EExecutionState *state) const { unsigned ptrSize = state->getPointerSize(); if (settings.arch == RECIPE_AMD64) { @@ -362,8 +362,8 @@ bool RecipeDescriptor::isUsable(S2EExecutionState *state, OSMonitor *monitor) co } if (settings.platform == RECIPE_DECREE) { - DecreeMonitor *dm = dynamic_cast(monitor); - if (!dm) { + auto plugin = g_s2e->getPlugin("DecreeMonitor"); + if (!plugin) { return false; } } diff --git a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.h b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.h index e4abd54e8..f1609a3a3 100644 --- a/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.h +++ b/libs2eplugins/src/s2e/Plugins/VulnerabilityAnalysis/Recipe/RecipeDescriptor.h @@ -360,7 +360,7 @@ struct RecipeDescriptor { static bool mustTryRecipe(const RecipeDescriptor &recipe, const std::string &recipeName, const StateConditions &sc, uint64_t eip); - bool isUsable(S2EExecutionState *state, OSMonitor *monitor) const; + bool isUsable(S2EExecutionState *state) const; private: uint64_t concreteTargetEIP; From ce29b6c969d7d863a64cd68770f96d94557932ca Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 26 May 2024 21:05:00 +0200 Subject: [PATCH 19/20] testsuite/pov-cgc-cadet0: fixed build and validate pov Signed-off-by: Vitaly Chipounov --- testsuite/pov-cgc-cadet0/CADET_00001/Makefile | 4 +- .../pov-cgc-cadet0/CADET_00001/cgc-cb.mk | 346 ++++++++++++++++++ testsuite/pov-cgc-cadet0/Makefile | 2 +- testsuite/pov-cgc-cadet0/run-tests.tpl | 22 +- 4 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 testsuite/pov-cgc-cadet0/CADET_00001/cgc-cb.mk diff --git a/testsuite/pov-cgc-cadet0/CADET_00001/Makefile b/testsuite/pov-cgc-cadet0/CADET_00001/Makefile index fdc8baa9d..8fcbd2059 100755 --- a/testsuite/pov-cgc-cadet0/CADET_00001/Makefile +++ b/testsuite/pov-cgc-cadet0/CADET_00001/Makefile @@ -2,4 +2,6 @@ AUTHOR_ID = CADET SERVICE_ID = 00001 CFLAGS = -O0 -g -Werror -Wno-overlength-strings -Wno-packed -include /usr/share/cb-testing/cgc-cb.mk +include cgc-cb.mk + + diff --git a/testsuite/pov-cgc-cadet0/CADET_00001/cgc-cb.mk b/testsuite/pov-cgc-cadet0/CADET_00001/cgc-cb.mk new file mode 100644 index 000000000..a7386cba0 --- /dev/null +++ b/testsuite/pov-cgc-cadet0/CADET_00001/cgc-cb.mk @@ -0,0 +1,346 @@ +ifndef POLLS_RELEASE_COUNT +POLLS_RELEASE_COUNT=1000 +endif + +ifndef POLLS_RELEASE_SEED +POLLS_RELEASE_SEED=$(shell od -An -i -N 4 /dev/urandom) +endif + +ifndef POLLS_RELEASE_MAX_DEPTH +POLLS_RELEASE_MAX_DEPTH=1048575 +endif + +ifndef POLLS_TESTING_COUNT +POLLS_TESTING_COUNT=1000 +endif + +ifndef POLLS_TESTING_SEED +POLLS_TESTING_SEED=$(shell od -An -i -N 4 /dev/urandom) +endif + +ifndef POLLS_TESTING_MAX_DEPTH +POLLS_TESTING_MAX_DEPTH=1048575 +endif + +ifndef BIN_COUNT +BIN_COUNT=0 +endif + +ifndef POLL_REUSE_COUNT +POLL_REUSE_COUNT=0 +endif + +ifndef VULN_COUNT +VULN_COUNT=0 +endif + +LIBS = -L/usr/lib -lcgc +LDFLAGS = -nostdlib -static +POV_LIBS = -lpov + +CC = /usr/i386-linux-cgc/bin/clang +LD = /usr/i386-linux-cgc/bin/ld +CXX = /usr/i386-linux-cgc/bin/clang++ +OBJCOPY = /usr/i386-linux-cgc/bin/objcopy +LD_ELF = /usr/bin/ld + +SHELL := $(SHELL) -e +BIN_DIR = bin +POV_DIR = pov +BUILD_DIR = build +ifeq ("/usr/i386-linux-cgc/bin/clang", "$(CC)") +CGC_CFLAGS = -nostdlib -fno-builtin -nostdinc -Iinclude -Ilib -I/usr/include $(CFLAGS) -DCGC_BIN_COUNT=$(BIN_COUNT) +else +CGC_CFLAGS = -nostdlib -fno-builtin -nostdinc -nostartfiles -nodefaultlibs -Iinclude -Ilib -I/usr/include $(CFLAGS) -DCGC_BIN_COUNT=$(BIN_COUNT) +endif + +POV_CFLAGS = -nostdlib -fno-builtin -nostdinc -Iinclude -Ilib -I/usr/include $(CFLAGS) + +EXE = $(AUTHOR_ID)_$(SERVICE_ID) +CB_INSTALL_DIR = $(DESTDIR)/usr/share/cgc-challenges/$(EXE) +PATH := /usr/i386-linux-cgc/bin:$(PATH) +BINS = $(wildcard cb_*) +POV_BINS = $(wildcard pov_*) +POV_SRCS = $(wildcard pov_*/*.c) +SRCS = $(wildcard src/*.c src/*.cc lib/*.c lib/*.cc) +CXX_SRCS = $(filter %.cc, $(SRCS)) +EXTENDED_APP = /usr/share/cb-testing/CGC_Extended_Application.pdf +EXTENDED_APP_SZ = $(shell du -b $(EXTENDED_APP) | awk '{print $$1}') + +POLLS_RELEASE = $(wildcard poller/for-release/*.xml) +POLLS_TESTING = $(wildcard poller/for-testing/*.xml) +POVS = $(wildcard $(POV_DIR)/*.xml) +POVXML = $(wildcard $(POV_DIR)/*.povxml) + +CB_ADDITIONS = +ifndef NO_CB_DPKG +CB_ADDITIONS +=dpkg.o +endif +ifndef NO_CB_EXTENDED_APP +CB_ADDITIONS +=cgc-extended-application.o +endif + +has_ids_rules = $(sort $(wildcard ids/*.rules)) +has_cfe_pov = $(sort $(wildcard pov_*) $(wildcard pov/*.povxml)) +get_poll_args = $(if $(call has_cfe_pov),--store_seed --repeat $(POLL_REUSE_COUNT),) +get_pcap_args = $(if $(call has_cfe_pov),,--pcap $(PCAP_FILE_PATH)) +get_test_args = $(if $(call has_cfe_pov),--negotiate,) +get_ids_args = $(if $(call has_ids_rules),--ids_rules $(call has_ids_rules,)) +get_cb_bins = $(sort $(filter-out %_partial, $(filter-out %_patched, $(notdir $(wildcard $(BIN_DIR)/$(EXE)*))))) +get_cb_patched_bins = $(sort $(filter-out %_partial, $(filter %_patched, $(notdir $(wildcard $(BIN_DIR)/$(EXE)*))))) + +OBJS_ = $(SRCS:.c=.o) +OBJS = $(OBJS_:.cc=.o) $(CB_ADDITIONS) +ELF_OBJS = $(OBJS:.o=.elf) + +POV_OBJS = $(POV_SRCS:.c=.o) +BUILD_POV_OBJS = $(addprefix $(BUILD_DIR)/, $(POV_OBJS)) +BUILD_POV_DIRS = $(addprefix $(BUILD_DIR)/, $(POV_BINS)) + +POV_XML_BINS = $(POVXML:.povxml=.pov) + +POLL_RELEASE_LOGS = $(POLLS_RELEASE:.povxml=.log) +POLL_TESTING_LOGS = $(POLLS_TESTING:.povxml=.log) +POV_LOGS = $(POVS:.xml=.log) + +BUILD_POLL_TESTING_LOG = $(addprefix $(BUILD_DIR)/, $(POLL_RELEASE_LOGS)) +BUILD_POLL_RELEASE_LOG = $(addprefix $(BUILD_DIR)/, $(POLL_TESTING_LOGS)) +BUILD_POV_LOG = $(addprefix $(BUILD_DIR)/, $(POV_LOGS)) + +ifeq ($(VULN_COUNT), 0) +PATCHED_CFLAGS = $(CGC_CFLAGS) -DPATCHED +else +PATCHED_CFLAGS = $(CGC_CFLAGS) $(shell seq -f '-DPATCHED_%g ' -s '' $(VULN_COUNT)) +endif + +PATCHED_DIR = patched +PATCHED_EXE = $(EXE)_patched +PATCHED_PATH = $(BIN_DIR)/$(PATCHED_EXE) +PATCHED_OBJS = $(addprefix $(BUILD_DIR)/$(PATCHED_DIR)/, $(OBJS)) +PATCHED_DEBUG_PATH = $(BUILD_DIR)/$(PATCHED_DIR)/$(BIN_DIR)/$(EXE) + +PATCHED_ELF_OBJS = $(addprefix $(BUILD_DIR)/$(PATCHED_DIR)/, $(ELF_OBJS)) +PATCHED_ELF_STUB = $(BUILD_DIR)/$(PATCHED_DIR)/syscall-stub.elf +PATCHED_SO = $(BUILD_DIR)/$(PATCHED_DIR)/so/$(EXE).so + +RELEASE_CFLAGS = $(CGC_CFLAGS) +RELEASE_DIR = release +RELEASE_EXE = $(EXE) +RELEASE_PATH = $(BIN_DIR)/$(RELEASE_EXE) +RELEASE_OBJS = $(addprefix $(BUILD_DIR)/$(RELEASE_DIR)/, $(OBJS)) +RELEASE_DEBUG_PATH = $(BUILD_DIR)/$(RELEASE_DIR)/$(BIN_DIR)/$(EXE) + +PARTIAL_CFLAGS = $(CGC_CFLAGS) -DPATCHED_$(PARTIAL_ID) +PARTIAL_DIR = partial_$(PARTIAL_ID) +PARTIAL_EXE = $(EXE)_patched_$(PARTIAL_ID)_partial +PARTIAL_PATH = $(BIN_DIR)/$(PARTIAL_EXE) +PARTIAL_OBJS = $(addprefix $(BUILD_DIR)/$(PARTIAL_DIR)/, $(OBJS)) +PARTIAL_DEBUG_PATH = $(BUILD_DIR)/$(PARTIAL_DIR)/$(BIN_DIR)/$(EXE) + +PCAP_DIR = pcap +PCAP_FILE_PATH = $(PCAP_DIR)/$(RELEASE_EXE)_poll.pcap +PARTIAL_ID = + +all: build test + +testit: + @echo $(call get_cb_bins) + @echo $(call get_cb_patched_bins) + @echo $(BINS) + +prep: + @mkdir -p $(BUILD_DIR)/$(PATCHED_DIR)/lib $(BUILD_DIR)/$(PATCHED_DIR)/src $(BUILD_DIR)/$(PATCHED_DIR)/$(BIN_DIR) + @mkdir -p $(BUILD_DIR)/$(RELEASE_DIR)/lib $(BUILD_DIR)/$(RELEASE_DIR)/src $(BUILD_DIR)/$(RELEASE_DIR)/$(BIN_DIR) + @mkdir -p $(BUILD_DIR)/$(POV_DIR) + @mkdir -p $(PCAP_DIR) $(BIN_DIR) $(POV_DIR) + @mkdir -p $(BUILD_DIR)/poller/for-testing $(BUILD_DIR)/poller/for-release + @mkdir -p $(BUILD_DIR)/$(PATCHED_DIR)/so $(BUILD_POV_DIRS) +ifneq ($(strip $(PARTIAL_ID)), ) + @mkdir -p $(BUILD_DIR)/$(PARTIAL_DIR)/lib $(BUILD_DIR)/$(PARTIAL_DIR)/src $(BUILD_DIR)/$(PARTIAL_DIR)/$(BIN_DIR) +endif + + +%.pov: %.povxml + pov-xml2c -x $< -o $(BUILD_DIR)/$@.c + $(CC) -c $(POV_CFLAGS) -o $(BUILD_DIR)/$@.o $(BUILD_DIR)/$@.c + $(LD) $(LDFLAGS) -o $@ $(BUILD_DIR)/$@.o $(LIBS) $(POV_LIBS) + +$(BUILD_DIR)/%.o: %.c + $(CC) -c $(POV_CFLAGS) -o $@ $^ + +build_pov_objs: prep $(BUILD_POV_OBJS) + +$(POV_BINS): build_pov_objs + $(LD) $(LDFLAGS) -o $(POV_DIR)/$@.pov $(BUILD_DIR)/$@/*.o $(LIBS) $(POV_LIBS) + +pov: prep $(POV_XML_BINS) $(POV_BINS) + +$(BINS): cb_%: + ( cd $@; make -f ../Makefile build build-partial SERVICE_ID=$(SERVICE_ID)_$* BIN_COUNT=$(words $(BINS)) VULN_COUNT=$(VULN_COUNT)) + cp $@/$(BIN_DIR)/* $(BIN_DIR) + +build-binaries: $(BINS) + +clean-binaries: ; $(foreach dir, $(BINS), (cd $(dir) && make -f ../Makefile clean) &&) : + +$(BUILD_DIR)/$(RELEASE_DIR)/dpkg.o: /etc/decree_version + echo "The DECREE packages used in the creation of this challenge binary were:" > $@.txt + dpkg --list | grep -i cgc >> $@.txt + $(OBJCOPY) --input binary --output cgc32-i386 --binary-architecture i386 $@.txt $@ + +$(BUILD_DIR)/$(PATCHED_DIR)/dpkg.o $(BUILD_DIR)/$(PARTIAL_DIR)/dpkg.o: $(BUILD_DIR)/$(RELEASE_DIR)/dpkg.o + cp $< $@ + +$(BUILD_DIR)/$(RELEASE_DIR)/cgc-extended-application.o $(BUILD_DIR)/$(PATCHED_DIR)/cgc-extended-application.o $(BUILD_DIR)/$(PARTIAL_DIR)/cgc-extended-application.o: $(EXTENDED_APP) + echo "The $(EXTENDED_APP_SZ) byte CGC Extended Application follows. Each team participating in CGC must have submitted this completed agreement including the Team Information, the Liability Waiver, the Site Visit Information Sheet and the Event Participation agreement." > $@.tmp + cat $(EXTENDED_APP) >> $@.tmp + $(OBJCOPY) --input binary --output cgc32-i386 --binary-architecture i386 $@.tmp $@ + +%.elf: %.o + cp $< $@ + cgc2elf $@ + +$(PATCHED_ELF_STUB): + $(CC) -c -nostdlib -fno-builtin -nostdinc -o $(BUILD_DIR)/$(PATCHED_DIR)/syscall-stub.elf /usr/share/cb-testing/syscall-stub.c + cgc2elf $(BUILD_DIR)/$(PATCHED_DIR)/syscall-stub.elf + +build-partial: +ifneq ($(VULN_COUNT), 0) +ifeq ($(BIN_COUNT), 0) + ( for i in $(shell seq $(VULN_COUNT)); do \ + echo $(VULN_COUNT) ; \ + make PARTIAL_ID=$$i partial; \ + done ) +else + ( for i in $(shell seq $(VULN_COUNT)); do \ + echo $(VULN_COUNT) ; \ + make -f ../Makefile PARTIAL_ID=$$i partial; \ + done ) +endif +endif + +# Release rules +release: prep $(RELEASE_PATH) + +$(RELEASE_PATH): $(RELEASE_OBJS) + $(LD) $(LDFLAGS) -s -o $(RELEASE_PATH) -I$(BUILD_DIR)/$(RELEASE_DIR)/lib $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(RELEASE_DEBUG_PATH) -I$(BUILD_DIR)/$(RELEASE_DIR)/lib $^ $(LIBS) + +$(BUILD_DIR)/$(RELEASE_DIR)/%.o: %.c + $(CC) -c $(RELEASE_CFLAGS) -o $@ $< + +$(BUILD_DIR)/$(RELEASE_DIR)/%.o: %.cc + $(CXX) -c $(RELEASE_CFLAGS) $(CXXFLAGS) -o $@ $< + +# Partially patched binary rules +partial: prep $(PARTIAL_PATH) + +$(PARTIAL_PATH): $(PARTIAL_OBJS) + $(LD) $(LDFLAGS) -s -o $(PARTIAL_PATH) -I$(BUILD_DIR)/$(PARTIAL_DIR)/lib $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(PARTIAL_DEBUG_PATH) -I$(BUILD_DIR)/$(PARTIAL_DIR)/lib $^ $(LIBS) + +$(BUILD_DIR)/$(PARTIAL_DIR)/%.o: %.c + $(CC) -c $(PARTIAL_CFLAGS) -o $@ $< + +$(BUILD_DIR)/$(PARTIAL_DIR)/%.o: %.cc + $(CXX) -c $(PARTIAL_CFLAGS) $(CXXFLAGS) -o $@ $< + +# Patched rules +patched: prep $(PATCHED_PATH) $(PATCHED_SO) + +patched-so: prep $(PATCHED_SO) + +$(PATCHED_PATH): $(PATCHED_OBJS) + $(LD) $(LDFLAGS) -s -o $(PATCHED_PATH) $^ $(LIBS) + $(LD) $(LDFLAGS) -o $(PATCHED_DEBUG_PATH) -I$(BUILD_DIR)/$(RELEASE_DIR)/lib $^ $(LIBS) + +$(BUILD_DIR)/$(PATCHED_DIR)/%.o: %.c + $(CC) -c $(PATCHED_CFLAGS) -o $@ $< + +$(BUILD_DIR)/$(PATCHED_DIR)/%.o: %.cc + $(CXX) -c $(PATCHED_CFLAGS) $(CXXFLAGS) -o $@ $< + +ifneq ("$(CXX_SRCS)", "") +$(PATCHED_SO): + @echo "SO build artifact not currently supported for C++ services" +else +$(PATCHED_SO): $(PATCHED_ELF_OBJS) $(PATCHED_ELF_STUB) + $(LD_ELF) -shared -o $@ $^ +endif + +generate-polls: + if [ -f poller/for-release/machine.py ] && [ -f poller/for-release/state-graph.yaml ]; then generate-polls $(call get_poll_args) --count $(POLLS_RELEASE_COUNT) --seed $(POLLS_RELEASE_SEED) --depth $(POLLS_RELEASE_MAX_DEPTH) poller/for-release/machine.py poller/for-release/state-graph.yaml poller/for-release; fi + if [ -f poller/for-testing/machine.py ] && [ -f poller/for-testing/state-graph.yaml ]; then generate-polls $(call get_poll_args) --count $(POLLS_TESTING_COUNT) --seed $(POLLS_TESTING_SEED) --depth $(POLLS_TESTING_MAX_DEPTH) poller/for-testing/machine.py poller/for-testing/state-graph.yaml poller/for-testing; fi + +check: generate-polls +# Polls that the CB author intends to release the resulting network traffic during CQE + if [ -d poller/for-release ]; then cb-test $(call get_test_args) --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-release.txt --cb $(call get_cb_bins) $(call get_pcap_args); fi + if [ -d poller/for-release ]; then cb-test $(call get_test_args) --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-release.txt --cb $(call get_cb_patched_bins); fi + +# Polls that the CB author intends to NOT release the resulting network traffic during CQE + if [ -d poller/for-testing ]; then cb-test $(call get_test_args) --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-testing.txt --cb $(call get_cb_bins); fi + if [ -d poller/for-testing ]; then cb-test $(call get_test_args) --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-testing.txt --cb $(call get_cb_patched_bins); fi + +# POVs that should generate an identified crash for CQE when sent to the release CB but not the patched CB + cb-test $(call get_test_args) --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).pov.txt --failure_ok --should_core --cb $(call get_cb_bins) + cb-test $(call get_test_args) --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).pov.txt --failure_ok --cb $(call get_cb_patched_bins) + +check-ids: + if [ -d poller/for-release ]; then cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-release-ids.txt --cb $(call get_cb_bins); fi + if [ -d poller/for-release ]; then cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-release-ids.txt --cb $(call get_cb_patched_bins); fi + if [ -d poller/for-testing ]; then cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-testing-ids.txt --cb $(call get_cb_bins); fi + if [ -d poller/for-testing ]; then cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-testing-ids.txt --cb $(call get_cb_patched_bins); fi + cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).pov-ids.txt --failure_ok --cb $(call get_cb_bins) + cb-test $(call get_ids_args) $(call get_test_args) --enable_remote --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).pov-ids.txt --failure_ok --cb $(call get_cb_patched_bins) + +check-remote: + if [ -d poller/for-release ]; then cb-test $(call get_test_args) --enable_remote --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-release-remote.txt --cb $(call get_cb_bins); fi + if [ -d poller/for-release ]; then cb-test $(call get_test_args) --enable_remote --xml_dir poller/for-release/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-release-remote.txt --cb $(call get_cb_patched_bins); fi + if [ -d poller/for-testing ]; then cb-test $(call get_test_args) --enable_remote --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).for-testing-remote.txt --cb $(call get_cb_bins); fi + if [ -d poller/for-testing ]; then cb-test $(call get_test_args) --enable_remote --xml_dir poller/for-testing/ --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).for-testing-remote.txt --cb $(call get_cb_patched_bins); fi + cb-test $(call get_test_args) --enable_remote --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(RELEASE_EXE).pov-remote.txt --failure_ok --should_core --cb $(call get_cb_bins) + cb-test $(call get_test_args) --enable_remote --xml_dir $(POV_DIR) --directory $(BIN_DIR) --log $(BUILD_DIR)/$(PATCHED_EXE).pov-remote.txt --failure_ok --cb $(call get_cb_patched_bins) + +clean-test: + @-rm -f $(BUILD_POV_LOG) $(BUILD_POLL_LOG) + +clean: clean-test clean-binaries + -rm -rf $(BUILD_DIR) $(BIN_DIR) $(PCAP_DIR) + -rm -f test.log + -rm -f poller/for-release/edges.png poller/for-release/nodes.png poller/for-release/counts.png + -rm -f poller/for-release/gen_*.xml + -rm -f poller/for-release/GEN_*.xml + -rm -f poller/for-release/graph.dot + -rm -f poller/for-testing/edges.png poller/for-testing/nodes.png poller/for-testing/counts.png + -rm -f poller/for-testing/gen_*.xml + -rm -f poller/for-testing/GEN_*.xml + -rm -f poller/for-testing/graph.dot + -rm -f poller/for-release/machine.pyc + -rm -f poller/for-testing/machine.pyc + -rm -f $(POV_DIR)/*.pov + -if [ -d $(POV_DIR) ]; then rmdir --ignore-fail-on-non-empty $(POV_DIR); fi + +ifeq ($(strip $(BINS)),) +build: prep release patched pov build-partial +else +build: prep build-binaries pov +endif + +install: + install -d $(CB_INSTALL_DIR)/$(BIN_DIR) + install -d $(CB_INSTALL_DIR)/$(POV_DIR) + if [ ! -z "$(POVS)" ]; then install -m 444 $(wildcard $(POV_DIR)/*.xml) $(CB_INSTALL_DIR)/$(POV_DIR) ; fi + if [ ! -z "$(POV_BINS)" ] || [ ! -z "$(POVXML)" ]; then install -m 555 $(wildcard $(POV_DIR)/*.pov) $(CB_INSTALL_DIR)/$(POV_DIR) ; fi + if [ -d ids ]; then install -d $(CB_INSTALL_DIR)/ids ; fi + if [ -d ids ]; then install -m 444 $(wildcard ids/*.rules) $(CB_INSTALL_DIR)/ids ; fi + if [ -d poller/for-release ]; then install -d $(CB_INSTALL_DIR)/poller/for-release ; fi + if [ -d poller/for-release ]; then install -m 444 $(wildcard poller/for-release/*.xml) $(CB_INSTALL_DIR)/poller/for-release ; fi + if [ -d poller/for-testing ]; then install -d $(CB_INSTALL_DIR)/poller/for-testing ; fi + if [ -d poller/for-testing ]; then install -m 444 $(wildcard poller/for-testing/*.xml) $(CB_INSTALL_DIR)/poller/for-testing ; fi + if [ -f $(PCAP_FILE_PATH) ]; then install -d $(CB_INSTALL_DIR)/$(PCAP_DIR) ; fi + if [ -f $(PCAP_FILE_PATH) ]; then install -m 444 $(PCAP_FILE_PATH) $(CB_INSTALL_DIR)/$(PCAP_DIR) ; fi + install -m 555 $(wildcard $(BIN_DIR)/$(EXE)*) $(CB_INSTALL_DIR)/$(BIN_DIR) + +test: build check + +.PHONY: install all clean clean-test patched prep release remake test build-partial $(BINS) $(POV_BINS) diff --git a/testsuite/pov-cgc-cadet0/Makefile b/testsuite/pov-cgc-cadet0/Makefile index a2f1e1ca8..dd6846e9a 100644 --- a/testsuite/pov-cgc-cadet0/Makefile +++ b/testsuite/pov-cgc-cadet0/Makefile @@ -23,7 +23,7 @@ SOURCE=main.c BUILD_SCRIPTS_SRC:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) -RUN_DOCKER = docker run --rm -w $(BUILD_SCRIPTS_SRC) -e NO_CB_EXTENDED_APP=1 -v $(BUILD_SCRIPTS_SRC):$(BUILD_SCRIPTS_SRC) linux-build-i386cgc /run_as.sh $(shell id -u) $(shell id -g) make -C CADET_00001 $(1) +RUN_DOCKER = docker run --rm -w $(BUILD_SCRIPTS_SRC) -e NO_CB_EXTENDED_APP=1 -v $(BUILD_SCRIPTS_SRC):$(BUILD_SCRIPTS_SRC) linux-build-i386 /run_as.sh $(shell id -u) $(shell id -g) make -C CADET_00001 $(1) $(CB)/bin/$(CB): $(CB)/src/service.c cd $(BUILD_SCRIPTS_SRC) && $(call RUN_DOCKER,build) diff --git a/testsuite/pov-cgc-cadet0/run-tests.tpl b/testsuite/pov-cgc-cadet0/run-tests.tpl index 7a2d80fec..d6c8f97b7 100644 --- a/testsuite/pov-cgc-cadet0/run-tests.tpl +++ b/testsuite/pov-cgc-cadet0/run-tests.tpl @@ -59,6 +59,16 @@ def poll_recipes(s2e, directory): return False +def verify_pov(): + cwd = os.path.dirname(__file__) + verifier_path = os.path.join(cwd, 'verify-pov.sh') + verifier = subprocess.Popen(verifier_path, stdout=sys.stdout, stderr=sys.stderr, cwd=cwd) + while verifier.poll() is None: + print('Waiting for verifier...') + time.sleep(1) + + return verifier.returncode == 0 + def main(): if not os.getenv('S2EDIR'): @@ -72,10 +82,20 @@ def main(): global g_success + recipes_ok = False + pov_verified = False + try: s2e = run_s2e('s2e', 'run', '-n', PROJECT_NAME) - g_success = poll_recipes(s2e, s2e_last) + recipes_ok = poll_recipes(s2e, s2e_last) s2e.terminate() + + try: + pov_verified = verify_pov() + except Exception as e: + print('Caught exception %s' % e) + + g_success = recipes_ok and pov_verified except Exception as e: print('Caught exception %s' % e) finally: From 88b8446c2966de2678caeda5f654ad7f64e5997e Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 1 Jun 2024 23:34:47 +0200 Subject: [PATCH 20/20] docs: update pov tutorial Signed-off-by: Vitaly Chipounov --- docs/src/Tutorials/MSOffice/index.rst | 1 - docs/src/Tutorials/PoV/index.rst | 72 ++++++++++++++++----------- docs/src/Tutorials/PoV/pov.rst | 15 +++--- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/docs/src/Tutorials/MSOffice/index.rst b/docs/src/Tutorials/MSOffice/index.rst index 40f9fb9fd..e09ebb5dc 100644 --- a/docs/src/Tutorials/MSOffice/index.rst +++ b/docs/src/Tutorials/MSOffice/index.rst @@ -28,7 +28,6 @@ this case Windows and Microsoft Office. S2E supports various combinations of Win * all - Build all images Available images: - * cgc_debian-9.2.1-i386 - Debian i386 image with CGC kernel and user-space packages * debian-12.5-i386 - Debian i386 image * debian-12.5-x86_64 - Debian x86_64 image * windows-10pro1909-x86_64 - Windows 10 Pro 1909 x86_64 diff --git a/docs/src/Tutorials/PoV/index.rst b/docs/src/Tutorials/PoV/index.rst index 1ce29f98e..b38916472 100644 --- a/docs/src/Tutorials/PoV/index.rst +++ b/docs/src/Tutorials/PoV/index.rst @@ -36,12 +36,12 @@ You can find the corresponding source file in .. code-block:: console $ cd ~/s2e/env - $ s2e new_project -i debian-11.3-i386 -n vuln-lin32-32 --tools=pov \ + $ s2e new_project -i debian-12.5-i386 -n vuln-lin32-32 --tools=pov \ build/s2e/guest-tools32/common/demos/vulnerabilities @@ Here is a breakdown of the ``s2e new_project`` command above: -* It creates a new project called ``vuln-lin32-32`` that will run on the ``debian-11.3-i386`` image. +* It creates a new project called ``vuln-lin32-32`` that will run on the ``debian-12.5-i386`` image. * The ``--tools=pov`` option is important in order to generate PoVs. If you omit it, you will just get random test cases, which will at best crash the binary depending on what the constraint solver generates. Vulnerability generation @@ -222,13 +222,20 @@ find vulnerabilities and exploit them. This demo walks you through the process o The CGC Final Event (CFE) ran on the Decree operating system. Decree is a modified Linux OS with a reduced number of `system calls `__. In addition to this, the Decree OS has been modified to add "hook points" for S2E (e.g. to signal process creation, termination, etc.) and to allow S2E to inject symbolic -values. The source code for the Decree OS is available at https://github.com/S2E/s2e-linux-kernel. A Decree -virtual machine image can be built by running the following command: +values. The source code for the Decree OS is available at https://github.com/S2E/s2e-linux-kernel. + +The current version of S2E does not use the old Decree kernel anymore. It was based on Linux 3.13, which does not build +on modern Linux images that currently ship with S2E. Instead, S2E comes with ``cgcload``, a tool that emulates the Decree system calls +in user-mode using the syscall `user dispatch mechanism `__ +in recent Linux kernels. This tool is a loader that maps the native CGC challenge binary into its address space, sets up syscall emulation +that translates Decree syscalls into native Linux syscalls, then jumps to the binary's entry point. + +To run CGC binaries, build the 32-bit Linux image. This is the same image that is used for running normal Linux binaries. .. code-block:: console $ cd ~/s2e/env - $ s2e image_build cgc_debian-9.2.1-i386 + $ s2e image_build debian-12.5-i386 Next, create an analysis project for a challenge binary. Sample CBs are available `here @@ -236,37 +243,36 @@ Next, create an analysis project for a challenge binary. Sample CBs are availabl `__ . The remainder of this tutorial will focus on the CADET_00001 program (a pre-compiled version of which is available `here `__), but the ideas and techniques should be applicable -to all of the CBs. +to all of the CBs. Note that the S2E tooling does not support multi-binary CBs. The following command creates a ``projects/CADET_00001`` directory with various scripts and configuration files needed by S2E, as described `here <../../s2e-env.rst>`__. .. code-block:: console - $ s2e new_project --image cgc_debian-9.2.1-i386 ./source/s2e/decree/samples/CADET_00001 + $ s2e new_project ./source/s2e/decree/samples/CADET_00001 -Finally, to start S2E, run the following command: +Then run S2E: .. code-block:: console - $ s2e run CADET_00001 + $ cd projects/CADET_00001 + $ ./launch-s2e.sh -This will display a TUI-based dashboard, similar to that used by the American Fuzzy Lop (AFL) fuzzer. As S2E finds -vulnerabilities, it generates PoV files in the ``s2e-last`` directory. These files have either ``.xml`` or ``.c`` -file extensions. Once some PoV files have been generated you can press ``q`` to stop S2E. +Wait for PoVs to be generated (``s2e-last/*.c`` files) then interrupt S2E with Ctrl+C. +Finally, verify that the generated PoVs are valid, i.e., they can reproduce the correct crashes when run, +using the following command: -.. image:: cadet_00001_tui.png +.. code-block:: console -Alternatively, you can run S2E without the TUI by using the ``-n`` option in ``s2e run``. Instead of the TUI you will -see the standard S2E output. Once some POVs have been generated you can stop S2E by killing the process with -``Ctrl+C`` or ``killall -9 qemu-system-i386``. + $ ./verify-pov.sh Understanding CGC-style PoVs ============================ If you followed the tutorial on PoV generation on Linux, you will notice that the PoV format for CGC binaries is -different. Instead of being a concrete input file, CGC binaries produce PoVs in ``.xml`` or ``.c`` format. The reason +different. Instead of being a concrete input file, S2E plugins produce PoVs in ``.c`` format. The reason for this is that CGC binaries read their input from ``stdin`` and write results to ``stdout``. So in order to exercise the vulnerability, the PoV must implement a two-way communication with the program, by reading the program's output and writing an appropriate input. This is different from file-based PoVs, where all the input is sent to the program at @@ -280,21 +286,29 @@ once, and the program's output is ignored. For this reason, replaying a CGC-style PoV is more complex. It requires a special setup so that the PoV can communicate -with the CB. For more details, see `here -`__. -The following outlines the steps required to replay a PoV: +with the CB. The ``verify-pov.sh`` script takes care of all the steps. First, the PoV file must be compiled to a binary. +Then the ``povtest`` tool takes this binary and connects its standard input/output to the challenge binary's output/input. +It negotiates the PoV parameters with the PoV binary and monitors the challenge binary for crashes. When the PoV terminates, +``povtest`` verifies that the challenge binary crashed at the correct location (type 1 PoV) or the PoV binary exfiltrated +the correct data from the challenge binary's address space (type 2 PoV). -1. Follow the instructions `here - `__ to - setup and run the CGC testing VM +.. code-block:: console + + # Build the PoV source code. The C file is generated by the DecreePoVGenerator plugin. + $ gcc -O0 -g -m32 -o s2e-last/recipe-type2_i386_decree_shellcode_1.rcp-pov-type2-1 \ + -L$S2EDIR/install/bin/guest-tools32/lib -I$S2EDIR/source/s2e/guest/linux/include \ + s2e-last/recipe-type2_i386_decree_shellcode_1.rcp-pov-type2-1.c -lpov -lcgc -2. As discussed in the instructions in the previous step, files can be shared between the host and CGC testing VM via - the ``/vagrant`` directory. Copy the CADET_00001 binary, the PoV XML files generated by S2E and `this - `__ script (located in your S2E environment in - ``bin/cgc-tools/test_pov.sh``) to the CGC testing VM. + # Verify the PoV. + $ $S2EDIR/install/bin/guest-tools32/povtest \ + --pov=s2e-last/recipe-type2_i386_decree_shellcode_1.rcp-pov-type2-1 \ + -- $S2EDIR/bin/guest-tools32/cgcload $S2EDIR/source/s2e/testsuite/pov-cgc-cadet0/CADET_00001/bin/CADET_00001 + +.. note:: -3. Run ``vagrant ssh`` to access the VM and copy the files from ``/vagrant`` into ``/home/vagrant``. Then run the - ``test_pov.sh`` script to check the PoV's correctness. + S2E does not use the original DARPA CGC tooling for PoV verification. The tooling is not maintained + and does not run on modern Linux distributions, mostly because it uses an old Python 2 version. + The Decree kernel does not build anymore either so it is not possible to run the CGC binaries natively. Plugin architecture overview diff --git a/docs/src/Tutorials/PoV/pov.rst b/docs/src/Tutorials/PoV/pov.rst index abf635f10..967c549d4 100644 --- a/docs/src/Tutorials/PoV/pov.rst +++ b/docs/src/Tutorials/PoV/pov.rst @@ -394,9 +394,8 @@ the binary, S2E disassembles it, extracts all function addresses, then invokes e the function produces the expected output, identification succeeded. - - S2E uses `RevGen `__, an x86-to-LLVM translator, - in order to extract function types from the binary before analyzing it. +S2E used `RevGen <../Revgen/Revgen.rst>`__, an x86-to-LLVM translator, in order to extract function types from +the binary before analyzing it. Generating Replayable PoVs @@ -536,10 +535,10 @@ A correct PoV would look like this: To generate a correct PoV, S2E looks at all branch conditions and looks for cases where the content of a receive buffer is compared with a symbolic value derived from the random number generator. Once it found such a comparison, it can -easily generate the correct PoV code by mapping the symbolic value created in the receive call to the symbolic value +generate the correct PoV code by mapping the symbolic value created in the receive call to the symbolic value written by the transmit function. -A much harder case happens when the PoV needs to perform computations. Consider the slightly modified above example: +A harder case happens when the PoV needs to perform computations. Consider the slightly modified above example: .. code-block:: c :number-lines: @@ -589,9 +588,9 @@ solver. The following snippet shows how would an automatically generated PoV wit transmit(&data1, sizeof(data1)); } -Unfortunately, we ran out of time and didn't have time to implement this solution. The main challenge was to fit an -entire solver within the size and memory limits of a PoV, as well as modifying the solver to accommodate a very -restricted runtime environment, that has primitive memory allocation, no standard library, etc. +Unfortunately, we ran out of time and did not have time to implement this solution. The main challenge was to fit an +entire solver within the size and memory limits of a PoV, as well as modifying the solver to accommodate +a restricted runtime environment, that has primitive memory allocation, no standard library, etc. Conclusion ==========