From e9b86842441a668b30796bff7d60828614570a1b Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@dovecot.fi>
Date: Fri, 22 Dec 2017 18:42:53 +0200
Subject: [PATCH 4/7] lib-mail: Make sure parsers don't accidentally go much
 beyond end pointer

---
 src/lib-mail/message-address.c   | 18 +++++++++---------
 src/lib-mail/message-date.c      |  4 ++--
 src/lib-mail/message-part-data.c |  2 +-
 src/lib-mail/rfc2231-parser.c    |  2 +-
 src/lib-mail/rfc822-parser.c     | 38 +++++++++++++++++++-------------------
 5 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c
index 787a26e..88d638a 100644
--- a/src/lib-mail/message-address.c
+++ b/src/lib-mail/message-address.c
@@ -84,7 +84,7 @@ static int parse_local_part(struct message_address_parser_context *ctx)
 	   local-part      = dot-atom / quoted-string / obs-local-part
 	   obs-local-part  = word *("." word)
 	*/
-	i_assert(ctx->parser.data != ctx->parser.end);
+	i_assert(ctx->parser.data < ctx->parser.end);
 
 	str_truncate(ctx->str, 0);
 	if (*ctx->parser.data == '"')
@@ -117,7 +117,7 @@ static int parse_domain_list(struct message_address_parser_context *ctx)
 	/* obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain) */
 	str_truncate(ctx->str, 0);
 	for (;;) {
-		if (ctx->parser.data == ctx->parser.end)
+		if (ctx->parser.data >= ctx->parser.end)
 			return 0;
 
 		if (*ctx->parser.data != '@')
@@ -153,7 +153,7 @@ static int parse_angle_addr(struct message_address_parser_context *ctx)
 		if (parse_domain_list(ctx) <= 0 || *ctx->parser.data != ':') {
 			if (ctx->fill_missing)
 				ctx->addr.route = "INVALID_ROUTE";
-			if (ctx->parser.data == ctx->parser.end)
+			if (ctx->parser.data >= ctx->parser.end)
 				return -1;
 			/* try to continue anyway */
 		} else {
@@ -203,7 +203,7 @@ static int parse_name_addr(struct message_address_parser_context *ctx)
 			ctx->addr.domain = "SYNTAX_ERROR";
 		ctx->addr.invalid_syntax = TRUE;
 	}
-	return ctx->parser.data != ctx->parser.end;
+	return ctx->parser.data < ctx->parser.end ? 1 : 0;
 }
 
 static int parse_addr_spec(struct message_address_parser_context *ctx)
@@ -211,7 +211,7 @@ static int parse_addr_spec(struct message_address_parser_context *ctx)
 	/* addr-spec       = local-part "@" domain */
 	int ret, ret2 = -2;
 
-	i_assert(ctx->parser.data != ctx->parser.end);
+	i_assert(ctx->parser.data < ctx->parser.end);
 
 	str_truncate(ctx->parser.last_comment, 0);
 
@@ -221,7 +221,7 @@ static int parse_addr_spec(struct message_address_parser_context *ctx)
 		/* end of input or parsing local-part failed */
 		ctx->addr.invalid_syntax = TRUE;
 	}
