CVE-2019-12951: Out-of-bounds Write (CWE-787)
value: An issue was discovered in Mongoose before 6.15. The parse_mqtt() function in mg_mqtt.c has a critical heap-based buffer overflow.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2019-06-24T23:15Z
Repository: https://github.com/cesanta/mongoose
Language: C
Files changed: mongoose.c, src/mg_mqtt.c
Commit date: 2019-06-13 08:18:14 +0000
Commit message: Fix heap-based overflow in parse_mqtt

PUBLISHED_FROM=3306592896298597fff5269634df0c1a1555113b

Diff: diff --git a/mongoose.c b/mongoose.c
index 1df32a80..9571d574 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -10839,33 +10839,31 @@ static const char *scanto(const char *p, struct mg_str *s) {
 }
 
 MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   uint8_t header;
   size_t len = 0, len_len = 0;
-  const char *p, *end;
+  const char *p, *end, *eop = &io->buf[io->len];
   unsigned char lc = 0;
   int cmd;
 
   if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG;
   header = io->buf[0];
   cmd = header >> 4;
 
   /* decode mqtt variable length */
   len = len_len = 0;
   p = io->buf + 1;
-  while ((size_t)(p - io->buf) < io->len) {
+  while (p < eop) {
     lc = *((const unsigned char *) p++);
     len += (lc & 0x7f) << 7 * len_len;
     len_len++;
     if (!(lc & 0x80)) break;
     if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
   }
 
   end = p + len;
-  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
-    return MG_MQTT_ERROR_INCOMPLETE_MSG;
-  }
+  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
 
   mm->cmd = cmd;
   mm->qos = MG_MQTT_GET_QOS(header);
 
   switch (cmd) {
@@ -10915,11 +10913,13 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
     case MG_MQTT_CMD_PUBACK:
     case MG_MQTT_CMD_PUBREC:
     case MG_MQTT_CMD_PUBREL:
     case MG_MQTT_CMD_PUBCOMP:
     case MG_MQTT_CMD_SUBACK:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
       mm->message_id = getu16(p);
+      p += 2;
       break;
     case MG_MQTT_CMD_PUBLISH: {
       p = scanto(p, &mm->topic);
       if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
       if (mm->qos > 0) {
diff --git a/src/mg_mqtt.c b/src/mg_mqtt.c
index 68069e8d..9ab66bc6 100644
--- a/src/mg_mqtt.c
+++ b/src/mg_mqtt.c
@@ -22,33 +22,31 @@ static const char *scanto(const char *p, struct mg_str *s) {
 }
 
 MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
   uint8_t header;
   size_t len = 0, len_len = 0;
-  const char *p, *end;
+  const char *p, *end, *eop = &io->buf[io->len];
   unsigned char lc = 0;
   int cmd;
 
   if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG;
   header = io->buf[0];
   cmd = header >> 4;
 
   /* decode mqtt variable length */
   len = len_len = 0;
   p = io->buf + 1;
-  while ((size_t)(p - io->buf) < io->len) {
+  while (p < eop) {
     lc = *((const unsigned char *) p++);
     len += (lc & 0x7f) << 7 * len_len;
     len_len++;
     if (!(lc & 0x80)) break;
     if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
   }
 
   end = p + len;
-  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
-    return MG_MQTT_ERROR_INCOMPLETE_MSG;
-  }
+  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
 
   mm->cmd = cmd;
   mm->qos = MG_MQTT_GET_QOS(header);
 
   switch (cmd) {
@@ -98,11 +96,13 @@ MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
     case MG_MQTT_CMD_PUBACK:
     case MG_MQTT_CMD_PUBREC:
     case MG_MQTT_CMD_PUBREL:
     case MG_MQTT_CMD_PUBCOMP:
     case MG_MQTT_CMD_SUBACK:
+      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
       mm->message_id = getu16(p);
+      p += 2;
       break;
     case MG_MQTT_CMD_PUBLISH: {
       p = scanto(p, &mm->topic);
       if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
       if (mm->qos > 0) {

Vulnerable code:   const char *p, *end;
  while ((size_t)(p - io->buf) < io->len) {
  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
    return MG_MQTT_ERROR_INCOMPLETE_MSG;
  }
  const char *p, *end;
  while ((size_t)(p - io->buf) < io->len) {
  if (lc & 0x80 || len > (io->len - (p - io->buf))) {
    return MG_MQTT_ERROR_INCOMPLETE_MSG;
  }
Fixed code:   const char *p, *end, *eop = &io->buf[io->len];
  while (p < eop) {
  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
      p += 2;
  const char *p, *end, *eop = &io->buf[io->len];
  while (p < eop) {
  if (lc & 0x80 || end > eop) return MG_MQTT_ERROR_INCOMPLETE_MSG;
      if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
      p += 2;