Skip to content

fix(linter): skip foreign-arch ELF files in library linter - #6249

Open
lengau wants to merge 1 commit into
mainfrom
work/fix-4373
Open

fix(linter): skip foreign-arch ELF files in library linter#6249
lengau wants to merge 1 commit into
mainfrom
work/fix-4373

Conversation

@lengau

@lengau lengau commented May 15, 2026

Copy link
Copy Markdown
Contributor

The library linter calls load_dependencies() on every ELF file found in the prime directory. For foreign-arch builds (e.g. arm64 snap on an amd64 host) this invokes QEMU via binfmt to run the foreign binary, which fails with 'not a dynamic executable' errors and can SEGFAULT, producing multi-gigabyte core dumps.

Add elf_machine field to _ArchConfig and get_host_elf_machine() to elf_utils.py. In library_linter.run(), skip any ELF file whose arch_tuple[2] (e_machine) does not match the host's expected value.

Fixes: #4373


  • I've followed the contribution guidelines.
  • I've signed the CLA.
  • I've successfully run make lint && make test.
  • I've added or updated any relevant documentation.
  • In documents I changed, I added a meta description if one was missing.
  • I've updated the relevant release notes.

The library linter calls load_dependencies() on every ELF file found in
the prime directory. For foreign-arch builds (e.g. arm64 snap on an
amd64 host) this invokes QEMU via binfmt to run the foreign binary,
which fails with 'not a dynamic executable' errors and can SEGFAULT,
producing multi-gigabyte core dumps.

Add elf_machine field to _ArchConfig and get_host_elf_machine() to
elf_utils.py. In library_linter.run(), skip any ELF file whose
arch_tuple[2] (e_machine) does not match the host's expected value.

Fixes: #4373

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a bug where snapcraft's library linter invokes foreign-arch ELF binaries (via binfmt/QEMU) when building cross-arch snaps, causing spurious errors and large core dumps. The linter now compares each ELF's e_machine against the host's value and skips foreign binaries.

Changes:

  • Added elf_machine to _ArchConfig and a new get_host_elf_machine() helper in snapcraft/elf/elf_utils.py.
  • Added a foreign-arch skip in LibraryLinter.run() based on elf_file.arch_tuple[2].
  • Added unit and integration tests; unrelated FakeProvider methods (list_instances, prune) added to tests/unit/conftest.py.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
snapcraft/elf/elf_utils.py Adds elf_machine field to _ArchConfig and get_host_elf_machine() helper.
snapcraft/linters/library_linter.py Skips ELF files whose e_machine doesn't match the host's.
tests/unit/linters/test_library_linter.py Adds arch_tuple to mocks and a new unit test for the skip behavior.
tests/integration/linters/test_library_linter.py New integration test that downloads a powerpc bash .deb to verify the skip.
tests/unit/conftest.py Unrelated additions of list_instances/prune to FakeProvider.
Comments suppressed due to low confidence (1)

snapcraft/linters/library_linter.py:81

  • elf_file.arch_tuple can be None (see snapcraft/elf/_elf_file.py:198, where it's initialised to None and only populated when the ELF header is successfully parsed). Treating arch_tuple is None as "do not skip" means a file whose architecture could not be determined will still be passed to load_dependencies(), which immediately raises RuntimeError("failed to parse architecture") (see _elf_file.py:390-391). Consider skipping these files as well, rather than letting them through.
            if (
                host_elf_machine is not None
                and elf_file.arch_tuple is not None
                and elf_file.arch_tuple[2] != host_elf_machine
            ):
                emit.debug(
                    f"Skipping library linting for foreign-arch ELF "
                    f"{str(elf_file.path)!r} ({elf_file.arch_tuple[2]})"
                )
                continue

Comment on lines +30 to +61
_POWERPC_BASH_URL = "https://old-releases.ubuntu.com/ubuntu/pool/main/b/bash/bash_2.05b-15ubuntu5_powerpc.deb"


def setup_function():
elf_utils.get_elf_files.cache_clear()


@pytest.fixture
def powerpc_bash(tmp_path):
"""Download and extract the bash binary from a powerpc .deb package.

Returns the path to the extracted bash executable, which is a 32-bit
big-endian PowerPC ELF (EM_PPC) — foreign on every supported host arch.
"""
deb_path = tmp_path / "bash-powerpc.deb"
extract_dir = tmp_path / "bash-powerpc"

try:
urllib.request.urlretrieve(_POWERPC_BASH_URL, deb_path) # noqa: S310
except OSError as exc:
pytest.skip(f"Could not download powerpc bash package: {exc}")

extract_dir.mkdir()
result = subprocess.run(
["dpkg-deb", "-x", str(deb_path), str(extract_dir)],
capture_output=True,
check=False,
)
if result.returncode != 0:
pytest.skip(f"Could not extract powerpc bash package: {result.stderr.decode()}")

return extract_dir / "bin" / "bash"
@@ -0,0 +1,88 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
Comment on lines +72 to +81
if (
host_elf_machine is not None
and elf_file.arch_tuple is not None
and elf_file.arch_tuple[2] != host_elf_machine
):
emit.debug(
f"Skipping library linting for foreign-arch ELF "
f"{str(elf_file.path)!r} ({elf_file.arch_tuple[2]})"
)
continue
Comment thread tests/unit/conftest.py
Comment on lines +418 to +428
def list_instances(
self,
*,
project_name: str | None = None,
instance_name_prefix: str | None = None,
include_base_instances: bool = False,
):
return []

def prune(self, *, project_name: str, prune_templates: bool = False) -> None:
pass
_POWERPC_BASH_URL = "https://old-releases.ubuntu.com/ubuntu/pool/main/b/bash/bash_2.05b-15ubuntu5_powerpc.deb"


def setup_function():
Comment on lines +70 to +79
# Skip ELF files for foreign architectures — invoking them via binfmt/QEMU
# to resolve dependencies causes spurious errors and potential SEGFAULTs.
if (
host_elf_machine is not None
and elf_file.arch_tuple is not None
and elf_file.arch_tuple[2] != host_elf_machine
):
emit.debug(
f"Skipping library linting for foreign-arch ELF "
f"{str(elf_file.path)!r} ({elf_file.arch_tuple[2]})"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

library linter fails on foreign archs and can't be disabled for core20

2 participants