summaryrefslogtreecommitdiff
path: root/test/py
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-08-27 15:05:06 -0600
committerTom Rini <[email protected]>2026-08-27 15:05:06 -0600
commit658fc6aee95784139f070f9945cb3bc5da8d7d22 (patch)
tree1e8bd0eb2c2666181e89b59fb88e079b90855f31 /test/py
parentced85f31db7abb858db2f88894e0166a177e3192 (diff)
parent4750bcfe857c5a0feda86b54f260a11a4e3222cd (diff)
Merge patch series "fs/squashfs: fix directory table integer overflow"HEADmain
Shahriyar Jalayeri <[email protected]> says: This fixes an integer overflow in the SquashFS directory-table reader that leads to a heap out-of-bounds write, and adds a regression test. sqfs_read_directory_table() sizes the directory table with an int multiply (metablks_count * SQFS_METADATA_BLOCK_SIZE) that wraps for a crafted image, under-allocating the buffer that the fill loop then overruns. It is reached by listing or reading the image (sqfsls / sqfsload). Patch 1 guards the allocation with __builtin_mul_overflow(); patch 2 adds a test that a crafted image is rejected. Based on v2026.07 (fdfe2ec48d5c). A reproducer is available on request. [trini: As part of the merge, this touches on what commit 9a9d46cb5e1a ("fs/squashfs: fix heap exhaustion during symlink resolution") also handles, but they appear to be separate issues] Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'test/py')
-rw-r--r--test/py/tests/test_fs/test_squashfs/test_sqfs_overflow.py75
1 files changed, 75 insertions, 0 deletions
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]('cmd_squashfs')
[email protected]('fs_squashfs')
+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)