-	if (ret != 0 && ctx->parser.data != ctx->parser.end &&
+	if (ret != 0 && ctx->parser.data < ctx->parser.end &&
 	    *ctx->parser.data == '@') {
 		ret2 = parse_domain(ctx);
 		if (ret2 <= 0)
@@ -317,7 +317,7 @@ static int parse_group(struct message_address_parser_context *ctx)
 			if (parse_mailbox(ctx) <= 0) {
 				/* broken mailbox - try to continue anyway. */
 			}
-			if (ctx->parser.data == ctx->parser.end ||
+			if (ctx->parser.data >= ctx->parser.end ||
 			    *ctx->parser.data != ',')
 				break;
 			ctx->parser.data++;
@@ -328,7 +328,7 @@ static int parse_group(struct message_address_parser_context *ctx)
 		}
 	}
 	if (ret >= 0) {
-		if (ctx->parser.data == ctx->parser.end ||
+		if (ctx->parser.data >= ctx->parser.end ||
 		    *ctx->parser.data != ';')
 			ret = -1;
 		else {
@@ -368,7 +368,7 @@ static int parse_address_list(struct message_address_parser_context *ctx,
 		max_addresses--;
 		if ((ret = parse_address(ctx)) == 0)
 			break;
-		if (ctx->parser.data == ctx->parser.end ||
+		if (ctx->parser.data >= ctx->parser.end ||
 		    *ctx->parser.data != ',') {
 			ret = -1;
 			break;
diff --git a/src/lib-mail/message-date.c b/src/lib-mail/message-date.c
index e6956b7..b67b21e 100644
--- a/src/lib-mail/message-date.c
+++ b/src/lib-mail/message-date.c
@@ -103,7 +103,7 @@ static int next_token(struct message_date_parser_context *ctx,
 	int ret;
 
 	str_truncate(ctx->str, 0);
-	ret = ctx->parser.data == ctx->parser.end ? 0 :
+	ret = ctx->parser.data >= ctx->parser.end ? 0 :
 		rfc822_parse_atom(&ctx->parser, ctx->str);
 
 	*value = str_data(ctx->str);
@@ -205,7 +205,7 @@ message_date_parser_tokens(struct message_date_parser_context *ctx,
 	tm.tm_min = (value[0]-'0') * 10 + (value[1]-'0');
 
 	/* [:ss] */
-	if (ctx->parser.data != ctx->parser.end &&
+	if (ctx->parser.data < ctx->parser.end &&
 	    IS_TIME_SEP(*ctx->parser.data)) {
 		ctx->parser.data++;
 		rfc822_skip_lwsp(&ctx->parser);
diff --git a/src/lib-mail/message-part-data.c b/src/lib-mail/message-part-data.c
index 75651a2..7f13244 100644
--- a/src/lib-mail/message-part-data.c
+++ b/src/lib-mail/message-part-data.c
@@ -374,7 +374,7 @@ parse_content_language(struct message_part_data *data,
 		array_append(&langs, &lang, 1);
 		str_truncate(str, 0);
 
-		if (parser.data == parser.end || *parser.data != ',')
+		if (parser.data >= parser.end || *parser.data != ',')
 			break;
 		parser.data++;
 		rfc822_skip_lwsp(&parser);
diff --git a/src/lib-mail/rfc2231-parser.c b/src/lib-mail/rfc2231-parser.c
index f62326e..c65b798 100644
--- a/src/lib-mail/rfc2231-parser.c
+++ b/src/lib-mail/rfc2231-parser.c
@@ -59,7 +59,7 @@ int rfc2231_parse(struct rfc822_parser_context *ctx,
 		if (ret < 0) {
 			/* try to continue anyway.. */
 			broken = TRUE;
-			if (ctx->data == ctx->end)
+			if (ctx->data >= ctx->end)
 				break;
 			ctx->data++;
 			continue;
diff --git a/src/lib-mail/rfc822-parser.c b/src/lib-mail/rfc822-parser.c
index edb07f5..4e7a86d 100644
--- a/src/lib-mail/rfc822-parser.c
+++ b/src/lib-mail/rfc822-parser.c
@@ -72,7 +72,7 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
 		str_truncate(ctx->last_comment, 0);
 
 	start = ++ctx->data;
-	for (; ctx->data != ctx->end; ctx->data++) {
+	for (; ctx->data < ctx->end; ctx->data++) {
 		switch (*ctx->data) {
 		case '(':
 			level++;
@@ -84,7 +84,7 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
 						     ctx->data - start);
 				}
 				ctx->data++;
-				return ctx->data != ctx->end;
+				return ctx->data < ctx->end ? 1 : 0;
 			}
 			break;
 		case '\\':
@@ -95,7 +95,7 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
 			start = ctx->data + 1;
 
 			ctx->data++;
-			if (ctx->data == ctx->end)
+			if (ctx->data >= ctx->end)
 				return -1;
 			break;
 		}
@@ -107,7 +107,7 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
 
 int rfc822_skip_lwsp(struct rfc822_parser_context *ctx)
 {
-	for (; ctx->data != ctx->end;) {
+	for (; ctx->data < ctx->end;) {
 		if (*ctx->data == ' ' || *ctx->data == '\t' ||
 		    *ctx->data == '\r' || *ctx->data == '\n') {
                         ctx->data++;
@@ -120,7 +120,7 @@ int rfc822_skip_lwsp(struct rfc822_parser_context *ctx)
 		if (rfc822_skip_comment(ctx) < 0)
 			return -1;
 	}
-	return ctx->data != ctx->end;
+	return ctx->data < ctx->end ? 1 : 0;
 }
 
 int rfc822_parse_atom(struct rfc822_parser_context *ctx, string_t *str)
@@ -132,10 +132,10 @@ int rfc822_parse_atom(struct rfc822_parser_context *ctx, string_t *str)
 	   atext           =
 	     ; Any character except controls, SP, and specials.
 	*/
-	if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data))
+	if (ctx->data >= ctx->end || !IS_ATEXT(*ctx->data))
 		return -1;
 
-	for (start = ctx->data++; ctx->data != ctx->end; ctx->data++) {
+	for (start = ctx->data++; ctx->data < ctx->end; ctx->data++) {
 		if (IS_ATEXT(*ctx->data))
 			continue;
 
@@ -161,10 +161,10 @@ int rfc822_parse_dot_atom(struct rfc822_parser_context *ctx, string_t *str)
 
 	   For RFC-822 compatibility allow LWSP around '.'
 	*/
-	if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data))
+	if (ctx->data >= ctx->end || !IS_ATEXT(*ctx->data))
 		return -1;
 
-	for (start = ctx->data++; ctx->data != ctx->end; ) {
+	for (start = ctx->data++; ctx->data < ctx->end; ) {
 		if (IS_ATEXT(*ctx->data)) {
 			ctx->data++;
 			continue;
@@ -194,7 +194,7 @@ int rfc822_parse_mime_token(struct rfc822_parser_context *ctx, string_t *str)
 {
 	const unsigned char *start;
 
-	for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
+	for (start = ctx->data; ctx->data < ctx->end; ctx->data++) {
 		if (IS_ATEXT_NON_TSPECIAL(*ctx->data) || *ctx->data == '.')
 			continue;
 
@@ -215,7 +215,7 @@ int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str)
 	i_assert(*ctx->data == '"');
 	ctx->data++;
 
-	for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
+	for (start = ctx->data; ctx->data < ctx->end; ctx->data++) {
 		switch (*ctx->data) {
 		case '"':
 			str_append_n(str, start, ctx->data - start);
@@ -231,7 +231,7 @@ int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str)
 			break;
 		case '\\':
 			ctx->data++;
-			if (ctx->data == ctx->end)
+			if (ctx->data >= ctx->end)
 				return -1;
 
 			str_append_n(str, start, ctx->data - start - 1);
@@ -257,7 +257,7 @@ rfc822_parse_atom_or_dot(struct rfc822_parser_context *ctx, string_t *str)
 	   The difference between this function and rfc822_parse_dot_atom()
 	   is that this doesn't just silently skip over all the whitespace.
 	*/
-	for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
+	for (start = ctx->data; ctx->data < ctx->end; ctx->data++) {
 		if (IS_ATEXT(*ctx->data) || *ctx->data == '.')
 			continue;
 
@@ -279,7 +279,7 @@ int rfc822_parse_phrase(struct rfc822_parser_context *ctx, string_t *str)
 	   obs-phrase = word *(word / "." / CFWS)
 	*/
 
-	if (ctx->data == ctx->end)
+	if (ctx->data >= ctx->end)
 		return 0;
 	if (*ctx->data == '.')
 		return -1;
@@ -317,10 +317,10 @@ rfc822_parse_domain_literal(struct rfc822_parser_context *ctx, string_t *str)
 	i_assert(ctx->data < ctx->end);
 	i_assert(*ctx->data == '[');
 
-	for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
+	for (start = ctx->data; ctx->data < ctx->end; ctx->data++) {
 		if (*ctx->data == '\\') {
 			ctx->data++;
-			if (ctx->data == ctx->end)
+			if (ctx->data >= ctx->end)
 				break;
 		} else if (*ctx->data == ']') {
 			ctx->data++;
@@ -389,7 +389,7 @@ int rfc822_parse_content_param(struct rfc822_parser_context *ctx,
 	*key_r = NULL;
 	*value_r = NULL;
 
-	if (ctx->data == ctx->end)
+	if (ctx->data >= ctx->end)
 		return 0;
 	if (*ctx->data != ';')
 		return -1;
@@ -412,10 +412,10 @@ int rfc822_parse_content_param(struct rfc822_parser_context *ctx,
 		/* broken / no value */
 	} else if (*ctx->data == '"') {
 		ret = rfc822_parse_quoted_string(ctx, tmp);
-	} else if (ctx->data != ctx->end && *ctx->data == '=') {
+	} else if (ctx->data < ctx->end && *ctx->data == '=') {
 		/* workaround for broken input:
 		   name==?utf-8?b?...?= */
-		while (ctx->data != ctx->end && *ctx->data != ';' &&
+		while (ctx->data < ctx->end && *ctx->data != ';' &&
 		       *ctx->data != ' ' && *ctx->data != '\t' &&
 		       *ctx->data != '\r' && *ctx->data != '\n') {
 			str_append_c(tmp, *ctx->data);
-- 
2.1.4

