changeset 52:d5f0f634b86e HEAD

More overflow fixes.
author Timo Sirainen <tss@iki.fi>
date Wed, 28 Aug 2002 00:36:06 +0300
parents b4adc3429867
children 37a388fe4dab
files src/lib-index/mail-index-compress.c src/lib-index/mail-index.c src/lib-index/mail-index.h src/lib-storage/index/index-expunge.c src/lib-storage/index/index-status.c src/lib-storage/index/index-sync.c
diffstat 6 files changed, 114 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-index-compress.c	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-index/mail-index-compress.c	Wed Aug 28 00:36:06 2002 +0300
@@ -23,13 +23,8 @@
 		return TRUE;
 	}
 
-	if (index->header->first_hole_position >= index->mmap_length) {
-		index_set_error(index, "Error in index file %s: "
-				"first_hole_position points outside file",
-				index->filepath);
-		index->header->flags |= MAIL_INDEX_FLAG_REBUILD;
+	if (!mail_index_verify_hole_range(index))
 		return FALSE;
-	}
 
 	/* if we get interrupted, the whole index is probably corrupted.
 	   so keep rebuild-flag on while doing this */
--- a/src/lib-index/mail-index.c	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-index/mail-index.c	Wed Aug 28 00:36:06 2002 +0300
@@ -753,11 +753,56 @@
 	return TRUE;
 }
 
