commit 39fc59391b7cf2a007bffce280c1e3e66674258f
Author: Tim Kientzle <kientzle@acm.org>
Date:   Sat Apr 11 22:44:12 2015 -0700

    Issue #522: Dir loop in malformed ISO causes segfault
    
    Github Issue #522 revealed that we could blow the stack
    when recursing to assemble ISO paths.  I saw this happen
    at 130,000 dir levels.  This patch addresses this by limiting
    the directory recursion to 1,000 elements.
    
    TODO:  It would be even better to track and detect the dir loop
    directly.

Index: libarchive-3.1.2/libarchive/archive_read_support_format_iso9660.c
===================================================================
--- libarchive-3.1.2.orig/libarchive/archive_read_support_format_iso9660.c
+++ libarchive-3.1.2/libarchive/archive_read_support_format_iso9660.c
@@ -387,7 +387,7 @@ static int	archive_read_format_iso9660_r
 static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);
 static int	archive_read_format_iso9660_read_header(struct archive_read *,
 		    struct archive_entry *);
-static const char *build_pathname(struct archive_string *, struct file_info *);
+static const char *build_pathname(struct archive_string *, struct file_info *, int);
 static int	build_pathname_utf16be(unsigned char *, size_t, size_t *,
 		    struct file_info *);
 #if DEBUG
@@ -1223,6 +1223,7 @@ archive_read_format_iso9660_read_header(
 			archive_set_error(&a->archive,
 			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Pathname is too long");
+			return (ARCHIVE_FATAL);
 		}
 
 		r = archive_entry_copy_pathname_l(entry,
@@ -1245,9 +1246,16 @@ archive_read_format_iso9660_read_header(
 			rd_r = ARCHIVE_WARN;
 		}
 	} else {
-		archive_string_empty(&iso9660->pathname);
-		archive_entry_set_pathname(entry,
-		    build_pathname(&iso9660->pathname, file));
+		const char *path = build_pathname(&iso9660->pathname, file, 0);
+		if (path == NULL) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname is too long");
+			return (ARCHIVE_FATAL);
+		} else {
+			archive_string_empty(&iso9660->pathname);
+			archive_entry_set_pathname(entry, path);
+		}
 	}
 
 	iso9660->entry_bytes_remaining = file->size;
@@ -1742,12 +1750,12 @@ parse_file_info(struct archive_read *a,
     const unsigned char *isodirrec)
 {
 	struct iso9660 *iso9660;
-	struct file_info *file;
+	struct file_info *file, *filep;
 	size_t name_len;
 	const unsigned char *rr_start, *rr_end;
 	const unsigned char *p;
 	size_t dr_len;
-	uint64_t fsize;
+	uint64_t fsize, offset;
 	int32_t location;
 	int flags;
 
@@ -1791,6 +1799,16 @@ parse_file_info(struct archive_read *a,
 		return (NULL);
 	}
 
+	/* Sanity check that this entry does not create a cycle. */
+	offset = iso9660->logical_block_size * (uint64_t)location;
+	for (filep = parent; filep != NULL; filep = filep->parent) {
+		if (filep->offset == offset) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Directory structure contains loop");
+			return (NULL);
+		}
+	}
+
 	/* Create a new file entry and copy data from the ISO dir record. */
 	file = (struct file_info *)calloc(1, sizeof(*file));
 	if (file == NULL) {
@@ -1799,7 +1817,7 @@ parse_file_info(struct archive_read *a,
 		return (NULL);
 	}
 	file->parent = parent;
-	file->offset = iso9660->logical_block_size * (uint64_t)location;
+	file->offset = offset;
 	file->size = fsize;
 	file->mtime = isodate7(isodirrec + DR_date_offset);
 	file->ctime = file->atime = file->mtime;
@@ -3164,10 +3182,17 @@ time_from_tm(struct tm *t)
 }
 
 static const char *
-build_pathname(struct archive_string *as, struct file_info *file)
+build_pathname(struct archive_string *as, struct file_info *file, int depth)
 {
+	// Plain ISO9660 only allows 8 dir levels; if we get
+	// to 1000, then something is very, very wrong.
+	if (depth > 1000) {
+		return NULL;
+	}
 	if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
-		build_pathname(as, file->parent);
+		if (build_pathname(as, file->parent, depth + 1) == NULL) {
+			return NULL;
+		}
 		archive_strcat(as, "/");
 	}
 	if (archive_strlen(&file->name) == 0)
