From ea28907359a199aabdaa6aa0ac6765aef779318d Mon Sep 17 00:00:00 2001 From: Andrei Vlad LUTAS Date: Thu, 27 Aug 2020 16:25:39 +0300 Subject: [PATCH] Fix potential division error in bdshemu, when the destination operand is not large enough to hold the result. --- bdshemu/bdshemu.c | 187 +++++++++++++++++++++---- bdshemu_test/basic/test_64_div2 | Bin 0 -> 10 bytes bdshemu_test/basic/test_64_div2.asm | 6 + bdshemu_test/basic/test_64_div2.result | 19 +++ inc/version.h | 2 +- pybddisasm/setup.py | 2 +- 6 files changed, 185 insertions(+), 31 deletions(-) create mode 100644 bdshemu_test/basic/test_64_div2 create mode 100644 bdshemu_test/basic/test_64_div2.asm create mode 100644 bdshemu_test/basic/test_64_div2.result diff --git a/bdshemu/bdshemu.c b/bdshemu/bdshemu.c index b1d8591..9f1e5fd 100644 --- a/bdshemu/bdshemu.c +++ b/bdshemu/bdshemu.c @@ -663,7 +663,7 @@ ShemuSetGprValue( if (High8) { // AH, CH, DH or BH accessed. - *((uint8_t *)(&Context->Registers.RegRax + Reg - 4) + 1) = Value & 0xff; + *((uint8_t *)(&Context->Registers.RegRax + Reg - 4) + 1) = Value & 0xFF; } else { @@ -1509,6 +1509,116 @@ ShemuMultiply64Signed( } +// +// ShemuCheckDiv +// +static bool +ShemuCheckDiv( + uint64_t Divident, + uint64_t Divider, + uint8_t Size // The size of the Source (Divider). The Divident is twice as large. + ) +{ + // Returns true if all checks are OK, and Divident / Divider will not cause #DE. + + if (Divider == 0) + { + // Division by zero. + return false; + } + + // If the result won't fit in the destination, a #DE would be generated. + switch (Size) + { + case 1: + if (((Divident >> 8) & 0xFF) >= Divider) + { + return false; + } + break; + + case 2: + if (((Divident >> 16) & 0xFFFF) >= Divider) + { + return false; + } + break; + + case 4: + if (((Divident >> 32) & 0xFFFFFFFF) >= Divider) + { + return false; + } + break; + + default: + // 64 bit source division is not supported. + return false; + } + + return true; +} + + +// +// ShemuCheckIdiv +// +static bool +ShemuCheckIdiv( + int64_t Divident, + int64_t Divider, + uint8_t Size // The size of the Source (Divider). + ) +{ + bool neg1, neg2; + uint64_t quotient, max; + + neg1 = Divident < 0; + neg2 = Divider < 0; + + if (neg1) + { + Divident = -Divident; + } + + if (neg2) + { + Divider = -Divider; + } + + // Do checks when dividing positive values. + if (!ShemuCheckDiv(Divident, Divider, Size)) + { + return false; + } + + // Get the positive quotient. + quotient = (uint64_t)Divident / (uint64_t)Divider; + + max = (Size == 1) ? 0x80 : (Size == 2) ? 0x8000 : (Size == 4) ? 0x80000000 : 0x8000000000000000; + + if (neg1 ^ neg2) + { + // The Divident and the Divider have different signs, the quotient must be negative. If it's positive => #DE. + if (ND_GET_SIGN(Size, quotient) && quotient != max) + { + return false; + } + } + else + { + // Both the Divident and the Divider are positive/negative, so a positive result must be produced. If it's + // negative => #DE. + if (ND_GET_SIGN(Size, quotient)) + { + return false; + } + } + + return true; +} + + // // ShemuPrintContext // @@ -2417,22 +2527,31 @@ ShemuEmulate( if (src.Size == 1) { - if (src.Value.Bytes[0] == 0) - { - // Division by zero, force emulation stop. - stop = true; - break; - } + uint16_t divident; + + divident = (uint16_t)Context->Registers.RegRax; if (ND_INS_DIV == Context->Instruction.Instruction) { - res.Value.Bytes[0] = (uint8_t)((uint16_t)Context->Registers.RegRax / (uint8_t)src.Value.Bytes[0]); - res.Value.Bytes[1] = (uint8_t)((uint16_t)Context->Registers.RegRax % (uint8_t)src.Value.Bytes[0]); + if (!ShemuCheckDiv(divident, src.Value.Bytes[0], 1)) + { + stop = true; + break; + } + + res.Value.Bytes[0] = (uint8_t)(divident / src.Value.Bytes[0]); + res.Value.Bytes[1] = (uint8_t)(divident % src.Value.Bytes[0]); } else { - res.Value.Bytes[0] = (int8_t)((int16_t)Context->Registers.RegRax / (int8_t)src.Value.Bytes[0]); - res.Value.Bytes[1] = (int8_t)((int16_t)Context->Registers.RegRax % (int8_t)src.Value.Bytes[0]); + if (!ShemuCheckIdiv((int64_t)(int16_t)divident, (int64_t)(int8_t)src.Value.Bytes[0], 1)) + { + stop = true; + break; + } + + res.Value.Bytes[0] = (int8_t)((int16_t)divident / (int8_t)src.Value.Bytes[0]); + res.Value.Bytes[1] = (int8_t)((int16_t)divident % (int8_t)src.Value.Bytes[0]); } // Result in AX (AL - quotient, AH - reminder). @@ -2442,23 +2561,28 @@ ShemuEmulate( { uint32_t divident; - if (src.Value.Words[0] == 0) - { - // Division by zero, force emulation stop. - stop = true; - break; - } - divident = ((uint32_t)(uint16_t)Context->Registers.RegRdx << 16) | (uint32_t)(uint16_t)Context->Registers.RegRax; if (ND_INS_DIV == Context->Instruction.Instruction) { - res.Value.Words[0] = (uint16_t)((uint32_t)divident / (uint16_t)src.Value.Words[0]); - res.Value.Words[1] = (uint16_t)((uint32_t)divident % (uint16_t)src.Value.Words[0]); + if (!ShemuCheckDiv(divident, src.Value.Words[0], 2)) + { + stop = true; + break; + } + + res.Value.Words[0] = (uint16_t)(divident / src.Value.Words[0]); + res.Value.Words[1] = (uint16_t)(divident % src.Value.Words[0]); } else { + if (!ShemuCheckIdiv((int64_t)(int32_t)divident, (int64_t)(int16_t)src.Value.Words[0], 2)) + { + stop = true; + break; + } + res.Value.Words[0] = (int16_t)((int32_t)divident / (int16_t)src.Value.Words[0]); res.Value.Words[1] = (int16_t)((int32_t)divident % (int16_t)src.Value.Words[0]); } @@ -2470,23 +2594,28 @@ ShemuEmulate( { uint64_t divident; - if (src.Value.Dwords[0] == 0) - { - // Division by zero, force emulation stop. - stop = true; - break; - } - divident = ((uint64_t)(uint32_t)Context->Registers.RegRdx << 32) | (uint64_t)(uint32_t)Context->Registers.RegRax; if (ND_INS_DIV == Context->Instruction.Instruction) { - res.Value.Dwords[0] = (uint32_t)((uint64_t)divident / (uint32_t)src.Value.Dwords[0]); - res.Value.Dwords[1] = (uint32_t)((uint64_t)divident % (uint32_t)src.Value.Dwords[0]); + if (!ShemuCheckDiv(divident, src.Value.Dwords[0], 4)) + { + stop = true; + break; + } + + res.Value.Dwords[0] = (uint32_t)(divident / src.Value.Dwords[0]); + res.Value.Dwords[1] = (uint32_t)(divident % src.Value.Dwords[0]); } else { + if (!ShemuCheckIdiv((int64_t)divident, (int64_t)(int32_t)src.Value.Dwords[0], 4)) + { + stop = true; + break; + } + res.Value.Dwords[0] = (int32_t)((int64_t)divident / (int32_t)src.Value.Dwords[0]); res.Value.Dwords[1] = (int32_t)((int64_t)divident % (int32_t)src.Value.Dwords[0]); } diff --git a/bdshemu_test/basic/test_64_div2 b/bdshemu_test/basic/test_64_div2 new file mode 100644 index 0000000000000000000000000000000000000000..2775074df83b33eb8fc8441ae3d49bedef2c6c59 GIT binary patch literal 10 RcmdnRz`)S({rdO+hXEKP1;PLT literal 0 HcmV?d00001 diff --git a/bdshemu_test/basic/test_64_div2.asm b/bdshemu_test/basic/test_64_div2.asm new file mode 100644 index 0000000..d7cab6d --- /dev/null +++ b/bdshemu_test/basic/test_64_div2.asm @@ -0,0 +1,6 @@ + bits 64 + + MOV edx, 0x80000000 + NOT edi + IDIV edi + RETN \ No newline at end of file diff --git a/bdshemu_test/basic/test_64_div2.result b/bdshemu_test/basic/test_64_div2.result new file mode 100644 index 0000000..439e74a --- /dev/null +++ b/bdshemu_test/basic/test_64_div2.result @@ -0,0 +1,19 @@ + RAX = 0x0000000000000000 RCX = 0x0000000000000000 RDX = 0x0000000000000000 RBX = 0x0000000000000000 + RSP = 0x0000000000101000 RBP = 0x0000000000000000 RSI = 0x0000000000000000 RDI = 0x0000000000000000 + R8 = 0x0000000000000000 R9 = 0x0000000000000000 R10 = 0x0000000000000000 R11 = 0x0000000000000000 + R12 = 0x0000000000000000 R13 = 0x0000000000000000 R14 = 0x0000000000000000 R15 = 0x0000000000000000 + RIP = 0x0000000000200000 RFLAGS = 0x0000000000000202 +Emulating: 0x0000000000200000 MOV edx, 0x80000000 + RAX = 0x0000000000000000 RCX = 0x0000000000000000 RDX = 0x0000000080000000 RBX = 0x0000000000000000 + RSP = 0x0000000000101000 RBP = 0x0000000000000000 RSI = 0x0000000000000000 RDI = 0x0000000000000000 + R8 = 0x0000000000000000 R9 = 0x0000000000000000 R10 = 0x0000000000000000 R11 = 0x0000000000000000 + R12 = 0x0000000000000000 R13 = 0x0000000000000000 R14 = 0x0000000000000000 R15 = 0x0000000000000000 + RIP = 0x0000000000200005 RFLAGS = 0x0000000000000202 +Emulating: 0x0000000000200005 NOT edi + RAX = 0x0000000000000000 RCX = 0x0000000000000000 RDX = 0x0000000080000000 RBX = 0x0000000000000000 + RSP = 0x0000000000101000 RBP = 0x0000000000000000 RSI = 0x0000000000000000 RDI = 0x00000000ffffffff + R8 = 0x0000000000000000 R9 = 0x0000000000000000 R10 = 0x0000000000000000 R11 = 0x0000000000000000 + R12 = 0x0000000000000000 R13 = 0x0000000000000000 R14 = 0x0000000000000000 R15 = 0x0000000000000000 + RIP = 0x0000000000200007 RFLAGS = 0x0000000000000202 +Emulating: 0x0000000000200007 IDIV edi +Emulation terminated with status 0x0000000a, flags: 0x0, 0 NOPs diff --git a/inc/version.h b/inc/version.h index 11affb4..19a6096 100644 --- a/inc/version.h +++ b/inc/version.h @@ -7,6 +7,6 @@ #define DISASM_VERSION_MAJOR 1 #define DISASM_VERSION_MINOR 28 -#define DISASM_VERSION_REVISION 1 +#define DISASM_VERSION_REVISION 2 #endif // DISASM_VER_H diff --git a/pybddisasm/setup.py b/pybddisasm/setup.py index 1ae49f3..0e59650 100644 --- a/pybddisasm/setup.py +++ b/pybddisasm/setup.py @@ -12,7 +12,7 @@ from setuptools import find_packages, setup, Command, Extension, Distribution from codecs import open VERSION = (0, 1, 3) -LIBRARY_VERSION = (1, 28, 1) +LIBRARY_VERSION = (1, 28, 2) LIBRARY_INSTRUX_SIZE = 856 packages = ['pybddisasm']