+int mail_index_verify_hole_range(MailIndex *index)
+{
+	MailIndexHeader *hdr;
+	unsigned int max_records, first_records;
+
+	hdr = index->header;
+	if (hdr->first_hole_position == 0)
+		return TRUE;
+
+	/* make sure position is valid */
+	if (hdr->first_hole_position < sizeof(MailIndexHeader) ||
+	    (hdr->first_hole_position -
+	     sizeof(MailIndexHeader)) % sizeof(MailIndexRecord) != 0) {
+		index_set_error(index, "Error in index file %s: "
+				"first_hole_position contains invalid value",
+				index->filepath);
+		INDEX_MARK_CORRUPTED(index);
+		return FALSE;
+	}
+
+	/* make sure position is in range.. */
+	if (hdr->first_hole_position >= index->mmap_length) {
+		index_set_error(index, "Error in index file %s: "
+				"first_hole_position points outside file",
+				index->filepath);
+		INDEX_MARK_CORRUPTED(index);
+		return FALSE;
+	}
+
+	/* and finally check that first_hole_records is in valid range */
+	max_records = MAIL_INDEX_RECORD_COUNT(index);
+	first_records = (hdr->first_hole_position -
+			 sizeof(MailIndexHeader)) / sizeof(MailIndexRecord);
+	if (index->header->first_hole_records > max_records ||
+	    first_records + index->header->first_hole_records > max_records) {
+		index_set_error(index, "Error in index file %s: "
+				"first_hole_records points outside file",
+				index->filepath);
+		INDEX_MARK_CORRUPTED(index);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static MailIndexRecord *mail_index_lookup_mapped(MailIndex *index,
 						 unsigned int lookup_seq)
 {
 	MailIndexHeader *hdr;
-	MailIndexRecord *rec, *end_rec;
+	MailIndexRecord *rec, *last_rec;
 	unsigned int seq;
 	uoff_t seekpos;
 
@@ -767,21 +812,34 @@
 		return index->last_lookup;
 	}
 
-	hdr = index->mmap_base;
-	rec = (MailIndexRecord *) ((char *) index->mmap_base +
-				   sizeof(MailIndexHeader));
+	hdr = index->header;
+	if (lookup_seq > hdr->messages_count) {
+		/* out of range */
+		return NULL;
+	}
+
+	if (!mail_index_verify_hole_range(index))
+		return NULL;
 
 	seekpos = sizeof(MailIndexHeader) +
 		(uoff_t)(lookup_seq-1) * sizeof(MailIndexRecord);
-	if (seekpos > index->mmap_length - sizeof(MailIndexRecord)) {
+	if (seekpos + sizeof(MailIndexRecord) > index->mmap_length) {
 		/* out of range */
 		return NULL;
 	}
 
+	rec = (MailIndexRecord *) ((char *) index->mmap_base +
+				   sizeof(MailIndexHeader));
+	last_rec = (MailIndexRecord *) ((char *) index->mmap_base +
+					index->mmap_length -
+					sizeof(MailIndexRecord));
+
 	if (hdr->first_hole_position == 0 ||
 	    hdr->first_hole_position > seekpos) {
 		/* easy, it's just at the expected index */
 		rec += lookup_seq-1;
+		i_assert(rec <= last_rec);
+
 		if (rec->uid == 0) {
 			index_set_error(index, "Error in index file %s: "
 					"first_hole_position wasn't updated "
@@ -801,15 +859,11 @@
 	} else {
 		/* some mails are deleted, jump after the first known hole
 		   and start counting non-deleted messages.. */
-		i_assert(hdr->first_hole_records > 0);
-
 		seq = INDEX_POSITION_INDEX(hdr->first_hole_position + 1) + 1;
 		rec += seq-1 + hdr->first_hole_records;
 	}
 
-	end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
-				       index->mmap_length);
-	while (seq < lookup_seq && rec < end_rec) {
+	while (seq < lookup_seq && rec <= last_rec) {
 		if (rec->uid != 0)
 			seq++;
 		rec++;
@@ -869,9 +923,7 @@
 
 	i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
 	i_assert(first_uid > 0 && last_uid > 0);
-
-	if (first_uid > last_uid)
-		return NULL;
+	i_assert(first_uid <= last_uid);
 
 	if (!mmap_update(index))
 		return NULL;
@@ -936,7 +988,7 @@
 	datarec = mail_index_data_lookup(index->data, rec, field);
 	if (datarec == NULL) {
 		/* corrupted, the field should have been there */
-		index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
+		INDEX_MARK_CORRUPTED(index);
 		return NULL;
 	}
 
@@ -966,6 +1018,9 @@
 			INDEX_FILE_POSITION(index, rec)) + 1;
 	}
 
+	if (!mail_index_verify_hole_range(index))
+		return 0;
+
 	seekrec = (MailIndexRecord *) ((char *) index->mmap_base +
 				       index->header->first_hole_position);
 	if (rec < seekrec) {
@@ -979,7 +1034,7 @@
 	seq = INDEX_POSITION_INDEX(INDEX_FILE_POSITION(index, seekrec))+1;
 	seekrec += index->header->first_hole_records;
 
-	for (; seekrec != rec; seekrec++) {
+	for (; seekrec < rec; seekrec++) {
 		if (seekrec->uid != 0)
 			seq++;
 	}
@@ -987,22 +1042,6 @@
 	return seq;
 }
 
-static void update_first_hole_records(MailIndex *index)
-{
-        MailIndexRecord *rec, *end_rec;
-
-	/* see if first_hole_records can be grown */
-	rec = (MailIndexRecord *) ((char *) index->mmap_base +
-				   index->header->first_hole_position) +
-		index->header->first_hole_records;
-	end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
-				       index->mmap_length);
-	while (rec != end_rec && rec->uid == 0) {
-		index->header->first_hole_records++;
-		rec++;
-	}
-}
-
 static void index_mark_flag_changes(MailIndex *index, MailIndexRecord *rec,
 				    MailFlags old_flags, MailFlags new_flags)
 {
@@ -1019,7 +1058,7 @@
 			index->header->first_unseen_uid_lowwater = rec->uid;
 
 		if (index->header->seen_messages_count == 0)
-			index->header->flags |= MAIL_INDEX_FLAG_FSCK;
+                        INDEX_MARK_CORRUPTED(index);
 		else
 			index->header->seen_messages_count--;
 	} else if ((old_flags & MAIL_DELETED) == 0 &&
@@ -1036,12 +1075,28 @@
 		   (new_flags & MAIL_DELETED) == 0) {
 		/* deleted -> undeleted */
 		if (index->header->deleted_messages_count == 0)
-			index->header->flags |= MAIL_INDEX_FLAG_FSCK;
+                        INDEX_MARK_CORRUPTED(index);
 		else
 			index->header->deleted_messages_count--;
 	}
 }
 
+static void update_first_hole_records(MailIndex *index)
+{
+        MailIndexRecord *rec, *end_rec;
+
+	/* see if first_hole_records can be grown */
+	rec = (MailIndexRecord *) ((char *) index->mmap_base +
+				   index->header->first_hole_position) +
+		index->header->first_hole_records;
+	end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
+				       index->mmap_length);
+	while (rec < end_rec && rec->uid == 0) {
+		index->header->first_hole_records++;
+		rec++;
+	}
+}
+
 static int mail_index_truncate(MailIndex *index)
 {
 	/* truncate index file */
@@ -1095,11 +1150,14 @@
 			index->last_lookup_seq--;
 	}
 
+	if (!mail_index_verify_hole_range(index))
+		return FALSE;
+
 	hdr = index->header;
 
 	/* update first hole */
 	pos = INDEX_FILE_POSITION(index, rec);
-	if (hdr->first_hole_position == 0) {
+	if (hdr->first_hole_position < sizeof(MailIndexRecord)) {
 		/* first deleted message in index */
 		hdr->first_hole_position = pos;
 		hdr->first_hole_records = 1;
@@ -1125,6 +1183,12 @@
 	}
 
 	/* update message counts */
+	if (hdr->messages_count == 0) {
+		/* corrupted */
+		INDEX_MARK_CORRUPTED(index);
+		return FALSE;
+	}
+
 	hdr->messages_count--;
 	index_mark_flag_changes(index, rec, rec->msg_flags, 0);
 
--- a/src/lib-index/mail-index.h	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-index/mail-index.h	Wed Aug 28 00:36:06 2002 +0300
@@ -202,7 +202,7 @@
 	const char *(*lookup_field)(MailIndex *index, MailIndexRecord *rec,
 				    MailField field);
 
-	/* Returns sequence for given message. */
+	/* Returns sequence for given message, or 0 if failed. */
 	unsigned int (*get_sequence)(MailIndex *index, MailIndexRecord *rec);
 
 	/* Open mail file and return it as mmap()ed IOBuffer, or
@@ -347,6 +347,7 @@
 int mail_index_rebuild_all(MailIndex *index);
 int mail_index_sync_file(MailIndex *index);
 int mail_index_fmsync(MailIndex *index, size_t size);
+int mail_index_verify_hole_range(MailIndex *index);
 void mail_index_update_headers(MailIndexUpdate *update, IOBuffer *inbuf,
                                MailField cache_fields,
 			       MessageHeaderFunc header_func, void *context);
@@ -369,4 +370,9 @@
 #define INDEX_MARK_CORRUPTED(index) \
 	STMT_START { (index)->set_flags |= MAIL_INDEX_FLAG_REBUILD; } STMT_END
 
+/* get number of records in mmaped index */
+#define MAIL_INDEX_RECORD_COUNT(index) \
+	((index->mmap_length - sizeof(MailIndexHeader)) / \
+	 sizeof(MailIndexRecord))
+
 #endif
--- a/src/lib-storage/index/index-expunge.c	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-storage/index/index-expunge.c	Wed Aug 28 00:36:06 2002 +0300
@@ -15,8 +15,10 @@
 
 	/* find mails with DELETED flag and expunge them */
 	if (hdr->first_deleted_uid_lowwater > 1) {
-		rec = ibox->index->lookup_uid_range(ibox->index,
-			hdr->first_deleted_uid_lowwater, hdr->next_uid-1);
+		rec = hdr->first_deleted_uid_lowwater >= hdr->next_uid ? NULL :
+			ibox->index->lookup_uid_range(ibox->index,
+						hdr->first_deleted_uid_lowwater,
+						hdr->next_uid-1);
 		if (rec == NULL) {
 			i_warning("index header's deleted_messages_count or "
 				  "first_deleted_uid_lowwater is invalid.");
--- a/src/lib-storage/index/index-status.c	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-storage/index/index-status.c	Wed Aug 28 00:36:06 2002 +0300
@@ -15,6 +15,9 @@
 	}
 
 	/* get the first recent message */
+	if (index->first_recent_uid >= hdr->next_uid)
+		return 0;
+
 	rec = index->lookup_uid_range(index, index->first_recent_uid,
 				      hdr->next_uid - 1);
 	if (rec == NULL)
--- a/src/lib-storage/index/index-sync.c	Wed Aug 28 00:04:08 2002 +0300
+++ b/src/lib-storage/index/index-sync.c	Wed Aug 28 00:36:06 2002 +0300
@@ -47,8 +47,7 @@
 				break;
 
 			rec = ibox->index->lookup_uid_range(ibox->index,
-							    log->uid,
-							    log->uid);
+							    log->uid, log->uid);
 			if (rec != NULL) {
 				flags = rec->msg_flags;
 				if (rec->uid >= ibox->index->first_recent_uid)