commit cfa3559b8ec9e693142638eedb4d6340ecf01c90
Author: Adam Majer <amajer@suse.de>
Date:   Thu Jun 28 13:03:06 2018 +0200

    Verify we do not read longer than the buffer
    
    Netwide Assembler (NASM) 2.13 has a stack-based buffer over-read
    in the disasm function of the disasm/disasm.c file.
    
    Bug: 3392475
    Signed-off-by: Adam Majer <amajer@suse.de>

diff --git a/disasm/disasm.c b/disasm/disasm.c
index a75d839e..a503434b 100644
--- a/disasm/disasm.c
+++ b/disasm/disasm.c
@@ -474,8 +474,10 @@ static uint8_t *do_ea(uint8_t *data, int modrm, int asize,
  * stream in data. Return the number of bytes matched if so.
  */
 #define case4(x) case (x): case (x)+1: case (x)+2: case (x)+3
+#define check_can_read_data_byte() if (--remaining_bytes < 0) return 0
+#define check_can_read_data_bytes(n) { if (remaining_bytes < n) return 0; remaining_bytes -= n; } while(0)
 
-static int matches(const struct itemplate *t, uint8_t *data,
+static int matches(const struct itemplate *t, uint8_t *data, int remaining_bytes,
                    const struct prefix_info *prefix, int segsize, insn *ins)
 {
     uint8_t *r = (uint8_t *)(t->code);
@@ -525,9 +527,11 @@ static int matches(const struct itemplate *t, uint8_t *data,
         case 02:
         case 03:
         case 04:
-            while (c--)
+            while (c--) {
+                check_can_read_data_byte();
                 if (*r++ != *data++)
                     return 0;
+            }
             break;
 
         case 05:
@@ -538,7 +542,9 @@ static int matches(const struct itemplate *t, uint8_t *data,
 
         case4(010):
         {
-            int t = *r++, d = *data++;
+            int d, t = *r++;
+            check_can_read_data_byte();
+            d = *data++;
             if (d < t || d > t + 7)
                 return 0;
             else {
@@ -555,28 +561,34 @@ static int matches(const struct itemplate *t, uint8_t *data,
             break;
 
         case4(0274):
+            check_can_read_data_byte();
             opx->offset = (int8_t)*data++;
             opx->segment |= SEG_SIGNED;
             break;
 
         case4(020):
+            check_can_read_data_byte();
             opx->offset = *data++;
             break;
 
         case4(024):
+            check_can_read_data_byte();
             opx->offset = *data++;
             break;
 
         case4(030):
+            check_can_read_data_bytes(2);
             opx->offset = getu16(data);
             data += 2;
             break;
 
         case4(034):
             if (osize == 32) {
+                check_can_read_data_bytes(4);
                 opx->offset = getu32(data);
                 data += 4;
             } else {
+                check_can_read_data_bytes(2);
                 opx->offset = getu16(data);
                 data += 2;
             }
@@ -585,11 +597,13 @@ static int matches(const struct itemplate *t, uint8_t *data,
             break;
 
         case4(040):
+            check_can_read_data_bytes(4);
             opx->offset = getu32(data);
             data += 4;
             break;
 
         case4(0254):
+            check_can_read_data_bytes(4);
             opx->offset = gets32(data);
             data += 4;
             break;
@@ -597,18 +611,21 @@ static int matches(const struct itemplate *t, uint8_t *data,
         case4(044):
             switch (asize) {
             case 16:
+                check_can_read_data_bytes(2);
                 opx->offset = getu16(data);
                 data += 2;
                 if (segsize != 16)
                     opx->disp_size = 16;
                 break;
             case 32:
+                check_can_read_data_bytes(4);
                 opx->offset = getu32(data);
                 data += 4;
                 if (segsize == 16)
                     opx->disp_size = 32;
                 break;
             case 64:
+                check_can_read_data_bytes(8);
                 opx->offset = getu64(data);
                 opx->disp_size = 64;
                 data += 8;
@@ -617,16 +634,19 @@ static int matches(const struct itemplate *t, uint8_t *data,
             break;
 
         case4(050):
+            check_can_read_data_byte();
             opx->offset = gets8(data++);
             opx->segment |= SEG_RELATIVE;
             break;
 
         case4(054):
+            check_can_read_data_bytes(8);
             opx->offset = getu64(data);
             data += 8;
             break;
 
         case4(060):
+            check_can_read_data_bytes(2);
             opx->offset = gets16(data);
             data += 2;
             opx->segment |= SEG_RELATIVE;
@@ -637,6 +657,7 @@ static int matches(const struct itemplate *t, uint8_t *data,
             opx->segment |= SEG_RELATIVE;
             /* In long mode rel is always 32 bits, sign extended. */
             if (segsize == 64 || osize == 32) {
+                check_can_read_data_bytes(4);
                 opx->offset = gets32(data);
                 data += 4;
                 if (segsize != 64)
@@ -644,6 +665,7 @@ static int matches(const struct itemplate *t, uint8_t *data,
                 opx->type = (opx->type & ~SIZE_MASK)
                     | (segsize == 64 ? BITS64 : BITS32);
             } else {
+                check_can_read_data_bytes(2);
                 opx->offset = gets16(data);
                 data += 2;
                 opx->segment &= ~SEG_32BIT;
@@ -652,6 +674,7 @@ static int matches(const struct itemplate *t, uint8_t *data,
             break;
 
         case4(070):
+            check_can_read_data_bytes(4);
             opx->offset = gets32(data);
             data += 4;
             opx->segment |= SEG_32BIT | SEG_RELATIVE;
@@ -662,11 +685,19 @@ static int matches(const struct itemplate *t, uint8_t *data,
         case4(0120):
         case4(0130):
         {
-            int modrm = *data++;
+            int modrm;
+            uint8_t *new_data;
+
+            check_can_read_data_byte();
+            modrm = *data++;
             opx->segment |= SEG_RMREG;
-            data = do_ea(data, modrm, asize, segsize, eat, opy, ins);
-            if (!data)
+            new_data = do_ea(data, modrm, asize, segsize, eat, opy, ins);
+            if (!new_data)
+                return 0;
+            remaining_bytes = data + remaining_bytes - new_data;
+            if (remaining_bytes < 0)
                 return 0;
+            data = new_data;
             opx->basereg = ((modrm >> 3) & 7) + (ins->rex & REX_R ? 8 : 0);
             if ((ins->rex & REX_EV) && (segsize == 64))
                 opx->basereg += (ins->evex_p[0] & EVEX_P0RP ? 0 : 16);
@@ -675,7 +706,10 @@ static int matches(const struct itemplate *t, uint8_t *data,
 
         case 0172:
             {
-                uint8_t ximm = *data++;
+                uint8_t ximm;
+
+                check_can_read_data_byte();
+                ximm = *data++;
                 c = *r++;
                 ins->oprs[c >> 3].basereg = (ximm >> 4) & regmask;
                 ins->oprs[c >> 3].segment |= SEG_RMREG;
@@ -685,7 +719,10 @@ static int matches(const struct itemplate *t, uint8_t *data,
 
         case 0173:
             {
-                uint8_t ximm = *data++;
+                uint8_t ximm;
+
+                check_can_read_data_byte();
+                ximm = *data++;
                 c = *r++;
 
                 if ((c ^ ximm) & 15)
@@ -698,7 +735,10 @@ static int matches(const struct itemplate *t, uint8_t *data,
 
         case4(0174):
             {
-                uint8_t ximm = *data++;
+                uint8_t ximm;
+
+                check_can_read_data_byte();
+                ximm = *data++;
 
                 opx->basereg = (ximm >> 4) & regmask;
                 opx->segment |= SEG_RMREG;
@@ -714,12 +754,20 @@ static int matches(const struct itemplate *t, uint8_t *data,
         case4(0230):
         case4(0234):
         {
-            int modrm = *data++;
+            int modrm;
+            uint8_t *new_data;
+
+            check_can_read_data_byte();
+            modrm = *data++;
             if (((modrm >> 3) & 07) != (c & 07))
                 return 0;   /* spare field doesn't match up */
-            data = do_ea(data, modrm, asize, segsize, eat, opy, ins);
-            if (!data)
+            new_data = do_ea(data, modrm, asize, segsize, eat, opy, ins);
+            if (!new_data)
+                return 0;
+            remaining_bytes = data + remaining_bytes - new_data;
+            if (remaining_bytes < 0)
                 return 0;
+            data = new_data;
             break;
         }
 
@@ -935,7 +983,10 @@ static int matches(const struct itemplate *t, uint8_t *data,
 
         case 0330:
         {
-            int t = *r++, d = *data++;
+            int t = *r++, d;
+
+            check_can_read_data_byte();
+            d = *data++;
             if (d < t || d > t + 15)
                 return 0;
             else
@@ -1126,6 +1177,8 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
     bool end_prefix;
     bool is_evex;
 
+    int remaining_bytes = INSN_MAX;
+
     memset(&ins, 0, sizeof ins);
 
     /*
@@ -1141,6 +1194,8 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
 
     end_prefix = false;
     while (!end_prefix) {
+        check_can_read_data_byte();
+
         switch (*data) {
         case 0xF2:
         case 0xF3:
@@ -1185,6 +1240,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
 
         case 0xC4:
         case 0xC5:
+            check_can_read_data_byte();
             if (segsize == 64 || (data[1] & 0xc0) == 0xc0) {
                 prefix.vex[0] = *data++;
                 prefix.vex[1] = *data++;
@@ -1193,6 +1249,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
                 prefix.vex_c = RV_VEX;
 
                 if (prefix.vex[0] == 0xc4) {
+                    check_can_read_data_byte();
                     prefix.vex[2] = *data++;
                     prefix.rex |= (~prefix.vex[1] >> 5) & 7; /* REX_RXB */
                     prefix.rex |= (prefix.vex[2] >> (7-3)) & REX_W;
@@ -1213,7 +1270,8 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
 
         case 0x62:
         {
-            if (segsize == 64 || ((data[1] & 0xc0) == 0xc0)) {
+            if (segsize == 64 || (remaining_bytes > 3 && (data[1] & 0xc0) == 0xc0)) {
+                check_can_read_data_bytes(3);
                 data++;        /* 62h EVEX prefix */
                 prefix.evex[0] = *data++;
                 prefix.evex[1] = *data++;
@@ -1235,8 +1293,10 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
         }
 
         case 0x8F:
-            if ((data[1] & 030) != 0 &&
+             check_can_read_data_byte();
+             if ((data[1] & 030) != 0 &&
                     (segsize == 64 || (data[1] & 0xc0) == 0xc0)) {
+                check_can_read_data_byte();
                 prefix.vex[0] = *data++;
                 prefix.vex[1] = *data++;
                 prefix.vex[2] = *data++;
@@ -1280,6 +1340,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
             break;
 
         default:
+            remaining_bytes++; /* didn't actually use the last byte */
             end_prefix = true;
             break;
         }
@@ -1293,14 +1354,17 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize,
         return 0;        /* No instruction table at all... */
 
     dp = data;
+
+    check_can_read_data_byte();
     ix += *dp++;
     while (ix->n == -1) {
+        check_can_read_data_byte();
         ix = (const struct disasm_index *)ix->p + *dp++;
     }
 
     p = (const struct itemplate * const *)ix->p;
     for (n = ix->n; n; n--, p++) {
-        if ((length = matches(*p, data, &prefix, segsize, &tmp_ins))) {
+        if ((length = matches(*p, data, remaining_bytes, &prefix, segsize, &tmp_ins))) {
             works = true;
             /*
              * Final check to make sure the types of r/m match up.
