Last active
June 17, 2022 00:23
-
-
Save jcmvbkbc/88bd49600290ee5114418056a4806113 to your computer and use it in GitHub Desktop.
division-by-0-fixer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <signal.h> | |
#if defined(__x86_64__) || defined(__i386__) | |
static int fixup_div0(siginfo_t *si, ucontext_t *uc) | |
{ | |
const uint8_t *insnbuf = si->si_addr; | |
int d8 = 0; | |
int sib = 0; | |
int i = 0; | |
//fprintf(stderr, "%s: si_addr = %p\n", __func__, si->si_addr); | |
for (;;) { | |
/* check for the prefix */ | |
switch (insnbuf[i]) { | |
case 0xf0: /* LOCK */ | |
case 0xf2: /* REPNZ */ | |
case 0xf3: /* REP */ | |
case 0x2e: /* CS */ | |
case 0x36: /* SS */ | |
case 0x3e: /* DS */ | |
case 0x26: /* ES */ | |
case 0x64: /* FS */ | |
case 0x65: /* GS */ | |
case 0x66: /* operand size */ | |
case 0x67: /* address size */ | |
++i; | |
continue; | |
default: | |
#ifdef __x86_64__ | |
/* REX prefixes */ | |
if ((insnbuf[i] & 0xf0) == 0x40) { | |
++i; | |
continue; | |
} | |
#endif | |
break; | |
} | |
break; | |
} | |
switch (insnbuf[i]) { | |
case 0xf6: /* IDIV r/m8 */ | |
d8 = 1; | |
/* fallthrough */ | |
case 0xf7: /* IDIV */ | |
++i; | |
/* Reg/opcode field in ModR/M byte */ | |
if ((insnbuf[i] & 0x30) == 0x30) | |
break; | |
/* fallthrough */ | |
default: | |
/* unknown opcode */ | |
return 0; | |
} | |
/* ModR/M analysis */ | |
switch (insnbuf[i] & 0xc7) { | |
case 0x04: | |
case 0x44: | |
case 0x84: | |
sib = 1; | |
break; | |
} | |
if ((insnbuf[i] & 0xc7) == 0x05) { | |
/* disp32 */ | |
i += 4; | |
} else if ((insnbuf[i] & 0xc0) == 0x40) { | |
/* disp8 */ | |
++i; | |
} else if ((insnbuf[i] & 0xc0) == 0x80) { | |
/* disp32 */ | |
i += 4; | |
} | |
i += 1 + sib; | |
//fprintf(stderr, "insn length = %d\n", i); | |
#ifdef __x86_64__ | |
#define IP REG_RIP | |
#define AX REG_RAX | |
#define DX REG_RDX | |
#endif | |
#ifdef __i386__ | |
#define IP REG_EIP | |
#define AX REG_EAX | |
#define DX REG_EDX | |
#endif | |
uc->uc_mcontext.gregs[IP] += i; | |
uc->uc_mcontext.gregs[AX] = 0xbadc0de; | |
if (!d8) | |
uc->uc_mcontext.gregs[DX] = 0; | |
return 1; | |
} | |
#endif | |
#if defined(__xtensa__) | |
#if defined(__XTENSA_EL__) | |
#define OP_MASK 0x00ff000f | |
#define OP_QUOU 0x00c20000 | |
#define OP_QUOS 0x00d20000 | |
#define OP_REMU 0x00e20000 | |
#define OP_REMS 0x00f20000 | |
#define R_MASK 0x0000f000 | |
#define R_SHIFT 12 | |
#elif defined(__XTENSA_EB__) | |
#define OP_MASK 0xf000ff00 | |
#define OP_QUOU 0x00002c00 | |
#define OP_QUOS 0x00002d00 | |
#define OP_REMU 0x00002e00 | |
#define OP_REMS 0x00002f00 | |
#define R_MASK 0x000f0000 | |
#define R_SHIFT 16 | |
#else | |
#error Unsupported endianness | |
#endif | |
static int fixup_div0(siginfo_t *si, ucontext_t *uc) | |
{ | |
uint32_t opcode = 0; | |
memcpy(&opcode, si->si_addr, 3); | |
switch (opcode & OP_MASK) { | |
case OP_QUOU: | |
case OP_QUOS: | |
case OP_REMU: | |
case OP_REMS: | |
break; | |
default: | |
return 0; | |
} | |
uc->uc_mcontext.sc_pc += 3; | |
uc->uc_mcontext.sc_a[(opcode & R_MASK) >> R_SHIFT] = 0xbadc0de; | |
return 1; | |
} | |
#endif | |
void fpe_handler(int s, siginfo_t *si, void *c) | |
{ | |
if (!fixup_div0(si, c)) | |
abort(); | |
} | |
int8_t s8(int8_t a, int8_t b) | |
{ | |
return a / b; | |
} | |
int32_t s32(int32_t a, int32_t b) | |
{ | |
return a / b; | |
} | |
int64_t s64(int64_t a, int64_t b) | |
{ | |
return a / b; | |
} | |
uint8_t u8(uint8_t a, uint8_t b) | |
{ | |
return a / b; | |
} | |
uint32_t u32(uint32_t a, uint32_t b) | |
{ | |
return a / b; | |
} | |
uint64_t u64(uint64_t a, uint64_t b) | |
{ | |
return a / b; | |
} | |
int main() | |
{ | |
int a = 0; | |
int b = 0; | |
long c = -1; | |
struct sigaction old_action; | |
struct sigaction new_action = { | |
.sa_sigaction = fpe_handler, | |
.sa_flags = SA_SIGINFO, | |
}; | |
sigaction(SIGFPE, &new_action, &old_action); | |
c = s8(a, b); | |
printf("c = %lx\n", c); | |
c = s32(a, b); | |
printf("c = %lx\n", c); | |
c = s64(a, b); | |
printf("c = %lx\n", c); | |
c = u8(a, b); | |
printf("c = %lx\n", c); | |
c = u32(a, b); | |
printf("c = %lx\n", c); | |
c = u64(a, b); | |
printf("c = %lx\n", c); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment