Skip to content

cross-libc-dlopen.so causes all chromium browsers to crash only in aarch64 #37

Closed

Description

@Samueru-sama

Context: pkgforge-dev/Helium-Browser-AppImage-Enhanced#10

It only affects aarch64 for some reason.

Activity

  1. Link4Electronics commented on Sep 13, 2026

    @Link4Electronics
    Member

    https://github.com/pkgforge-dev/Ungoogled-Chromium-AppImage aarch64 build

    [0913/020816.280558:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020817.310426:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020817.404312:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020817.465165:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020817.495398:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020817.639103:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [0913/020817.648193:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020817.984770:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020818.158415:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [0913/020818.178972:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020826.840667:ERROR:third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.cc:27] ptrace: No such process (3)
    [0913/020826.841307:ERROR:third_party/crashpad/crashpad/util/file/directory_reader_posix.cc:43] opendir /proc/2067/task: No such file or directory (2)
    [0913/020826.844244:ERROR:third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.cc:27] ptrace: No such process (3)
    [0913/020826.844367:ERROR:third_party/crashpad/crashpad/util/file/directory_reader_posix.cc:43] opendir /proc/2068/task: No such file or directory (2)
    [0913/020826.852271:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020827.113185:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [0913/020828.033634:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020829.004293:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020829.465819:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [0913/020829.494239:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020829.981306:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020830.408698:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [0913/020830.902519:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [1910:1910:0913/020831.387566:ERROR:content/browser/gpu/gpu_process_host.cc:1035] GPU process exited unexpectedly: exit_code=139
    [1910:1910:0913/020831.387711:FATAL:content/browser/gpu/gpu_data_manager_impl_private.cc:417] GPU process isn't usable. Goodbye.
    [0913/020831.400345:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    [0913/020831.449152:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc:678] no dynamic segment
    xiaomi-beryllium:/run/media/link/00a99c68-cbb9-4703-9a16-a745323da3dc$ 
    
  2. talaria0101 commented on Sep 13, 2026

    @talaria0101
    Contributor

    Root cause found and measured: the generated forward shim exports a strong __stack_chk_guard, and on aarch64 that name is the process-wide stack canary owned by ld-linux. Preloading cross-libc-dlopen.so therefore interposes the canary itself, which kills chromium's GPU process (SIGSEGV, exit_code=139) independently of the dlopen hook.

    The defect

    The shipped aarch64 artefact exports the name as a strong function stub:

    $ readelf --dyn-syms -W aarch64-cross-libc-dlopen.so | grep stack_chk
        96: 0000000000006744    20 FUNC    GLOBAL DEFAULT   11 __stack_chk_guard
    

    Section 11 is .text: this is shim_stub___stack_chk_guard from src/forward-shim.c, emitted because src/forward-shim-manifest.json classifies the name as stub-only: no audited implementation.

    On aarch64 the name is not shim-only. glibc keeps the stack canary in thread-pointer storage only where THREAD_SET_STACK_GUARD exists (x86, x86-64, and friends, where the symbol is never exported). On aarch64 the canary is a global object exported by the loader and written by security_init():

    ld-linux-aarch64.so.1:   8 OBJECT GLOBAL DEFAULT __stack_chk_guard@@GLIBC_2.17
    libc.so.6 (aarch64):     UND __stack_chk_guard@GLIBC_2.17, R_AARCH64_GLOB_DAT
    

    glibc's lookup accepts an unversioned definition for a versioned reference (the interposition case in check_match), so once the preload is in scope every __stack_chk_guard GOT slot in the process, libc's own included, binds to the stub in the preload's read-only .text instead of the object the loader initializes. Canary values then disagree across the process, and the GPU process dies before any cross-libc logic runs. On x86-64 nothing references the name, which is why the x86_64 artefact is unaffected and why only aarch64 regressed.

    Measurements

    1. Real aarch64 silicon (ubuntu-24.04-arm), Helium 0.17.0.1 anylinux build, run twice against the packaged AppImage under xvfb:

      • preload present, CROSS_LIBC_DLOPEN=0: 6 x GPU process exited unexpectedly: exit_code=139
      • preload present, default mode: the same 6 crashes

      The mode switch only disables the dlopen hook; the canary interposition happens at relocation time, so it changes nothing. Removing the preload stops the crash (the debug branch in Helium PR Record the merge, and why the release tag was not pushed #10).

    2. The same build on x86_64: 0 GPU crashes in both arms.

    3. The symbol has been exported by every aarch64 artefact since at least v0.2.0 (readelf above shows the same entry in the v0.2.0 download). This matches the CI record: every chromium --test on aarch64 with the preload present has failed (Helium, Brave-Origin, Chrome, 2026-09-12), and the only passing chromium runs use --simple-test, which never initializes GL.

    4. Generator root cause: tools/gen_forward_shim.py decides FUNC versus OBJECT from target.get("kinds"), the x86-derived glibc inventory where the symbol is absent, instead of the merged kinds it builds from inventories/musl-1.2.5.json, where __stack_chk_guard is an 8-byte OBJECT. A function stub is emitted for what must be data.

    Why the in-repo aarch64 gates stayed green: the evidence probes are small and self-consistent canary reads do not fault, and the wide appimage suite has failed at its extraction step since late August, so no real aarch64 application run covered this.

    Not yet captured: the exact faulting instruction inside the crashed child (the run produced no crashpad dumps; a gdb run with --in-process-gpu is the next measurement). The causal chain above is established by the A/B, not by the backtrace.

    Proposed fix

    1. Primary: never emit __stack_chk_guard from the shim, and exclude loader-owned data generally. On global-guard architectures ld-linux supplies the real object. Weak or zero-data definitions would still interpose and would silently zero the process canary, so exclusion is the clean fix, not retyping.
    2. Defence in depth: emit stubs from the merged kinds so musl OBJECT symbols become data, not functions.
    3. Add a per-architecture collision gate: after each build, assert the artefact's exported dynamic symbols do not collide with the target libc.so.6/ld-linux exports. The current stale-shim guard compares only against the x86 floor inventory, where the symbol does not exist.
    4. Measure riscv64 and loongarch64 artefacts the same way before their next consumer: they are global-guard architectures too and ship from the same manifest.

    Evidence logs: https://github.com/talaria0101/Helium-Browser-AppImage-Enhanced/actions/runs/34740952842 (fork, workflow patched only to keep failing artefacts and add the instrumented run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions