dovecot: If extension header is broken, drop it when fscking.

dovecot at dovecot.org dovecot at dovecot.org
Sat Sep 15 13:07:49 EEST 2007


details:   http://hg.dovecot.org/dovecot/rev/9dde743dfbc1
changeset: 6385:9dde743dfbc1
user:      Timo Sirainen <tss at iki.fi>
date:      Sat Sep 15 11:44:11 2007 +0300
description:
If extension header is broken, drop it when fscking.

diffstat:

3 files changed, 165 insertions(+), 62 deletions(-)
src/lib-index/mail-index-fsck.c    |   62 ++++++++++++++
src/lib-index/mail-index-map.c     |  157 +++++++++++++++++++++---------------
src/lib-index/mail-index-private.h |    8 +

diffs (truncated from 307 to 300 lines):

diff -r 2c8b1d487728 -r 9dde743dfbc1 src/lib-index/mail-index-fsck.c
--- a/src/lib-index/mail-index-fsck.c	Sat Sep 15 10:51:03 2007 +0300
+++ b/src/lib-index/mail-index-fsck.c	Sat Sep 15 11:44:11 2007 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "ioloop.h"
+#include "array.h"
 #include "mail-index-private.h"
 #include "mail-transaction-log.h"
 
@@ -54,6 +55,66 @@ mail_index_fsck_header(struct mail_index
 		CHECK(log_file_head_offset, !=);
 		CHECK(log_file_tail_offset, !=);
 	}
