Add a parametrized regression test for the fix in the previous commit. The test invokes mkimage in auto-FIT mode (-f auto) with a -b argument whose directory component contains a '.' and whose leaf either lacks an extension or is a plain identifier. Before the fix these inputs caused get_basename() to compute a negative length and segfault inside memcpy. The test asserts that mkimage exits successfully and that the fdt sub-image description matches the expected stripped basename, covering "./mydt", "./sub.d/leaf", and "./a.b/c". A control input of "./mydt.dtb" is also exercised to confirm normal extension stripping still works. Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
163 lines
5.2 KiB
Python
163 lines
5.2 KiB
Python
# SPDX-License-Identifier: GPL-2.0+
|
|
# Copyright (c) 2025
|
|
#
|
|
# Test that mkimage validates image references in configurations
|
|
|
|
import os
|
|
import subprocess
|
|
import pytest
|
|
import fit_util
|
|
import utils
|
|
import re
|
|
|
|
@pytest.mark.boardspec('sandbox')
|
|
@pytest.mark.requiredtool('dtc')
|
|
def test_fit_invalid_image_reference(ubman):
|
|
"""Test that mkimage fails when configuration references a missing image"""
|
|
|
|
its_fname = fit_util.make_fname(ubman, "invalid.its")
|
|
itb_fname = fit_util.make_fname(ubman, "invalid.itb")
|
|
kernel = fit_util.make_kernel(ubman, 'kernel.bin', 'kernel')
|
|
|
|
# Write ITS with an invalid reference to a nonexistent image
|
|
its_text = '''
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
images {
|
|
kernel@1 {
|
|
description = "Test Kernel";
|
|
data = /incbin/("kernel.bin");
|
|
type = "kernel";
|
|
arch = "sandbox";
|
|
os = "linux";
|
|
compression = "none";
|
|
load = <0x40000>;
|
|
entry = <0x40000>;
|
|
};
|
|
};
|
|
|
|
configurations {
|
|
default = "conf@1";
|
|
conf@1 {
|
|
kernel = "kernel@1";
|
|
fdt = "notexist";
|
|
};
|
|
};
|
|
};
|
|
'''
|
|
|
|
with open(its_fname, 'w') as f:
|
|
f.write(its_text)
|
|
|
|
mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage')
|
|
cmd = [mkimage, '-f', its_fname, itb_fname]
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
|
|
assert result.returncode != 0, "mkimage should fail due to missing image reference"
|
|
assert "references undefined image 'notexist'" in result.stderr
|
|
|
|
@pytest.mark.boardspec('sandbox')
|
|
@pytest.mark.requiredtool('dtc')
|
|
def test_fit_invalid_default_config(ubman):
|
|
"""Test that mkimage fails when default config is missing"""
|
|
|
|
its_fname = fit_util.make_fname(ubman, "invalid.its")
|
|
itb_fname = fit_util.make_fname(ubman, "invalid.itb")
|
|
kernel = fit_util.make_kernel(ubman, 'kernel.bin', 'kernel')
|
|
|
|
# Write ITS with an invalid reference to a nonexistent default config
|
|
its_text = '''
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
images {
|
|
kernel@1 {
|
|
description = "Test Kernel";
|
|
data = /incbin/("kernel.bin");
|
|
type = "kernel";
|
|
arch = "sandbox";
|
|
os = "linux";
|
|
compression = "none";
|
|
load = <0x40000>;
|
|
entry = <0x40000>;
|
|
};
|
|
};
|
|
|
|
configurations {
|
|
default = "conf@1";
|
|
conf@2 {
|
|
kernel = "kernel@1";
|
|
};
|
|
};
|
|
};
|
|
'''
|
|
|
|
with open(its_fname, 'w') as f:
|
|
f.write(its_text)
|
|
|
|
mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage')
|
|
cmd = [mkimage, '-f', its_fname, itb_fname]
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
|
|
assert result.returncode != 0, "mkimage should fail due to missing default config"
|
|
assert re.search(r"Default configuration '.*' not found under /configurations", result.stderr)
|
|
|
|
@pytest.mark.boardspec('sandbox')
|
|
@pytest.mark.requiredtool('dtc')
|
|
@pytest.mark.requiredtool('fdtget')
|
|
@pytest.mark.parametrize('dtb_relpath,expected_desc', [
|
|
# Crash triggers: last '.' precedes last '/', or leaf has no extension.
|
|
('./mydt', 'mydt'),
|
|
('./sub.d/leaf', 'leaf'),
|
|
('./a.b/c', 'c'),
|
|
# Control case: extension lives in the leaf, no dotted directory.
|
|
('./mydt.dtb', 'mydt'),
|
|
])
|
|
def test_fit_auto_basename_dotted_directory(ubman, dtb_relpath, expected_desc):
|
|
"""Regression test: mkimage -f auto must not crash when a -b path has a
|
|
'.' in its directory portion.
|
|
|
|
Before the fix, get_basename() in tools/fit_image.c searched the whole
|
|
path for both the last '/' and the last '.'. When the '.' fell before
|
|
the '/', the computed length went negative and was passed unchanged to
|
|
memcpy(), which segfaulted. This test exercises three crashing paths
|
|
plus one control input.
|
|
"""
|
|
build_dir = ubman.config.build_dir
|
|
kernel = fit_util.make_kernel(ubman, 'kernel.bin', 'kernel')
|
|
itb_fname = fit_util.make_fname(ubman, 'auto_basename.itb')
|
|
|
|
# Materialize the dtb at the requested relative path inside build_dir.
|
|
dtb_abs = os.path.join(build_dir, dtb_relpath)
|
|
os.makedirs(os.path.dirname(dtb_abs), exist_ok=True)
|
|
with open(dtb_abs, 'wb') as f:
|
|
f.write(b'dummy')
|
|
|
|
cmd = ['./tools/mkimage', '-f', 'auto',
|
|
'-A', 'arm', '-O', 'linux', '-T', 'kernel', '-C', 'none',
|
|
'-a', '0x80000000', '-e', '0x80000000', '-n', 'test',
|
|
'-d', kernel,
|
|
'-b', dtb_relpath,
|
|
itb_fname]
|
|
# Run with cwd=build_dir so both ./tools/mkimage and the relative -b
|
|
# path resolve the same way the bug originally reproduced.
|
|
result = subprocess.run(cmd, capture_output=True, text=True,
|
|
cwd=build_dir)
|
|
|
|
assert result.returncode == 0, (
|
|
f"mkimage crashed or failed on -b {dtb_relpath!r}: "
|
|
f"rc={result.returncode}\nstdout:\n{result.stdout}\n"
|
|
f"stderr:\n{result.stderr}"
|
|
)
|
|
# The fdt sub-image description is set from get_basename(). Read it back
|
|
# from the produced FIT (a device tree) rather than parsing mkimage's
|
|
# console output.
|
|
desc = utils.run_and_log(
|
|
ubman, ['fdtget', itb_fname, '/images/fdt-1', 'description']).strip()
|
|
assert desc == expected_desc, (
|
|
f"Expected /images/fdt-1 description {expected_desc!r}, got {desc!r}"
|
|
)
|