dovecot-1.1: mbox: Don't crash if >=8192 bytes long line begins ...

dovecot at dovecot.org dovecot at dovecot.org
Thu Mar 5 20:11:23 EET 2009


details:   http://hg.dovecot.org/dovecot-1.1/rev/6aaf6a306c94
changeset: 8185:6aaf6a306c94
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Mar 05 13:11:16 2009 -0500
description:
mbox: Don't crash if >=8192 bytes long line begins with "From ".

diffstat:

1 file changed, 59 insertions(+), 20 deletions(-)
src/lib-storage/index/mbox/istream-raw-mbox.c |   79 ++++++++++++++++++-------

diffs (143 lines):

diff -r 6137bc40962e -r 6aaf6a306c94 src/lib-storage/index/mbox/istream-raw-mbox.c
--- a/src/lib-storage/index/mbox/istream-raw-mbox.c	Wed Mar 04 18:24:41 2009 -0500
+++ b/src/lib-storage/index/mbox/istream-raw-mbox.c	Thu Mar 05 13:11:16 2009 -0500
@@ -51,19 +51,31 @@ static int mbox_read_from_line(struct ra
 	char *sender;
 	time_t received_time;
 	size_t pos, line_pos;
-	int skip;
+	ssize_t ret;
+	unsigned int skip;
 
 	buf = i_stream_get_data(rstream->istream.parent, &pos);
 	i_assert(pos > 0);
 
 	/* from_offset points to "\nFrom ", so unless we're at the beginning
 	   of the file, skip the initial \n */
-	skip = rstream->from_offset != 0;
-	if (skip && *buf == '\r')
-		skip++;
+	if (rstream->from_offset == 0)
+		skip = 0;
+	else {
+		skip = 1;
+		if (*buf == '\r')
+			skip++;
+	}
 
 	while ((p = memchr(buf+skip, '\n', pos-skip)) == NULL) {
-		if (i_stream_read(rstream->istream.parent) < 0) {
+		ret = i_stream_read(rstream->istream.parent);
+		buf = i_stream_get_data(rstream->istream.parent, &pos);
+		if (ret < 0) {
+			if (ret == -2) {
+				/* From_-line is too long, but we should be
+				   able to parse what we have so far. */
+				break;
+			}
 			/* EOF shouldn't happen */
 			rstream->istream.istream.eof =
 				rstream->istream.parent->eof;
@@ -71,19 +83,14 @@ static int mbox_read_from_line(struct ra
 				rstream->istream.parent->stream_errno;
 			return -1;
 		}
-		buf = i_stream_get_data(rstream->istream.parent, &pos);
 		i_assert(pos > 0);
 	}
-	line_pos = (size_t)(p - buf);
-
-	if (rstream->from_offset != 0) {
-		buf += skip;
-		pos -= skip;
-	}
+	line_pos = p == NULL ? 0 : (size_t)(p - buf);
 
 	/* beginning of mbox */
-	if (memcmp(buf, "From ", 5) != 0 ||
-	    mbox_from_parse(buf+5, pos-5, &received_time, &sender) < 0) {
+	if (memcmp(buf+skip, "From ", 5) != 0 ||
+	    mbox_from_parse((buf+skip)+5, (pos-skip)-5,
+			    &received_time, &sender) < 0) {
 		/* broken From - should happen only at beginning of
 		   file if this isn't a mbox.. */
 		rstream->istream.istream.stream_errno = EINVAL;
@@ -100,9 +107,33 @@ static int mbox_read_from_line(struct ra
 		rstream->next_sender = sender;
 	}
 
-	/* we'll skip over From-line */
+	/* skip over From-line */
+	if (line_pos == 0) {
+		/* line was too long. skip the input until we find LF. */
+		rstream->istream.istream.v_offset += pos;
+		i_stream_skip(rstream->istream.parent, pos);
+
+		while ((ret = i_stream_read(rstream->istream.parent)) > 0) {
+			p = memchr(buf, '\n', pos);
+			if (p != NULL)
+				break;
+			rstream->istream.istream.v_offset += pos;
+			i_stream_skip(rstream->istream.parent, pos);
+		}
+		if (ret <= 0) {
+			i_assert(ret == -1);
+			/* EOF shouldn't happen */
+			rstream->istream.istream.eof =
+				rstream->istream.parent->eof;
+			rstream->istream.istream.stream_errno =
+				rstream->istream.parent->stream_errno;
+			return -1;
+		}
+		line_pos = (size_t)(p - buf);
+	}
 	rstream->istream.istream.v_offset += line_pos+1;
 	i_stream_skip(rstream->istream.parent, line_pos+1);
+
 	rstream->hdr_offset = rstream->istream.istream.v_offset;
 	return 0;
 }
@@ -179,8 +210,11 @@ static ssize_t i_stream_raw_mbox_read(st
 
 	if (ret < 0) {
 		if (ret == -2) {
-			if (stream->istream.v_offset + pos ==
-			    rstream->input_peak_offset) {
+			if (stream->skip == stream->pos) {
+				/* From_-line is longer than our input buffer.
+				   finish the check without seeing the LF. */
+			} else if (stream->istream.v_offset + pos ==
+				   rstream->input_peak_offset) {
 				/* we've read everything our parent stream
 				   has to offer. */
 				stream->buffer = buf;
@@ -256,9 +290,7 @@ static ssize_t i_stream_raw_mbox_read(st
 		if (buf[i] == *fromp) {
 			if (*++fromp == '\0') {
 				/* potential From-line, see if we have the
-				   rest of the line buffered.
-				   FIXME: if From-line is longer than input
-				   buffer, we break. probably irrelevant.. */
+				   rest of the line buffered. */
 				i++;
 				if (rstream->hdr_offset + rstream->mail_size ==
 				    stream->istream.v_offset + i - 6 ||
@@ -273,11 +305,18 @@ static ssize_t i_stream_raw_mbox_read(st
 					} else {
 						crlf_ending = FALSE;
 					}
+					if (ret == -2) {
+						/* even if we don't have the
+						   whole line, we need to
+						   finish this check now. */
+						goto mbox_verify;
+					}
 				}
 				fromp = mbox_from;
 			} else if (from_start_pos != (size_t)-1) {
 				/* we have the whole From-line here now.
 				   See if it's a valid one. */
+			mbox_verify:
 				if (mbox_from_parse(buf + from_after_pos,
 						    pos - from_after_pos,
 						    &received_time,


More information about the dovecot-cvs mailing list