sui's in-place ELF note appending (introduced in 9c63027) adds a .note.sui SHT_NOTE section header with SHF_ALLOC. This section lands at a file offset / vaddr range that's discontiguous from the original allocated sections. elfutils eu-strip (used by flatpak's SDK and many Linux distributions) cannot reconstruct valid PT_LOAD entries from this layout and writes all-NULL program headers, producing a binary that segfaults on exec.
Reproduction
cat > hello.c << 'EOF'
#include <stdio.h>
int main(void) { printf("hello\n"); return 0; }
EOF
gcc -o hello hello.c
echo -n "payload" > /tmp/data
git clone --depth=1 https://github.com/littledivy/sui.git
env -u CARGO_TARGET_DIR cargo build --manifest-path sui/Cargo.toml -q
./sui/target/debug/sui testdata hello /tmp/data hello.sui
chmod +x hello.sui
cp hello.sui hello.stripped
eu-strip -s hello.stripped
./hello.stripped # SEGFAULT (exit 139)
Full POC script:
#!/usr/bin/env bash
# POC: eu-strip corrupts ELF binaries modified by sui
# Requirements: gcc, cargo, eu-strip, git
# Reproduces the deno compile segfault on flatpak (exit 139)
set -euo pipefail
DIR=$(mktemp -d)
cd "$DIR"
echo "=== 1. Build a test binary ==="
cat > hello.c << 'EOF'
#include <stdio.h>
int main(void) { printf("hello\n"); return 0; }
EOF
gcc -o hello hello.c
echo "Before: $(file hello)"
./hello && echo "exit 0"
echo ""
echo "=== 2. Build sui ==="
git clone --depth=1 https://github.com/littledivy/sui.git 2>/dev/null
env -u CARGO_TARGET_DIR cargo build --manifest-path sui/Cargo.toml -q 2>&1
SUI=./sui/target/debug/sui
echo ""
echo "=== 3. Modify with sui ==="
echo -n "test payload" > /tmp/sui-data
$SUI testdata hello /tmp/sui-data hello.sui
chmod +x hello.sui
echo "After sui: $(file hello.sui)"
./hello.sui && echo "exit 0"
echo ""
echo "=== 4. Strip with eu-strip (elfutils) ==="
cp hello.sui hello.stripped
eu-strip -s hello.stripped 2>&1
echo "After eu-strip: $(file hello.stripped)"
r=0
./hello.stripped || r=$?
echo "exit $r"
echo ""
if [ "$r" -eq 139 ]; then
echo "SEGFAULT! eu-strip zeroed all program headers."
readelf -l hello.stripped 2>/dev/null | head -12
echo ""
echo "For comparison, binutils strip works fine:"
cp hello.sui hello.ok
strip -s hello.ok
./hello.ok && echo "binutils strip: exit 0"
fi
rm -rf "$DIR" /tmp/sui-data
Expected
eu-strip produces a working stripped binary (same as strip -s from binutils, which handles it correctly).
Actual
eu-strip zeros out all program headers — all 15 entries become PT_NULL:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0x0
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0x0
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0x0
...
The binary is reported as "statically linked" (because PT_DYNAMIC is gone) and segfaults immediately.
Root cause
The .note.sui section is placed via new PT_LOAD / PT_NOTE entries at the end of the file (file offset 0x4348, vaddr 0x404348), while all original allocated sections live in the 0x0000–0x2000 file range (vaddr 0x400000–0x403000). eu-strip tries to reconstruct PT_LOAD segments from the section headers and cannot satisfy the ELF congruence (p_offset % align == p_vaddr % align) across this discontiguity. It falls back to writing null entries.
binutils strip avoids this because it preserves the original program headers verbatim rather than reconstructing them from sections.
Fix suggestion
Remove the SHT_NOTE section header addition for .note.sui. The PT_NOTE program header alone is sufficient for runtime discovery via dl_iterate_phdr. A fully stripped binary already takes this path (see comment at line 1332), and eu-strip preserves PT_NOTE entries even without matching sections.
Alternatively, place the relocated .shstrtab and section header table inside the new PT_LOAD so the section-to-segment coverage is contiguous — but this is more invasive.
Environment
eu-strip (elfutils) 0.193 / 0.195
strip (binutils) 2.46 — unaffected
- Tested on Fedora 44, also inside
org.gnome.Platform//50 flatpak SDK
sui's in-place ELF note appending (introduced in 9c63027) adds a.note.suiSHT_NOTEsection header withSHF_ALLOC. This section lands at a file offset / vaddr range that's discontiguous from the original allocated sections. elfutilseu-strip(used by flatpak's SDK and many Linux distributions) cannot reconstruct validPT_LOADentries from this layout and writes all-NULLprogram headers, producing a binary that segfaults on exec.Reproduction
Full POC script:
Expected
eu-stripproduces a working stripped binary (same asstrip -sfrom binutils, which handles it correctly).Actual
eu-stripzeros out all program headers — all 15 entries becomePT_NULL:The binary is reported as "statically linked" (because
PT_DYNAMICis gone) and segfaults immediately.Root cause
The
.note.suisection is placed via newPT_LOAD/PT_NOTEentries at the end of the file (file offset0x4348, vaddr0x404348), while all original allocated sections live in the0x0000–0x2000file range (vaddr0x400000–0x403000).eu-striptries to reconstructPT_LOADsegments from the section headers and cannot satisfy the ELF congruence (p_offset % align == p_vaddr % align) across this discontiguity. It falls back to writing null entries.binutils stripavoids this because it preserves the original program headers verbatim rather than reconstructing them from sections.Fix suggestion
Remove the
SHT_NOTEsection header addition for.note.sui. ThePT_NOTEprogram header alone is sufficient for runtime discovery viadl_iterate_phdr. A fully stripped binary already takes this path (see comment at line 1332), andeu-strippreservesPT_NOTEentries even without matching sections.Alternatively, place the relocated
.shstrtaband section header table inside the newPT_LOADso the section-to-segment coverage is contiguous — but this is more invasive.Environment
eu-strip(elfutils) 0.193 / 0.195strip(binutils) 2.46 — unaffectedorg.gnome.Platform//50flatpak SDK