+}
+
+static bool
+array_has_name(const ARRAY_TYPE(const_string) *names, const char *name)
+{
+	const char *const *str;
+	unsigned int i, count;
+
+	str = array_get(names, &count);
+	for (i = 0; i < count; i++) {
+		if (strcmp(str[i], name) == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+mail_index_fsck_extensions(struct mail_index *index, struct mail_index_map *map,
+			   struct mail_index_header *hdr)
+{
+	const struct mail_index_ext_header *ext_hdr;
+	ARRAY_TYPE(const_string) names;
+	const char *name, *error;
+	unsigned int offset, ext_offset, i;
+
+	t_push();
+	t_array_init(&names, 64);
+	offset = MAIL_INDEX_HEADER_SIZE_ALIGN(hdr->base_header_size);
+	for (i = 0; offset < hdr->header_size; i++) {
+		ext_offset = offset;
+		if (mail_index_map_ext_get_next(map, &offset,
+						&ext_hdr, &name) < 0) {
+			/* the extension continued outside header, drop it */
+			mail_index_fsck_error(index,
+					      "Dropped extension #%d (%s) "
+					      "with invalid header size",
+					      i, name);
+			hdr->header_size = ext_offset;
+			break;
+		}
+		if (mail_index_map_ext_hdr_check(hdr, ext_hdr, name,
+						 &error) < 0) {
+			mail_index_fsck_error(index,
+				"Dropped broken extension #%d (%s)", i, name);
+		} else if (array_has_name(&names, name)) {
+			mail_index_fsck_error(index,
+				"Dropped duplicate extension %s", name);
+		} else {
+			array_append(&names, &name, 1);
+			continue;
+		}
+
+		/* drop the field */
+		hdr->header_size -= offset - ext_offset;
+		buffer_copy(map->hdr_copy_buf, ext_offset,
+			    map->hdr_copy_buf, offset, (size_t)-1);
+		buffer_set_used_size(map->hdr_copy_buf, hdr->header_size);
+		offset = ext_offset;
+	}
+	t_pop();
 }
 
 static void
@@ -162,6 +223,7 @@ mail_index_fsck_map(struct mail_index *i
 	hdr = map->hdr;
 
 	mail_index_fsck_header(index, map, &hdr);
+	mail_index_fsck_extensions(index, map, &hdr);
 	mail_index_fsck_records(index, map, &hdr);
 
 	map->hdr = hdr;
diff -r 2c8b1d487728 -r 9dde743dfbc1 src/lib-index/mail-index-map.c
--- a/src/lib-index/mail-index-map.c	Sat Sep 15 10:51:03 2007 +0300
+++ b/src/lib-index/mail-index-map.c	Sat Sep 15 11:44:11 2007 +0300
@@ -106,14 +106,92 @@ mail_index_map_register_ext(struct mail_
 	return idx;
 }
 
-static int mail_index_parse_extensions(struct mail_index_map *map)
+int mail_index_map_ext_get_next(struct mail_index_map *map,
+				unsigned int *offset_p,
+				const struct mail_index_ext_header **ext_hdr_r,
+				const char **name_r)
+{
+	const struct mail_index_ext_header *ext_hdr;
+	unsigned int offset, name_offset;
+
+	offset = *offset_p;
+	*name_r = "";
+
+	/* Extension header contains:
+	   - struct mail_index_ext_header
+	   - name (not 0-terminated)
+	   - 64bit alignment padding
+	   - extension header contents
+	   - 64bit alignment padding
+	*/
+	name_offset = offset + sizeof(*ext_hdr);
+	ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
+	if (offset + sizeof(*ext_hdr) >= map->hdr.header_size)
+		return -1;
+
+	offset += get_ext_size(ext_hdr->name_size);
+	if (offset > map->hdr.header_size)
+		return -1;
+
+	*name_r = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
+			    ext_hdr->name_size);
+	if (strcmp(*name_r, str_sanitize(*name_r, -1)) != 0) {
+		/* we allow only plain ASCII names, so this extension
+		   is most likely broken */
+		*name_r = "";
+	}
+
+	/* finally make sure that the hdr_size is small enough.
+	   do this last so that we could return a usable name. */
+	offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
+	if (offset > map->hdr.header_size)
+		return -1;
+
+	*offset_p = offset;
+	*ext_hdr_r = ext_hdr;
+	return 0;
+}
+
+int mail_index_map_ext_hdr_check(const struct mail_index_header *hdr,
+				 const struct mail_index_ext_header *ext_hdr,
+				 const char *name, const char **error_r)
+{
+	if ((ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) ||
+	    (ext_hdr->record_align == 0 && ext_hdr->record_size != 0)) {
+		*error_r = "Invalid field values";
+		return -1;
+	}
+	if (*name == '\0') {
+		*error_r = "Broken name";
+		return -1;
+	}
+
+	if (ext_hdr->record_offset + ext_hdr->record_size > hdr->record_size) {
+		*error_r = t_strdup_printf("Record field points "
+					   "outside record size (%u+%u > %u)",
+					   ext_hdr->record_offset,
+					   ext_hdr->record_size,
+					   hdr->record_size);
+		return -1;
+	}
+
+	if (ext_hdr->record_size > 0 &&
+	    ((ext_hdr->record_offset % ext_hdr->record_align) != 0 ||
+	     (hdr->record_size % ext_hdr->record_align) != 0)) {
+		*error_r = t_strdup_printf("Record field alignmentation %u "
+					   "not used", ext_hdr->record_align);
+		return -1;
+	}
+	return 0;
+}
+
+static int mail_index_map_parse_extensions(struct mail_index_map *map)
 {
 	struct mail_index *index = map->index;
 	const struct mail_index_ext_header *ext_hdr;
-	unsigned int i, old_count;
-	const char *name;
-	uint32_t ext_id, ext_offset, offset, name_offset;
-	size_t size_left;
+	unsigned int i, old_count, offset;
+	const char *name, *error;
+	uint32_t ext_id, ext_offset;
 
 	/* extension headers always start from 64bit offsets, so if base header
 	   doesn't happen to be 64bit aligned we'll skip some bytes */
@@ -132,44 +210,22 @@ static int mail_index_parse_extensions(s
 
 	for (i = 0; offset < map->hdr.header_size; i++) {
 		ext_offset = offset;
-		ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
-
-		/* Extension header contains:
-		   - struct mail_index_ext_header
-		   - name (not 0-terminated)
-		   - 64bit alignment padding
-		   - extension header contents
-		   - 64bit alignment padding
-		*/
-		size_left = map->hdr.header_size - offset;
-		if (size_left < sizeof(*ext_hdr) ||
-		    size_left < get_ext_size(ext_hdr->name_size) +
-		    ext_hdr->hdr_size) {
+
+		t_push();
+		if (mail_index_map_ext_get_next(map, &offset,
+						&ext_hdr, &name) < 0) {
 			mail_index_set_error(index, "Corrupted index file %s: "
-				"Header extension goes outside header",
-				index->filepath);
+				"Header extension #%d (%s) goes outside header",
+				index->filepath, i, name);
+			t_pop();
 			return -1;
 		}
 
-		name_offset = offset + sizeof(*ext_hdr);
-		offset += get_ext_size(ext_hdr->name_size);
-
-		t_push();
-		name = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
-				 ext_hdr->name_size);
-		if (strcmp(name, str_sanitize(name, -1)) != 0) {
-			/* we allow only plain ASCII names, so this extension
-			   is most likely broken */
-			name = "";
-		}
-
-		if ((ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) ||
-		    (ext_hdr->record_align == 0 && ext_hdr->record_size != 0) ||
-		    *name == '\0') {
+		if (mail_index_map_ext_hdr_check(&map->hdr, ext_hdr,
+						 name, &error) < 0) {
 			mail_index_set_error(index, "Corrupted index file %s: "
-					     "Broken header extension %s",
-					     index->filepath, *name == '\0' ?
-					     t_strdup_printf("#%d", i) : name);
+					     "Broken extension #%d (%s): %s",
+					     index->filepath, i, name, error);
 			t_pop();
 			return -1;
 		}
@@ -177,27 +233,6 @@ static int mail_index_parse_extensions(s
 			mail_index_set_error(index, "Corrupted index file %s: "
 				"Duplicate header extension %s",
 				index->filepath, name);
-			t_pop();
-			return -1;
-		}
-
-		if (map->hdr.record_size <
-		    ext_hdr->record_offset + ext_hdr->record_size) {
-			mail_index_set_error(index, "Corrupted index file %s: "
-				"Record field %s points outside record size "
-				"(%u < %u+%u)", index->filepath, name,
-				map->hdr.record_size,
-				ext_hdr->record_offset, ext_hdr->record_size);
-			t_pop();
-			return -1;
-		}
-
-		if (ext_hdr->record_size > 0 &&
-		    ((ext_hdr->record_offset % ext_hdr->record_align) != 0 ||
-		     (map->hdr.record_size % ext_hdr->record_align) != 0)) {
-			mail_index_set_error(index, "Corrupted index file %s: "
-				"Record field %s alignmentation %u not used",
-				index->filepath, name, ext_hdr->record_align);
 			t_pop();
 			return -1;
 		}
@@ -209,8 +244,6 @@ static int mail_index_parse_extensions(s
 					    ext_hdr->record_align,
 					    ext_hdr->reset_id);
 		t_pop();
-
-		offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
 	}
 	return 0;
 }
@@ -800,7 +833,7 @@ static int mail_index_map_latest_file(st
 		/* make sure the header is ok before using this mapping */
 		ret = mail_index_map_check_header(new_map);
 		if (ret > 0) {
-			if (mail_index_parse_extensions(new_map) < 0)
+			if (mail_index_map_parse_extensions(new_map) < 0)
 				ret = 0;
 			else if (mail_index_map_parse_keywords(new_map) < 0)
 				ret = 0;
diff -r 2c8b1d487728 -r 9dde743dfbc1 src/lib-index/mail-index-private.h
--- a/src/lib-index/mail-index-private.h	Sat Sep 15 10:51:03 2007 +0300
+++ b/src/lib-index/mail-index-private.h	Sat Sep 15 11:44:11 2007 +0300
@@ -289,6 +289,14 @@ int mail_index_map_check_header(struct m
 int mail_index_map_check_header(struct mail_index_map *map);
 int mail_index_map_parse_keywords(struct mail_index_map *map);
 
+int mail_index_map_ext_get_next(struct mail_index_map *map,
+				unsigned int *offset,
+				const struct mail_index_ext_header **ext_hdr_r,
+				const char **name_r);


More information about the dovecot-cvs mailing list