Add a Python test that lists a crafted SquashFS image whose directory
table declares an oversized metadata-block count. Such an image must be
rejected without corrupting the heap, which the test checks by confirming
U-Boot is still responsive afterwards.

Signed-off-by: shj <[email protected]>
---
 .../test_fs/test_squashfs/test_sqfs_overflow.py    | 75 ++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_overflow.py 
b/test/py/tests/test_fs/test_squashfs/test_sqfs_overflow.py
new file mode 100644
index 00000000000..df7f875a58f
--- /dev/null
+++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_overflow.py
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+# Regression test for the SquashFS directory-table integer overflow.
+
+import os
+import struct
+import pytest
+
+# metadata block size (SQFS_METADATA_BLOCK_SIZE) and metadata header size
+SQFS_METADATA_BLOCK_SIZE = 8192
+# metablks_count that makes metablks_count * SQFS_METADATA_BLOCK_SIZE wrap a
+# 32-bit int back down to a tiny value: (2^19 + 1) * 8192 == 2^32 + 8192.
+NR_METABLKS = (1 << 19) + 1
+
+def make_overflow_image(path):
+    """Build a SquashFS image whose directory table inflates metablks_count so
+    that metablks_count * SQFS_METADATA_BLOCK_SIZE wraps a 32-bit int, then
+    writes one block of data one metadata block past the resulting buffer."""
+    def metahdr(size):
+        # uncompressed metadata block header (bit 15 set)
+        return struct.pack('<H', 0x8000 | (size & 0x7fff))
+
+    # inode table: one small valid uncompressed metadata block
+    inode_region = metahdr(32) + b'\x00' * 32
+    inode_start = 96
+    dir_start = inode_start + len(inode_region)
+
+    # directory table: NR_METABLKS metadata blocks, only block 1 carries data
+    dir_region = bytearray()
+    dir_region += metahdr(0)                          # block 0: empty
+    dir_region += metahdr(200) + b'A' * 200           # block 1: data
+    dir_region += metahdr(0) * (NR_METABLKS - 2)      # blocks 2..N-1: empty
+    frag_start = dir_start + len(dir_region)
+
+    sb = bytearray(96)
+    struct.pack_into('<I', sb, 0, 0x73717368)         # s_magic
+    struct.pack_into('<I', sb, 4, 1)                  # inodes
+    struct.pack_into('<I', sb, 12, 131072)            # block_size
+    struct.pack_into('<H', sb, 20, 1)                 # compression = gzip/zlib
+    struct.pack_into('<H', sb, 22, 17)                # block_log
+    struct.pack_into('<H', sb, 26, 1)                 # no_ids
+    struct.pack_into('<H', sb, 28, 4)                 # s_major
+    struct.pack_into('<Q', sb, 48, frag_start)        # id_table_start
+    struct.pack_into('<Q', sb, 56, 0xffffffffffffffff)  # xattr_id_table_start
+    struct.pack_into('<Q', sb, 64, inode_start)       # inode_table_start
+    struct.pack_into('<Q', sb, 72, dir_start)         # directory_table_start
+    struct.pack_into('<Q', sb, 80, frag_start)        # fragment_table_start
+    struct.pack_into('<Q', sb, 88, 0xffffffffffffffff)  # export_table_start
+
+    img = bytearray(sb) + inode_region + dir_region
+    # pad so the directory-table block read stays within the file
+    need = ((len(img) + 511) // 512 + 1) * 512
+    img += b'\x00' * (need - len(img))
+    struct.pack_into('<Q', img, 40, len(img))         # bytes_used
+
+    with open(path, 'wb') as f:
+        f.write(img)
+
[email protected]('sandbox')
[email protected]('cmd_squashfs')
[email protected]('fs_squashfs')
[email protected]
+def test_sqfs_ls_dir_table_overflow(ubman):
+    """Listing a crafted image whose directory table declares an oversized
+    metadata-block count must be rejected without corrupting the heap.
+    """
+    ubman.restart_uboot()
+    image_path = os.path.join(ubman.config.build_dir, 
'sqfs_dir_table_overflow')
+    make_overflow_image(image_path)
+    try:
+        ubman.run_command('host bind 0 {}'.format(image_path))
+        ubman.run_command('sqfsls host 0')
+        # The crafted image must not take the board down.
+        assert 'alive' in ubman.run_command('echo alive')
+    finally:
+        os.remove(image_path)

-- 
2.43.0

Reply via email to