diff options
| author | ptitSeb <sebastien.chev@gmail.com> | 2024-12-03 11:43:38 +0100 |
|---|---|---|
| committer | ptitSeb <sebastien.chev@gmail.com> | 2024-12-03 11:43:46 +0100 |
| commit | 794f2104bcee9a2aff192804d9c07b2163e79a51 (patch) | |
| tree | 6d96d0493dc84d3ad10ffc6b598fce976b46a903 | |
| parent | 036af9f55fa5b8542872c14b801bce5512c31583 (diff) | |
| download | box64-794f2104bcee9a2aff192804d9c07b2163e79a51.tar.gz box64-794f2104bcee9a2aff192804d9c07b2163e79a51.zip | |
Improved signal handling ([BOX32] Too)
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/dynarec/dynarec.c | 8 | ||||
| -rw-r--r-- | src/libtools/decopcode.c | 381 | ||||
| -rw-r--r-- | src/libtools/signal32.c | 27 | ||||
| -rw-r--r-- | src/libtools/signals.c | 37 | ||||
| -rw-r--r-- | tests/ref21.txt | 48 | ||||
| -rwxr-xr-x | tests/test21 | bin | 23696 -> 23624 bytes | |||
| -rw-r--r-- | tests/test21.c | 195 |
8 files changed, 644 insertions, 53 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a4608510..fb308689 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,6 +374,7 @@ set(ELFLOADER_SRC "${BOX64_ROOT}/src/libtools/auxval.c" "${BOX64_ROOT}/src/libtools/myalign.c" "${BOX64_ROOT}/src/libtools/signals.c" + "${BOX64_ROOT}/src/libtools/decopcode.c" "${BOX64_ROOT}/src/libtools/threads.c" "${BOX64_ROOT}/src/tools/bitutils.c" "${BOX64_ROOT}/src/tools/box64stack.c" diff --git a/src/dynarec/dynarec.c b/src/dynarec/dynarec.c index 265b23f7..106b447a 100644 --- a/src/dynarec/dynarec.c +++ b/src/dynarec/dynarec.c @@ -34,17 +34,17 @@ void* LinkNext(x64emu_t* emu, uintptr_t addr, void* x2, uintptr_t* x3) #ifdef HAVE_TRACE if(!addr) { dynablock_t* db = FindDynablockFromNativeAddress(x2-4); - printf_log(LOG_NONE, "Warning, jumping to NULL address from %p (db=%p, x64addr=%p/%s)\n", x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); + printf_log(LOG_INFO, "Warning, jumping to NULL address from %p (db=%p, x64addr=%p/%s)\n", x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); } else if(addr<0x10000) { dynablock_t* db = FindDynablockFromNativeAddress(x2-4); - printf_log(LOG_NONE, "Warning, jumping to low address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); + printf_log(LOG_INFO, "Warning, jumping to low address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); #ifdef BOX32 } else if(emu->segs[_CS]==0x23 && addr>0x100000000LL) { dynablock_t* db = FindDynablockFromNativeAddress(x2-4); - printf_log(LOG_NONE, "Warning, jumping to high address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); + printf_log(LOG_INFO, "Warning, jumping to high address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); } else if(!memExist(addr)) { dynablock_t* db = FindDynablockFromNativeAddress(x2-4); - printf_log(LOG_NONE, "Warning, jumping to an unmapped address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); + printf_log(LOG_INFO, "Warning, jumping to an unmapped address %p from %p (db=%p, x64addr=%p/%s)\n", (void*)addr, x2-4, db, db?(void*)getX64Address(db, (uintptr_t)x2-4):NULL, db?getAddrFunctionName(getX64Address(db, (uintptr_t)x2-4)):"(nil)"); #endif } #endif diff --git a/src/libtools/decopcode.c b/src/libtools/decopcode.c new file mode 100644 index 00000000..5bc84657 --- /dev/null +++ b/src/libtools/decopcode.c @@ -0,0 +1,381 @@ +#include <stdint.h> +#include <sys/mman.h> + +#include "debug.h" +#include "x64emu.h" +#include "emu/x64run_private.h" +#include "custommem.h" + +#define OPCODE_READ (1<<0) +#define OPCODE_WRITE (1<<1) +#define OPCODE_STACK (1<<2) + +#define MODREG ((nextop&0xC0)==0xC0) + +int decode_avx(uint8_t* addr, int idx, vex_t vex) +{ + return 0; +} + +int decode_0f(uint8_t* addr, int idx, rex_t rex) +{ + uint8_t nextop; + switch(addr[idx++]) { + case 0x00: + return OPCODE_READ; + case 0x01: + return OPCODE_WRITE; + case 0x10: + case 0x12: + case 0x14: + case 0x15: + case 0x16: + case 0x28: + case 0x2A: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x50 ... 0x6B: + case 0x6E: + case 0x6F: + case 0x70: + case 0x74: + case 0x75: + case 0x76: + case 0xA3: + case 0xAF: + case 0xB6: + case 0xB7: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xC2: + case 0xC4 ... 0xC6: + case 0xD1 ... 0xD5: + case 0xD7 ... 0xE5: + case 0xE8 ... 0xEF: + case 0xF1 ... 0xFE: + nextop = addr[idx++]; + return (MODREG)?0:OPCODE_READ; + case 0x11: + case 0x13: + case 0x17: + case 0x29: + case 0x2B: + case 0x7E: + case 0x7F: + case 0x90 ... 0x9F: + case 0xAE: + case 0xC3: + case 0xE7: + nextop = addr[idx++]; + return (MODREG)?0:OPCODE_WRITE; + case 0x71: + case 0x72: + case 0x73: + case 0xA4: + case 0xA5: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xB0: + case 0xB1: + case 0xB3: + case 0xBA: + case 0xBB: + case 0xC0: + case 0xC1: + case 0xC7: + return (MODREG)?0:(OPCODE_READ|OPCODE_WRITE); + case 0xA0: + case 0xA8: + return OPCODE_WRITE|OPCODE_STACK; + case 0xA1: + case 0xA9: + return OPCODE_READ|OPCODE_STACK; + case 0x38: //todo + return 0; + case 0x3A: //todo + return 0; + } + return 0; +} +int decode_660f(uint8_t* addr, int idx, rex_t rex) +{ + return 0; +} +int decode_f20f(uint8_t* addr, int idx, rex_t rex) +{ + return 0; +} +int decode_f30f(uint8_t* addr, int idx, rex_t rex) +{ + return 0; +} + +int decode_opcode(uintptr_t rip, int is32bits) +{ + if(!(getProtection(rip)&PROT_READ)) + return 0; + // check if opcode is one that write to memory... pretty crude for now. + uint8_t* addr = (uint8_t*)rip; + int idx = 0; + rex_t rex = {0}; + int is66 = 0, is67 = 0, rep = 0; + int lock = 0; + vex_t vex = {0}; + uint8_t nextop; + if(is32bits) { + rex.is32bits = 1; + while(addr[idx]==0x66 || addr[idx]==0xF2 || addr[idx]==0xF3 || (addr[idx]==0x2E)|| (addr[idx]==0x3E) || (addr[idx]==0x26)|| (addr[idx]==0x36) || (addr[idx]==0xf0) || (addr[idx]==0x64)|| (addr[idx]==0x65)) { + switch(addr[idx++]) { + case 0x66: is66=1; break; + case 0xF0: lock=1; break; + case 0xF2: rep=1; break; + case 0xF3: rep=2; break; + } + } + } else { + while((addr[idx]>=0x40 && addr[idx]<0x4f) || (addr[idx]==0x66 || addr[idx]==0xF2 || addr[idx]==0xF3 || (addr[idx]==0x3E) || (addr[idx]==0x26) || (addr[idx]==0xf0)) || (addr[idx]==0x64)|| (addr[idx]==0x65)) { + switch(addr[idx++]) { + case 0x66: is66=1; break; + case 0xF0: lock=1; break; + case 0xF2: rep=1; break; + case 0xF3: rep=2; break; + case 0x40 ... 0x4f: rex.rex = addr[idx-1]; break; + } + } + } + if((addr[idx]==0xC4 || addr[idx]==0xC5) && (!is32bits || (addr[idx+1]&0xc0!=0xc0))) { + uint8_t tmp8u; + switch(addr[idx++]) { + case 0xC4: + vex.rex = rex; + tmp8u = nextop; + vex.m = tmp8u&0b00011111; + vex.rex.b = (tmp8u&0b00100000)?0:1; + vex.rex.x = (tmp8u&0b01000000)?0:1; + vex.rex.r = (tmp8u&0b10000000)?0:1; + tmp8u = addr[idx++]; + vex.p = tmp8u&0b00000011; + vex.l = (tmp8u>>2)&1; + vex.v = ((~tmp8u)>>3)&0b1111; + vex.rex.w = (tmp8u>>7)&1; + break; + case 0xC5: + vex.rex = rex; + tmp8u = nextop; + vex.p = tmp8u&0b00000011; + vex.l = (tmp8u>>2)&1; + vex.v = ((~tmp8u)>>3)&0b1111; + vex.rex.r = (tmp8u&0b10000000)?0:1; + vex.rex.b = 0; + vex.rex.x = 0; + vex.rex.w = 0; + vex.m = VEX_M_0F; + break; + } + return decode_avx(addr, idx, vex); + } + switch(addr[idx++]) { + case 0x00: + case 0x01: + case 0x08: + case 0x09: + case 0x10: + case 0x11: + case 0x18: + case 0x19: + case 0x20: + case 0x21: + case 0x28: + case 0x29: + case 0x30: + case 0x31: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8C: + case 0xC0: + case 0xC1: + case 0xC6: + case 0xC7: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + nextop = addr[idx++]; + return (MODREG)?0:(OPCODE_WRITE|OPCODE_READ); + case 0x02: + case 0x03: + case 0x0a: + case 0x0b: + case 0x12: + case 0x13: + case 0x1a: + case 0x1b: + case 0x22: + case 0x23: + case 0x2a: + case 0x2b: + case 0x32: + case 0x33: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x63: + case 0x69: + case 0x6B: + case 0x84: + case 0x85: + case 0x8A: + case 0x8B: + case 0x8E: + nextop = addr[idx++]; + return (MODREG)?0:(OPCODE_READ); + case 0x06: + case 0x0E: + case 0x16: + case 0x1E: + case 0x60: + return is32bits?(OPCODE_WRITE|OPCODE_STACK):0; + case 0x07: + case 0x17: + case 0x1F: + case 0x61: + return is32bits?(OPCODE_READ|OPCODE_STACK):0; + case 0x50 ... 0x57: + case 0x68: + case 0x6A: + case 0x9C: + case 0xC8: + case 0xE8: + return OPCODE_WRITE|OPCODE_STACK; + case 0x58 ... 0x5F: + case 0x9D: + case 0xC2: + case 0xC3: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCF: + return OPCODE_READ|OPCODE_STACK; + case 0x80 ... 0x83: + nextop = addr[idx++]; + return (MODREG)?0:((((nextop>>3)&7!=7)?OPCODE_WRITE:0)|OPCODE_READ); + case 0x8F: + nextop = addr[idx++]; + return ((MODREG)?0:(OPCODE_WRITE))|OPCODE_READ|OPCODE_STACK; + case 0xA0: + case 0xA1: + case 0xA6: + case 0xA7: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xD7: + return OPCODE_READ; + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xAA: + case 0xAB: + return OPCODE_WRITE; + case 0xF6: + case 0xF7: + nextop = addr[idx++]; + if(MODREG) return 0; + switch((nextop>>3)&7) { + case 2: + case 3: + return OPCODE_WRITE; + default: + return OPCODE_READ; + } + case 0xFE: + case 0xFF: + nextop = addr[idx++]; + if(MODREG) return 0; + switch((nextop>>3)&7) { + case 0: + case 1: + return OPCODE_WRITE; + case 2: + case 3: + case 6: + return OPCODE_READ|OPCODE_WRITE|OPCODE_STACK; + default: + return OPCODE_READ; + } + + case 0x0F: + if(is66) return decode_660f(addr, idx, rex); + if(rep==1) return decode_f20f(addr, idx, rex); + if(rep==2) return decode_f30f(addr, idx, rex); + return decode_0f(addr, idx, rex); + case 0xD8 ... 0xDF: + nextop = addr[idx++]; + if(nextop<0xC0) { + switch(addr[idx-2]) { + case 0xD8: + case 0xDA: + case 0xDC: + case 0xDE: + return OPCODE_READ; + case 0xD9: + switch((nextop>>3)&7) { + case 0: + case 4: + case 5: + return OPCODE_READ; + case 2: + case 3: + case 6: + case 7: + return OPCODE_WRITE; + } + return 0; + case 0xDB: + switch((nextop>>3)&7) { + case 0: + case 1: + case 2: + case 3: + case 7: + return OPCODE_WRITE; + case 5: + return OPCODE_READ; + } + return 0; + case 0xDF: + switch((nextop>>3)&7) { + case 0: + case 4: + case 5: + return OPCODE_READ; + case 1: + case 2: + case 3: + case 6: + case 7: + return OPCODE_WRITE; + } + } + } + return 0; + + } + return 0; +} + +int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits) +{ + // TODO, on ARM64, RiSCV and LoongArch, it would be easier to analyse the opcode at the native IP instead, as opcode that write to memory are more limited in quantity + return (decode_opcode(rip, is32bits)&OPCODE_WRITE)?1:0; +} \ No newline at end of file diff --git a/src/libtools/signal32.c b/src/libtools/signal32.c index 0cf9e440..e55c88f2 100644 --- a/src/libtools/signal32.c +++ b/src/libtools/signal32.c @@ -447,7 +447,7 @@ uint32_t RunFunctionHandler32(int* exit, int dynarec, i386_ucontext_t* sigcontex return ret; } - +int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits); #define is_memprot_locked (1<<1) #define is_dyndump_locked (1<<8) void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db) @@ -622,6 +622,7 @@ void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, vo */ uint32_t prot = getProtection((uintptr_t)info->si_addr); uint32_t mmapped = memExist((uintptr_t)info->si_addr); + uint32_t sysmapped = (info->si_addr<(void*)box64_pagesize)?1:mmapped; uint32_t real_prot = 0; if(prot&PROT_READ) real_prot|=PROT_READ; if(prot&PROT_WRITE) real_prot|=PROT_WRITE; @@ -642,28 +643,24 @@ void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, vo info2->_sifields._sigfault.__si_addr = 0; } else if (info->si_errno==0xecec) { // no excute bit on segment - sigcontext->uc_mcontext.gregs[I386_ERR] = 16;//(real_prot&PROT_READ)?16:1; // EXECUTE_FAULT & READ_FAULT + sigcontext->uc_mcontext.gregs[I386_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1); sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 14; + if(!mmapped) info2->si_code = 1; info2->si_errno = 0; }else { - sigcontext->uc_mcontext.gregs[I386_ERR] = mmapped?16:1;//(real_prot&PROT_READ)?16:1;//(info->si_errno==0x1234)?0:((info->si_errno==0xdead)?(0x2|(info->si_code<<3)):0x0010); // execution flag issue (probably), unless it's a #GP(0) - sigcontext->uc_mcontext.gregs[I386_TRAPNO] = (mmapped)?14:13; - //sigcontext->uc_mcontext.gregs[I386_TRAPNO] = ((info->si_code==SEGV_ACCERR) || (info->si_errno==0x1234) || (info->si_errno==0xdead) || ((uintptr_t)info->si_addr==0))?13:14; + sigcontext->uc_mcontext.gregs[I386_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1); + sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 14; } - } else if(info->si_code==SEGV_ACCERR && !(prot&PROT_WRITE)) { - sigcontext->uc_mcontext.gregs[I386_ERR] = (real_prot&PROT_READ)?2:1;//0x0002; // write flag issue - sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 14; } else { - if((info->si_code!=SEGV_ACCERR) && labs((intptr_t)info->si_addr-(intptr_t)sigcontext->uc_mcontext.gregs[I386_ESP])<16) - sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 12; // stack overflow probably - else - sigcontext->uc_mcontext.gregs[I386_TRAPNO] = (mmapped || ((uintptr_t)info->si_addr<0x10000))?14:13; - //I386_ERR seems to be INT:8 CODE:8. So for write access segfault it's 0x0002 For a read it's 0x0004 (and 8 for exec). For an int 2d it could be 0x2D01 for example - sigcontext->uc_mcontext.gregs[I386_ERR] = 0x0001; // read error? + sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 14; + sigcontext->uc_mcontext.gregs[I386_ERR] = 4|((sysmapped && !(real_prot&PROT_READ))?0:1); + if(write_opcode(sigcontext->uc_mcontext.gregs[I386_EIP], (uintptr_t)pc, 1)) + sigcontext->uc_mcontext.gregs[I386_ERR] |= 2; } if(info->si_code == SEGV_ACCERR && old_code) *old_code = -1; if(info->si_errno==0x1234) { + sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 13; info2->si_errno = 0; } else if(info->si_errno==0xdead) { // INT x @@ -671,6 +668,8 @@ void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, vo info2->si_errno = 0; info2->si_code = 128; info2->_sifields._sigfault.__si_addr = 0; + sigcontext->uc_mcontext.gregs[I386_TRAPNO] = 13; + // some special cases... if(int_n==3) { info2->si_signo = SIGTRAP; diff --git a/src/libtools/signals.c b/src/libtools/signals.c index 3bf37f50..d760ecf1 100644 --- a/src/libtools/signals.c +++ b/src/libtools/signals.c @@ -466,12 +466,13 @@ EXPORT int my_sigaltstack(x64emu_t* emu, const x64_stack_t* ss, x64_stack_t* oss return 0; } - #ifdef DYNAREC -uintptr_t getX64Address(dynablock_t* db, uintptr_t arm_addr) +uintptr_t getX64Address(dynablock_t* db, uintptr_t native_addr) { uintptr_t x64addr = (uintptr_t)db->x64_addr; uintptr_t armaddr = (uintptr_t)db->block; + if(native_addr<(uintptr_t)db->block || native_addr>(uintptr_t)db->block+db->size) + return 0; int i = 0; do { int x64sz = 0; @@ -482,7 +483,7 @@ uintptr_t getX64Address(dynablock_t* db, uintptr_t arm_addr) ++i; } while((db->instsize[i-1].x64==15) || (db->instsize[i-1].nat==15)); // if the opcode is a NOP on ARM side (so armsz==0), it cannot be an address to find - if((arm_addr>=armaddr) && (arm_addr<(armaddr+armsz))) + if((native_addr>=armaddr) && (native_addr<(armaddr+armsz))) return x64addr; armaddr+=armsz; x64addr+=x64sz; @@ -509,7 +510,7 @@ x64emu_t* getEmuSignal(x64emu_t* emu, ucontext_t* p, dynablock_t* db) return emu; } #endif - +int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits); void adjustregs(x64emu_t* emu) { // tests some special cases uint8_t* mem = (uint8_t*)R_RIP; @@ -1146,6 +1147,7 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo */ uint32_t prot = getProtection((uintptr_t)info->si_addr); uint32_t mmapped = memExist((uintptr_t)info->si_addr); + uint32_t sysmapped = (info->si_addr<(void*)box64_pagesize)?1:mmapped; uint32_t real_prot = 0; if(prot&PROT_READ) real_prot|=PROT_READ; if(prot&PROT_WRITE) real_prot|=PROT_WRITE; @@ -1166,28 +1168,24 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo info2->si_addr = NULL; } else if (info->si_errno==0xecec) { // no excute bit on segment - sigcontext->uc_mcontext.gregs[X64_ERR] = (real_prot&PROT_READ)?16:1; // EXECUTE_FAULT & READ_FAULT - sigcontext->uc_mcontext.gregs[X64_TRAPNO] = mmapped?14:13; + sigcontext->uc_mcontext.gregs[X64_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1); + sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14; + if(!mmapped) info2->si_code = 1; info2->si_errno = 0; }else { - sigcontext->uc_mcontext.gregs[X64_ERR] = (real_prot&PROT_READ)?16:1;//(info->si_errno==0x1234)?0:((info->si_errno==0xdead)?(0x2|(info->si_code<<3)):0x0010); // execution flag issue (probably), unless it's a #GP(0) - sigcontext->uc_mcontext.gregs[X64_TRAPNO] = mmapped?14:13; - //sigcontext->uc_mcontext.gregs[X64_TRAPNO] = ((info->si_code==SEGV_ACCERR) || (info->si_errno==0x1234) || (info->si_errno==0xdead) || ((uintptr_t)info->si_addr==0))?13:14; + sigcontext->uc_mcontext.gregs[X64_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1); + sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14; } - } else if(info->si_code==SEGV_ACCERR && !(prot&PROT_WRITE)) { - sigcontext->uc_mcontext.gregs[X64_ERR] = (real_prot&PROT_READ)?2:1;//0x0002; // write flag issue - sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14; } else { - if((info->si_code!=SEGV_ACCERR) && labs((intptr_t)info->si_addr-(intptr_t)sigcontext->uc_mcontext.gregs[X64_RSP])<16) - sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 12; // stack overflow probably - else - sigcontext->uc_mcontext.gregs[X64_TRAPNO] = mmapped?14:13; - //X64_ERR seems to be INT:8 CODE:8. So for write access segfault it's 0x0002 For a read it's 0x0004 (and 8 for exec). For an int 2d it could be 0x2D01 for example - sigcontext->uc_mcontext.gregs[X64_ERR] = 0x0001; // read error? + sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14; + sigcontext->uc_mcontext.gregs[X64_ERR] = 4|((sysmapped && !(real_prot&PROT_READ))?0:1); + if(write_opcode(sigcontext->uc_mcontext.gregs[X64_RIP], (uintptr_t)pc, (R_CS==0x23))) + sigcontext->uc_mcontext.gregs[X64_ERR] |= 2; } if(info->si_code == SEGV_ACCERR && old_code) *old_code = -1; if(info->si_errno==0x1234) { + sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13; info2->si_errno = 0; } else if(info->si_errno==0xdead) { // INT x @@ -1195,6 +1193,7 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo info2->si_errno = 0; info2->si_code = 128; info2->si_addr = NULL; + sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13; // some special cases... if(int_n==3) { info2->si_signo = SIGTRAP; @@ -1461,7 +1460,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) #ifdef BAD_SIGNAL // try to see if the si_code makes sense // the RK3588 tend to need a special Kernel that seems to have a weird behaviour sometimes - if((sig==SIGSEGV) && (addr) && (info->si_code == 1) && prot&(PROT_READ|PROT_WRITE|PROT_EXEC)) { + if((sig==SIGSEGV) && (addr) && (info->si_code == 1) && getMmapped((uintptr_t)addr)) { printf_log(LOG_DEBUG, "Workaround for suspicious si_code for %p / prot=0x%hhx\n", addr, prot); info->si_code = 2; } diff --git a/tests/ref21.txt b/tests/ref21.txt index 8fc221a0..14aada64 100644 --- a/tests/ref21.txt +++ b/tests/ref21.txt @@ -1,6 +1,50 @@ sig = 11 got bad_ptr sig = 5 -si_addr: 0, si_code: 128, si_errno: 0, RIP offset: 1, TRAPERR=0 TRAPNO=3 +si_addr: 0, si_code: 128, si_errno: 0, RIP offset: 1, TRAPERR=0x0 TRAPNO=3 sig = 5 -si_addr: 0, si_code: 128, si_errno: 0, RIP offset: 2, TRAPERR=0 TRAPNO=3 +si_addr: 0, si_code: 128, si_errno: 0, RIP offset: 2, TRAPERR=0x0 TRAPNO=3 +sig = 11 +si_addr: 0, si_code: 128, si_errno: 0, RIP offset: 0, TRAPERR=0x16a TRAPNO=13 +from non-existant memory +sig = 11 +si_addr: ffffffffdeadbeef, si_code: 1, si_errno: 0, TRAPERR=0x7 TRAPNO=14 +segfault, good +sig = 11 +si_addr: ffffffffdeadbeef, si_code: 1, si_errno: 0, TRAPERR=0x5 TRAPNO=14 +segfault, good +sig = 11 +si_addr: ffffffffdeadbeef, si_code: 1, si_errno: 0, TRAPERR=0x15 TRAPNO=14 +segfault, good +from NULL memory +sig = 11 +si_addr: 0, si_code: 1, si_errno: 0, TRAPERR=0x6 TRAPNO=14 +segfault, good +sig = 11 +si_addr: 0, si_code: 1, si_errno: 0, TRAPERR=0x4 TRAPNO=14 +segfault, good +sig = 11 +si_addr: 0, si_code: 1, si_errno: 0, TRAPERR=0x14 TRAPNO=14 +segfault, good +from existant memory +exec_p prot = 0 +sig = 11 +si_addr: exec_p+0, si_code: 2, si_errno: 0, TRAPERR=0x6 TRAPNO=14 +segfault, good +sig = 11 +si_addr: exec_p+0, si_code: 2, si_errno: 0, TRAPERR=0x4 TRAPNO=14 +segfault, good +exec_p prot = PROT_READ +sig = 11 +si_addr: exec_p+0, si_code: 2, si_errno: 0, TRAPERR=0x7 TRAPNO=14 +segfault, good +exec_p prot = PROT_READ|PROT_WRITE +sig = 11 +si_addr: exec_p+0, si_code: 2, si_errno: 0, RIP offset: 0, TRAPERR=0x15 TRAPNO=14 +Cannot run, good +exec_p prot = PROT_READ|PROT_WRITE|PROT_EXEC +exec_p prot = PROT_READ|PROT_WRITE +sig = 11 +si_addr: exec_p+0, si_code: 2, si_errno: 0, RIP offset: 0, TRAPERR=0x15 TRAPNO=14 +Cannot run, good! +exec_p prot = PROT_READ|PROT_WRITE|PROT_EXEC diff --git a/tests/test21 b/tests/test21 index 934ebcb7..7696b453 100755 --- a/tests/test21 +++ b/tests/test21 Binary files differdiff --git a/tests/test21.c b/tests/test21.c index 09bae72a..74865023 100644 --- a/tests/test21.c +++ b/tests/test21.c @@ -7,6 +7,7 @@ #include <errno.h> #include <string.h> #include <unistd.h> +#include <stdint.h> static jmp_buf context_buf; @@ -33,12 +34,20 @@ typedef void(*vFv_t)(void); #define X_ERR 19 static void segv_action(int sig, siginfo_t* info, ucontext_t* ucntx) { + if(!exec_p) { + segv_handler(sig); + return; + } printf("sig = %d\n", sig); - printf("si_addr: %zx, si_code: %d, si_errno: %d, RIP offset: %zd, TRAPERR=%d TRAPNO=%d\n", - info->si_addr, - info->si_code, - info->si_errno, - ((intptr_t)ucntx->uc_mcontext.gregs[X_IP])-((intptr_t)exec_p), + uintptr_t rip = (intptr_t)ucntx->uc_mcontext.gregs[X_IP]; + if(info->si_addr>=exec_p && info->si_addr<(exec_p+10)) + printf("si_addr: exec_p+%zx, ", (uintptr_t)info->si_addr-(uintptr_t)exec_p); + else + printf("si_addr: %zx, ", info->si_addr); + printf("si_code: %d, si_errno: %d, ", info->si_code, info->si_errno); + if(rip>=((intptr_t)exec_p) && rip<((intptr_t)exec_p+5)) + printf("RIP offset: %zd, ", rip-((intptr_t)exec_p)); + printf("TRAPERR=0x%x TRAPNO=%d\n", ucntx->uc_mcontext.gregs[X_ERR], ucntx->uc_mcontext.gregs[X_TRAPNO] ); @@ -48,6 +57,7 @@ static void segv_action(int sig, siginfo_t* info, ucontext_t* ucntx) static unsigned char buff_cc[] = { 0xcc, 0xc3 }; static unsigned char buff_cd03[] = { 0xcd, 0x03, 0xc3 }; static unsigned char buff_cd2d[] = { 0xcd, 0x2d, 0xc3 }; +static uint8_t buff_simplef[] = { 0xb8, 1, 0, 0, 0, 0xc3 }; void test_cc() { memcpy(exec_p, buff_cc, sizeof(buff_cc)); @@ -60,24 +70,178 @@ void test_cc() vFv_t f = (vFv_t)exec_p; f(); } - /*memcpy(exec_p, buff_cd2d, sizeof(buff_cd2d)); + memcpy(exec_p, buff_cd2d, sizeof(buff_cd2d)); if(!setjmp(context_buf)) { vFv_t f = (vFv_t)exec_p; f(); - }*/ + } } -int main() +void test_segfault() { - if(signal(SIGSEGV, segv_handler) == SIG_ERR) { - printf("signal: Err = %d\n", errno); - return -1; + printf("from non-existant memory\n"); + // writing to existing protected memory + if(!setjmp(context_buf)) { + int *bad_ptr = (int*)0xffffffffdeadbeef; + *(uint8_t*)bad_ptr = 0xc3; + } else { + printf("segfault, good\n"); } - //printf("handler = %p\n", segv_handler); - test(); + // writing to existing protected memory + if(!setjmp(context_buf)) { + int *bad_ptr = (int*)0xffffffffdeadbeef; + if(*(uint8_t*)bad_ptr == 0xc3) + printf("should not be readable or writeable!\n"); + else + printf("should not be readable!\n"); + printf("aborting test\n"); + return; + } else { + printf("segfault, good\n"); + } + // writing to existing protected memory + if(!setjmp(context_buf)) { + void* bad_ptr = (int*)0xffffffffdeadbeef; + void(*f)() = bad_ptr; + f(); + printf("should not work!!! aboting test\n"); + return; + } else { + printf("segfault, good\n"); + } + printf("from NULL memory\n"); + // writing to existing protected memory + if(!setjmp(context_buf)) { + int *bad_ptr = (int*)NULL; + *(uint8_t*)bad_ptr = 0xc3; + } else { + printf("segfault, good\n"); + } + // writing to existing protected memory + if(!setjmp(context_buf)) { + int *bad_ptr = (int*)NULL; + if(*(uint8_t*)bad_ptr == 0xc3) + printf("should not be readable or writeable!\n"); + else + printf("should not be readable!\n"); + printf("aborting test\n"); + return; + } else { + printf("segfault, good\n"); + } + // writing to existing protected memory + if(!setjmp(context_buf)) { + void* bad_ptr = (int*)NULL; + void(*f)() = bad_ptr; + f(); + printf("should not work!!! aboting test\n"); + return; + } else { + printf("segfault, good\n"); + } + printf("from existant memory\n"); + printf("exec_p prot = 0\n"); + mprotect(exec_p, 65536, 0); + // writing to existing protected memory + if(!setjmp(context_buf)) { + *(uint8_t*)exec_p = 0xc3; + } else { + printf("segfault, good\n"); + } + // reading for exising protected memory + if(!setjmp(context_buf)) { + if(*(uint8_t*)exec_p == 0xc3) + printf("Error, this value should not be 0xc3\n"); + } else { + printf("segfault, good\n"); + } + printf("exec_p prot = PROT_READ\n"); + mprotect(exec_p, 65536, PROT_READ); + // writing to existing protected memory + if(!setjmp(context_buf)) { + *(uint8_t*)exec_p = 0xc3; + } else { + printf("segfault, good\n"); + } + // reading should work + if(!setjmp(context_buf)) { + if(*(uint8_t*)exec_p == 0xc3) + printf("Error, this value should not be 0xc3\n"); + } else { + printf("segfault, not good....\n"); + } + // reading should work + if(!setjmp(context_buf)) { + if(*(uint8_t*)exec_p == 0xc3) + printf("Error, this value should not be 0xc3\n"); + } else { + printf("segfault, good\n"); + } + printf("exec_p prot = PROT_READ|PROT_WRITE\n"); + mprotect(exec_p, 65536, PROT_READ|PROT_WRITE); + // writing should + if(!setjmp(context_buf)) { + *(uint8_t*)exec_p = 0xc3; + } else { + printf("segfault, not good, aborting test\n"); + return; + } + // reading should work + if(!setjmp(context_buf)) { + if(*(uint8_t*)exec_p != 0xc3) { + printf("Error, this value should be 0xc3, aborting test\n"); + return; + } + } else { + printf("segfault, not good....\n"); + } + // should not be able to run + if(!setjmp(context_buf)) { + vFv_t f = (vFv_t)exec_p; + f(); + } else { + printf("Cannot run, good\n"); + } + printf("exec_p prot = PROT_READ|PROT_WRITE|PROT_EXEC\n"); + mprotect(exec_p, 65536, PROT_READ|PROT_WRITE|PROT_EXEC); + if(!setjmp(context_buf)) { + vFv_t f = (vFv_t)exec_p; + f(); + } else { + printf("Cannot run, not good!\n"); + } + printf("exec_p prot = PROT_READ|PROT_WRITE\n"); + mprotect(exec_p, 65536, PROT_READ|PROT_WRITE); + memcpy(exec_p, buff_simplef, sizeof(buff_simplef)); + if(!setjmp(context_buf)) { + int(*f)() = exec_p; + if(f()!=1) { + printf("function return should be 1\n"); + } + } else { + printf("Cannot run, good!\n"); + } + printf("exec_p prot = PROT_READ|PROT_WRITE|PROT_EXEC\n"); + mprotect(exec_p, 65536, PROT_READ|PROT_WRITE|PROT_EXEC); + if(!setjmp(context_buf)) { + int(*f)() = exec_p; + if(f()!=1) { + printf("function return should be 1\n"); + } + ((uint8_t*)exec_p)[1] = 2; + if(f()!=2) { + printf("function return should be 2\n"); + } + } else { + printf("Cannot run, not good, aborting test!\n"); + } +} + +int main() +{ struct sigaction action = {0}; action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER; - action.sa_sigaction = segv_action; + action.sa_sigaction = (void*)segv_action; if(sigaction(SIGSEGV, &action, NULL)) { printf("sigaction: Err = %d\n", errno); return -2; @@ -86,11 +250,14 @@ int main() printf("sigaction 2: Err = %d\n", errno); return -2; } + exec_p = NULL; + test(); exec_p = mmap(NULL, 65536, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if(exec_p==MAP_FAILED) { printf("mmap: Err = %d\n", errno); return -3; } test_cc(); + test_segfault(); return 0; } |