CVE-2018-20721: Out-of-bounds Read (CWE-125)
value: URI_FUNC() in UriParse.c in uriparser before 0.9.1 has an out-of-bounds read (in uriParse*Ex* functions) for an incomplete URI with an IPv6 address containing an embedded IPv4 address, such as a "//[::44.1" address.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2019-01-16T14:29Z
Repository: https://github.com/uriparser/uriparser
Language: Other
Files changed: ChangeLog, src/UriParse.c, test/test.cpp
Commit date: 2018-12-08 18:44:25 +0100
Commit message: Fix uriParse*Ex* out-of-bounds read

Diff: diff --git a/ChangeLog b/ChangeLog
index 8a3f49c..1ab4552 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,10 +2,16 @@ NOTE: uriparser is looking for help with a few things:
       https://github.com/uriparser/uriparser/labels/help%20wanted
       If you can help, please get in touch.  Thanks!
 
 201x-xx-xx -- x.x.x
 
+  * Fixed:
+      Out-of-bounds read in uriParse*Ex* for incomplete URIs with IPv6
+      addresses with embedded IPv4 address, e.g. "//[::44.1";
+      mitigated if passed parameter <afterLast> points to readable memory
+      containing a '\0' byte.
+      Thanks to Joergen Ibsen for the report!
   * Fixed: uriToStringCharsRequired* reported 1 more byte than actually needed
       for IPv4 address URIs (GitHub #41); Thanks to @gyh007 for the patch!
   * Fixed: Compilation with MinGW
       Thanks to Sandro Mani for the patch!
   * Fixed: Drop use of asprintf from the test suite for MinGW (GitHub #40)
diff --git a/src/UriParse.c b/src/UriParse.c
index f4bdda9..33ac891 100644
--- a/src/UriParse.c
+++ b/src/UriParse.c
@@ -690,10 +690,15 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)(
 				default:
 					URI_FUNC(StopSyntax)(state, first, memory);
 					return NULL;
 				}
 				first++;
+
+				if (first >= afterLast) {
+					URI_FUNC(StopSyntax)(state, first, memory);
+					return NULL;
+				}
 			}
 		} else {
 			/* Eat while no dot in sight */
 			int letterAmong = 0;
 			int walking = 1;
diff --git a/test/test.cpp b/test/test.cpp
index 2ffbc82..8eabee6 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -240,10 +240,23 @@ TEST(UriSuite, TestIpSixFail) {
 
 		// Nonhex
 		URI_TEST_IP_SIX_FAIL("g:0:0:0:0:0:0");
 }
 
+TEST(UriSuite, TestIpSixOverread) {
+		UriUriA uri;
+		const char * errorPos;
+
+		// NOTE: This string is designed to not have a terminator
+		char uriText[2 + 3 + 2 + 1 + 1];
+		strncpy(uriText, "//[::44.1", sizeof(uriText));
+
+		EXPECT_EQ(uriParseSingleUriExA(&uri, uriText,
+				uriText + sizeof(uriText), &errorPos), URI_ERROR_SYNTAX);
+		EXPECT_EQ(errorPos, uriText + sizeof(uriText));
+}
+
 TEST(UriSuite, TestUri) {
 		UriParserStateA stateA;
 		UriParserStateW stateW;
 		UriUriA uriA;
 		UriUriW uriW;

Fixed code:   * Fixed:
      Out-of-bounds read in uriParse*Ex* for incomplete URIs with IPv6
      addresses with embedded IPv4 address, e.g. "//[::44.1";
      mitigated if passed parameter <afterLast> points to readable memory
      containing a '\0' byte.
      Thanks to Joergen Ibsen for the report!

				if (first >= afterLast) {
					URI_FUNC(StopSyntax)(state, first, memory);
					return NULL;
				}
TEST(UriSuite, TestIpSixOverread) {
		UriUriA uri;
		const char * errorPos;

		// NOTE: This string is designed to not have a terminator
		char uriText[2 + 3 + 2 + 1 + 1];
		strncpy(uriText, "//[::44.1", sizeof(uriText));

		EXPECT_EQ(uriParseSingleUriExA(&uri, uriText,
				uriText + sizeof(uriText), &errorPos), URI_ERROR_SYNTAX);
		EXPECT_EQ(errorPos, uriText + sizeof(uriText));
}