CVE-2017-5335: Out-of-bounds Read (CWE-125)
value: The stream reading functions in lib/opencdk/read-packet.c in GnuTLS before 3.3.26 and 3.5.x before 3.5.8 allow remote attackers to cause a denial of service (out-of-memory error and crash) via a crafted OpenPGP certificate.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.5
Published: 2017-03-24T15:59Z
Repository: https://gitlab.com/gnutls/gnutls
Language: C
Files changed: lib/opencdk/read-packet.c
Commit date: 2017-01-04 15:24:57 +0100
Commit message: opencdk: added error checking in the stream reading functions

This addresses an out of memory error. Issue found using oss-fuzz:
  https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=337

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>

Diff: diff --git a/lib/opencdk/read-packet.c b/lib/opencdk/read-packet.c
index b999e1c000..fa4c604d81 100644
--- a/lib/opencdk/read-packet.c
+++ b/lib/opencdk/read-packet.c
@@ -40,10 +40,11 @@
 
 static int
 stream_read(cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread)
 {
 	int res = cdk_stream_read(s, buf, buflen);
+
 	if (res > 0) {
 		*r_nread = res;
 		return 0;
 	} else {
 		return (cdk_stream_eof(s) ? EOF : _cdk_stream_get_errno(s));
@@ -53,26 +54,26 @@ stream_read(cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread)
 
 /* Try to read 4 octets from the stream. */
 static u32 read_32(cdk_stream_t s)
 {
 	byte buf[4];
-	size_t nread;
+	size_t nread = 0;
 
 	assert(s != NULL);
 
 	stream_read(s, buf, 4, &nread);
 	if (nread != 4)
-		return (u32) - 1;
+		return (u32) -1;
 	return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
 }
 
 
 /* Try to read 2 octets from a stream. */
 static u16 read_16(cdk_stream_t s)
 {
 	byte buf[2];
-	size_t nread;
+	size_t nread = 0;
 
 	assert(s != NULL);
 
 	stream_read(s, buf, 2, &nread);
 	if (nread != 2)
@@ -570,11 +571,11 @@ read_user_id(cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t user_id)
 
 
 static cdk_error_t
 read_subpkt(cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes)
 {
-	byte c, c1;
+	int c, c1;
 	size_t size, nread, n;
 	cdk_subpkt_t node;
 	cdk_error_t rc;
 
 	if (!inp || !r_nbytes)
@@ -585,15 +586,22 @@ read_subpkt(cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes)
 
 	n = 0;
 	*r_nbytes = 0;
 	c = cdk_stream_getc(inp);
 	n++;
+
 	if (c == 255) {
 		size = read_32(inp);
+		if (size == (u32)-1)
+			return CDK_Inv_Packet;
+
 		n += 4;
 	} else if (c >= 192 && c < 255) {
 		c1 = cdk_stream_getc(inp);
+		if (c1 == EOF)
+			return CDK_Inv_Packet;
+
 		n++;
 		if (c1 == 0)
 			return 0;
 		size = ((c - 192) << 8) + c1 + 192;
 	} else if (c < 192)
@@ -856,21 +864,33 @@ read_literal(cdk_stream_t inp, size_t pktlen,
 /* Read an old packet CTB and return the length of the body. */
 static void
 read_old_length(cdk_stream_t inp, int ctb, size_t * r_len, size_t * r_size)
 {
 	int llen = ctb & 0x03;
+	int c;
 
 	if (llen == 0) {
-		*r_len = cdk_stream_getc(inp);
+		c = cdk_stream_getc(inp);
+		if (c == EOF)
+			goto fail;
+
+		*r_len = c;
 		(*r_size)++;
 	} else if (llen == 1) {
 		*r_len = read_16(inp);
+		if (*r_len == (u16)-1)
+			goto fail;
 		(*r_size) += 2;
 	} else if (llen == 2) {
 		*r_len = read_32(inp);
+		if (*r_len == (u32)-1) {
+			goto fail;
+		}
+
 		(*r_size) += 4;
 	} else {
+ fail:
 		*r_len = 0;
 		*r_size = 0;
 	}
 }
 
@@ -881,19 +901,29 @@ read_new_length(cdk_stream_t inp,
 		size_t * r_len, size_t * r_size, size_t * r_partial)
 {
 	int c, c1;
 
 	c = cdk_stream_getc(inp);
+	if (c == EOF)
+		return;
+
 	(*r_size)++;
 	if (c < 192)
 		*r_len = c;
 	else if (c >= 192 && c <= 223) {
 		c1 = cdk_stream_getc(inp);
+		if (c1 == EOF)
+			return;
+
 		(*r_size)++;
 		*r_len = ((c - 192) << 8) + c1 + 192;
 	} else if (c == 255) {
 		*r_len = read_32(inp);
+		if (*r_len == (u32)-1) {
+			return;
+		}
+
 		(*r_size) += 4;
 	} else {
 		*r_len = 1 << (c & 0x1f);
 		*r_partial = 1;
 	}

Vulnerable code: 	size_t nread;
		return (u32) - 1;
	size_t nread;
	byte c, c1;
		*r_len = cdk_stream_getc(inp);
Fixed code: 
	size_t nread = 0;
		return (u32) -1;
	size_t nread = 0;
	int c, c1;

		if (size == (u32)-1)
			return CDK_Inv_Packet;

		if (c1 == EOF)
			return CDK_Inv_Packet;

	int c;
		c = cdk_stream_getc(inp);
		if (c == EOF)
			goto fail;

		*r_len = c;
		if (*r_len == (u16)-1)
			goto fail;
		if (*r_len == (u32)-1) {
			goto fail;
		}

 fail:
	if (c == EOF)
		return;

		if (c1 == EOF)
			return;

		if (*r_len == (u32)-1) {
			return;
		}