From c3cb1c568ebf9e8f7f478cfc0356ae54e99712b0 Mon Sep 17 00:00:00 2001
From: elhananhaenel <elhanan.haenel@mail.huji.ac.il>
Date: Sat, 7 Mar 2026 22:14:23 +0200
Subject: [PATCH 1/2] iso9660: validate pz_log2_bs in parse_rockridge_ZF1()

The zisofs block size exponent (pz_log2_bs) read from the Rock Ridge ZF
extension entry is used directly in shift expressions without validation.
The zisofs specification only permits values 15, 16, or 17 (corresponding
to 32K, 64K, and 128K block sizes).

When pz_log2_bs >= 64 on 64-bit systems (or >= 32 on 32-bit), the
expression (size_t)1UL << pz_log2_bs is undefined behavior per C11
6.5.7. On 32-bit systems, a large exponent also causes the block pointer
allocation size computation (ceil + 1) * 4 to overflow to zero, leading
to a heap buffer overflow write after malloc(0).

Fix: reject any pz_log2_bs outside the range [15, 17] by disabling
zisofs for the entry (file->pz = 0), which prevents the zisofs
decompression path from executing.

Found by fuzzing with ASAN/UBSAN.
---
 libarchive/archive_read_support_format_iso9660.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

Index: libarchive-3.8.1/libarchive/archive_read_support_format_iso9660.c
===================================================================
--- libarchive-3.8.1.orig/libarchive/archive_read_support_format_iso9660.c
+++ libarchive-3.8.1/libarchive/archive_read_support_format_iso9660.c
@@ -2756,11 +2756,19 @@ parse_rockridge_ZF1(struct file_info *fi
 {
 
 	if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
-		/* paged zlib */
-		file->pz = 1;
-		file->pz_log2_bs = data[3];
-		file->pz_uncompressed_size = archive_le32dec(&data[4]);
-	}
+        /* paged zlib */
+        file->pz = 1;
+        file->pz_log2_bs = data[3];
+        if (file->pz_log2_bs < 15 || file->pz_log2_bs > 17) {
+            /* TODO: Return an error here instead of silently
+             * disabling zisofs. That requires propagating an
+             * error return through parse_rockridge() and its
+             * callers. */
+            file->pz = 0;
+            return;
+        }
+        file->pz_uncompressed_size = archive_le32dec(&data[4]);
+    }
 }
 
 static void
