From 961fb96bb7a0aa88bef0b71182cbbcafe3108201 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=91=D0=BE=D0=B1=D0=B1=D0=B8?= <lsm@cesanta.com>
Date: Sat, 27 Jan 2018 01:35:00 +0000
Subject: [PATCH] Fix OOB access in mg_match_prefix_n()

CL: Fix OOB access in mg_match_prefix_n().
    Made mg_match_prefix_n() return non-negative result.

PUBLISHED_FROM=611454df6a6c55bfa7ddf05e7d268a86fa0457a8
---
 mongoose.c | 48 ++++++++++++++++++++++--------------------------
 mongoose.h | 13 ++++++-------
 2 files changed, 28 insertions(+), 33 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index c9b4f0694..f80b4daf3 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -612,8 +612,8 @@ int cs_log_print_prefix(enum cs_log_level level, const char *func,
 
   if (level > cs_log_threshold) return 0;
   if (s_filter_pattern != NULL &&
-      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) < 0 &&
-      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) < 0) {
+      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
+      mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
     return 0;
   }
 
@@ -1632,9 +1632,9 @@ const char *mg_strstr(const struct mg_str haystack,
 
 #ifndef EXCLUDE_COMMON
 
+/* Amalgamated: #include "common/str_util.h" */
 /* Amalgamated: #include "common/mg_mem.h" */
 /* Amalgamated: #include "common/platform.h" */
-/* Amalgamated: #include "common/str_util.h" */
 
 #ifndef C_DISABLE_BUILTIN_SNPRINTF
 #define C_DISABLE_BUILTIN_SNPRINTF 0
@@ -2090,11 +2090,10 @@ struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
   return list;
 }
 
-int mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
-int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
+size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
+size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
   const char *or_str;
-  size_t len, i = 0, j = 0;
-  int res;
+  size_t res = 0, len = 0, i = 0, j = 0;
 
   if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL ||
       (or_str = (const char *) memchr(pattern.p, ',', pattern.len)) != NULL) {
@@ -2106,11 +2105,9 @@ int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
     return mg_match_prefix_n(pstr, str);
   }
 
-  for (; i < pattern.len; i++, j++) {
-    if (pattern.p[i] == '?' && j != str.len) {
+  for (; i < pattern.len && j < str.len; i++, j++) {
+    if (pattern.p[i] == '?') {
       continue;
-    } else if (pattern.p[i] == '$') {
-      return j == str.len ? (int) j : -1;
     } else if (pattern.p[i] == '*') {
       i++;
       if (i < pattern.len && pattern.p[i] == '*') {
@@ -2118,29 +2115,29 @@ int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
         len = str.len - j;
       } else {
         len = 0;
-        while (j + len != str.len && str.p[j + len] != '/') {
-          len++;
-        }
+        while (j + len < str.len && str.p[j + len] != '/') len++;
       }
-      if (i == pattern.len) {
+      if (i == pattern.len || (pattern.p[i] == '$' && i == pattern.len - 1))
         return j + len;
-      }
       do {
         const struct mg_str pstr = {pattern.p + i, pattern.len - i};
         const struct mg_str sstr = {str.p + j + len, str.len - j - len};
         res = mg_match_prefix_n(pstr, sstr);
-      } while (res == -1 && len-- > 0);
-      return res == -1 ? -1 : (int) (j + res + len);
+      } while (res == 0 && len != 0 && len-- > 0);
+      return res == 0 ? 0 : j + res + len;
     } else if (str_util_lowercase(&pattern.p[i]) !=
                str_util_lowercase(&str.p[j])) {
-      return -1;
+      break;
     }
   }
-  return j;
+  if (i < pattern.len && pattern.p[i] == '$') {
+    return j == str.len ? str.len : 0;
+  }
+  return i == pattern.len ? j : 0;
 }
 
-int mg_match_prefix(const char *, int, const char *) WEAK;
-int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
+size_t mg_match_prefix(const char *, int, const char *) WEAK;
+size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
   const struct mg_str pstr = {pattern, (size_t) pattern_len};
   struct mg_str s = {str, 0};
   if (str != NULL) s.len = strlen(str);
@@ -6234,7 +6231,7 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
 
   ep = pd->endpoints;
   while (ep != NULL) {
-    if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) != -1) {
+    if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
       if (matched > matched_max) {
         /* Looking for the longest suitable handler */
         ret = ep;
@@ -7291,8 +7288,7 @@ static int mg_is_file_hidden(const char *path,
   }
 
   return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) ||
-         (p1 != NULL &&
-          mg_match_prefix(p1, strlen(p1), path) == (int) strlen(p1)) ||
+         (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) ||
          (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0);
 }
 
@@ -7842,7 +7838,7 @@ MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
         }
       } else {
         /* Regular rewrite, URI=directory */
-        int match_len = mg_match_prefix_n(a, hm->uri);
+        size_t match_len = mg_match_prefix_n(a, hm->uri);
         if (match_len > 0) {
           file_uri_start = hm->uri.p + match_len;
           if (*file_uri_start == '/' || file_uri_start == cp_end) {
diff --git a/mongoose.h b/mongoose.h
index f8767404e..5112294f6 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -2076,8 +2076,8 @@ int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len);
 #include <stdarg.h>
 #include <stdlib.h>
 
-/* Amalgamated: #include "common/platform.h" */
 /* Amalgamated: #include "common/mg_str.h" */
+/* Amalgamated: #include "common/platform.h" */
 
 #ifndef CS_ENABLE_STRDUP
 #define CS_ENABLE_STRDUP 0
@@ -2221,23 +2221,22 @@ struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
  * - | or ,  divides alternative patterns
  * - any other character matches itself
  * ```
- * Match is case-insensitive. Returns number of bytes matched, or -1 if no
- * match.
+ * Match is case-insensitive. Return number of bytes matched.
  * Examples:
  * ```
  * mg_match_prefix("a*f", len, "abcdefgh") == 6
- * mg_match_prefix("a*f", len, "abcdexgh") == -1
+ * mg_match_prefix("a*f", len, "abcdexgh") == 0
  * mg_match_prefix("a*f|de*,xy", len, "defgh") == 5
  * mg_match_prefix("?*", len, "abc") == 3
- * mg_match_prefix("?*", len, "") == -1
+ * mg_match_prefix("?*", len, "") == 0
  * ```
  */
-int mg_match_prefix(const char *pattern, int pattern_len, const char *str);
+size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str);
 
 /*
  * Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`.
  */
-int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
+size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
 
 #ifdef __cplusplus
 }
-- 
GitLab