diff options
author | Cyrill Gorcunov <gorcunov@gmail.com> | 2018-08-15 01:12:19 +0300 |
---|---|---|
committer | Cyrill Gorcunov <gorcunov@gmail.com> | 2018-08-16 01:20:01 +0300 |
commit | 55d09bbf6f7087339277b1e3b17c134b2afb2510 (patch) | |
tree | e5d1ff6088c1724f28c4af318479a9382167b457 | |
parent | b8d153eb4dee2ac22fc09cfba99dbae48c724b88 (diff) | |
download | nasm-55d09bbf6f7087339277b1e3b17c134b2afb2510.tar.gz |
disasm: Fix buffer overread in ndisasm
https://nvd.nist.gov/vuln/detail/CVE-2018-10254
https://sourceforge.net/p/nasm/bugs/561/
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
-rw-r--r-- | disasm/disasm.c | 31 | ||||
-rw-r--r-- | disasm/disasm.h | 4 | ||||
-rw-r--r-- | disasm/ndisasm.c | 5 |
3 files changed, 33 insertions, 7 deletions
diff --git a/disasm/disasm.c b/disasm/disasm.c index fa52d30f..fd3eb42a 100644 --- a/disasm/disasm.c +++ b/disasm/disasm.c @@ -49,6 +49,15 @@ #include "regdis.h" #include "disp8.h" +#define fetch_safe(_start, _ptr, _size, _need, _op) \ + do { \ + if (((_ptr) - (_start)) >= ((_size) - (_need))) \ + _op; \ + } while (0) + +#define fetch_or_return(_start, _ptr, _size, _need) \ + fetch_safe(_start, _ptr, _size, _need, return 0) + /* * Flags that go into the `segment' field of `insn' structures * during disassembly. @@ -1108,8 +1117,8 @@ static const char * const condition_name[16] = { "s", "ns", "pe", "po", "l", "nl", "ng", "g" }; -int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, - int64_t offset, int autosync, iflag_t *prefer) +int32_t disasm(uint8_t *data, int32_t data_size, char *output, int outbufsize, int segsize, + int64_t offset, int autosync, iflag_t *prefer) { const struct itemplate * const *p, * const *best_p; const struct disasm_index *ix; @@ -1144,41 +1153,52 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, switch (*data) { case 0xF2: case 0xF3: + fetch_or_return(origdata, data, data_size, 1); prefix.rep = *data++; break; case 0x9B: + fetch_or_return(origdata, data, data_size, 1); prefix.wait = *data++; break; case 0xF0: + fetch_or_return(origdata, data, data_size, 1); prefix.lock = *data++; break; case 0x2E: + fetch_or_return(origdata, data, data_size, 1); segover = "cs", prefix.seg = *data++; break; case 0x36: + fetch_or_return(origdata, data, data_size, 1); segover = "ss", prefix.seg = *data++; break; case 0x3E: + fetch_or_return(origdata, data, data_size, 1); segover = "ds", prefix.seg = *data++; break; case 0x26: + fetch_or_return(origdata, data, data_size, 1); segover = "es", prefix.seg = *data++; break; case 0x64: + fetch_or_return(origdata, data, data_size, 1); segover = "fs", prefix.seg = *data++; break; case 0x65: + fetch_or_return(origdata, data, data_size, 1); segover = "gs", prefix.seg = *data++; break; case 0x66: + fetch_or_return(origdata, data, data_size, 1); prefix.osize = (segsize == 16) ? 32 : 16; prefix.osp = *data++; break; case 0x67: + fetch_or_return(origdata, data, data_size, 1); prefix.asize = (segsize == 32) ? 16 : 32; prefix.asp = *data++; break; @@ -1186,6 +1206,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, case 0xC4: case 0xC5: if (segsize == 64 || (data[1] & 0xc0) == 0xc0) { + fetch_or_return(origdata, data, data_size, 2); prefix.vex[0] = *data++; prefix.vex[1] = *data++; @@ -1193,6 +1214,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, prefix.vex_c = RV_VEX; if (prefix.vex[0] == 0xc4) { + fetch_or_return(origdata, data, data_size, 1); prefix.vex[2] = *data++; prefix.rex |= (~prefix.vex[1] >> 5) & 7; /* REX_RXB */ prefix.rex |= (prefix.vex[2] >> (7-3)) & REX_W; @@ -1214,6 +1236,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, case 0x62: { if (segsize == 64 || ((data[1] & 0xc0) == 0xc0)) { + fetch_or_return(origdata, data, data_size, 4); data++; /* 62h EVEX prefix */ prefix.evex[0] = *data++; prefix.evex[1] = *data++; @@ -1237,6 +1260,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, case 0x8F: if ((data[1] & 030) != 0 && (segsize == 64 || (data[1] & 0xc0) == 0xc0)) { + fetch_or_return(origdata, data, data_size, 3); prefix.vex[0] = *data++; prefix.vex[1] = *data++; prefix.vex[2] = *data++; @@ -1272,6 +1296,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, case REX_P + 0xE: case REX_P + 0xF: if (segsize == 64) { + fetch_or_return(origdata, data, data_size, 1); prefix.rex = *data++; if (prefix.rex & REX_W) prefix.osize = 64; @@ -1293,8 +1318,10 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, return 0; /* No instruction table at all... */ dp = data; + fetch_or_return(origdata, dp, data_size, 1); ix += *dp++; while (ix->n == -1) { + fetch_or_return(origdata, dp, data_size, 1); ix = (const struct disasm_index *)ix->p + *dp++; } diff --git a/disasm/disasm.h b/disasm/disasm.h index 6c89293e..053474d2 100644 --- a/disasm/disasm.h +++ b/disasm/disasm.h @@ -42,8 +42,8 @@ #define INSN_MAX 32 /* one instruction can't be longer than this */ -int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, - int64_t offset, int autosync, iflag_t *prefer); +int32_t disasm(uint8_t *data, int32_t data_size, char *output, int outbufsize, int segsize, + int64_t offset, int autosync, iflag_t *prefer); int32_t eatbyte(uint8_t *data, char *output, int outbufsize, int segsize); #endif diff --git a/disasm/ndisasm.c b/disasm/ndisasm.c index a4a217db..2d0cf153 100644 --- a/disasm/ndisasm.c +++ b/disasm/ndisasm.c @@ -316,9 +316,8 @@ int main(int argc, char **argv) nextsync = next_sync(offset, &synclen); } while (p > q && (p - q >= INSN_MAX || lenread == 0)) { - lendis = - disasm((uint8_t *) q, outbuf, sizeof(outbuf), bits, - offset, autosync, &prefer); + lendis = disasm((uint8_t *)q, INSN_MAX, outbuf, sizeof(outbuf), + bits, offset, autosync, &prefer); if (!lendis || lendis > (p - q) || ((nextsync || synclen) && (uint32_t)lendis > nextsync - offset)) |