fix: Linux launch crash on glibc 2.42+ (bypass jpackage native launcher)#685
fix: Linux launch crash on glibc 2.42+ (bypass jpackage native launcher)#685rainxchzed wants to merge 4 commits into
Conversation
WalkthroughLinux desktop crashes on startup when glibc >= 2.42 due to a broken jpackage native launcher. This PR fixes the issue by introducing a JVM-direct launcher script that parses jpackage app config and executes the bundled Java directly, then integrates this launcher into all Linux package formats (AppImage, Debian, RPM, Arch) and documents the fix in release notes. ChangesLinux launcher wrapper and packaging integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes a Linux launch crash on glibc 2.42+ (Fedora 43, Ubuntu 26.04, Debian 13, Bazzite) by bypassing the jpackage-generated native launcher, which segfaults in
Confidence Score: 4/5Safe to merge for AppImage, deb, and Arch users; rpm users could receive the unpatched crash-inducing binary if an edge-case mv failure goes undetected. The launcher script and the AppImage/Arch/deb wiring look correct. The rpm step intentionally drops rpms on failure to avoid shipping the broken binary, but the mv -f call on line 428 is not guarded — a silent failure there leaves the original ELF in place and it gets uploaded to release artifacts. Every other failure path in that step explicitly deletes the rpm; this one is the only gap. .github/workflows/build-desktop-platforms.yml — specifically the rpm patching step around line 428. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[User launches app] --> B{Package type}
B -->|AppImage| C[AppRun]
B -->|deb / Arch| D[github-store-launcher.sh via PATH symlink / Exec=]
B -->|rpm| E[GitHub-Store replaced by wrapper]
C --> D
E --> D
D --> F[readlink -f BASH_SOURCE, resolve BINDIR / ROOTDIR]
F --> G[find lib/app/*.cfg]
G --> H[Parse cfg: classpath, mainclass, modulepath, javaopts]
H --> I{mainmodule set?}
I -->|Yes| J[exec java --module-path ... -m module]
I -->|No| K[exec java -cp classpath mainclass]
J --> L[JVM starts]
K --> L
Reviews (2): Last reviewed commit: "Merge branch 'main' into fix/563-linux-l..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build-desktop-platforms.yml:
- Around line 412-434: The workflow currently continues and may publish original
RPMs when patching fails; change the failure branches so the job exits non‑zero
instead of continuing. Specifically, replace the apt-get failure path that
currently does "echo ...; exit 0" with a non‑zero exit (e.g. exit 1) and
similarly change the rpmrebuild failure branches (the else blocks that echo
warnings after rpmrebuild or when "$rebuilt" is empty) to fail the job (exit 1)
rather than logging and continuing; ensure you also do not mv the original file
when rpmrebuild produced no output (the mv inside the success branch should
remain the only place originals are overwritten). Use the WRAPPER, OUTDIR,
rpmrebuild invocation, and the "$rebuilt" check to locate the exact places to
modify.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fcb9fd13-e2de-401f-988c-227342bd793d
📒 Files selected for processing (15)
.github/workflows/build-desktop-platforms.ymlcore/presentation/src/commonMain/composeResources/files/whatsnew/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ar/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/bn/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/es/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/fr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/hi/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/it/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ja/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ko/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/pl/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ru/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/tr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/19.jsonpackaging/linux/github-store-launcher.sh
|
Addressed all four review findings: Launcher (
RPM CI (
|
| set -uo pipefail | ||
| shopt -s nullglob | ||
| rpms=(composeApp/build/compose/binaries/main/rpm/*.rpm) | ||
| [ ${#rpms[@]} -gt 0 ] || { echo "No rpm to patch"; exit 0; } | ||
|
|
||
| if ! sudo apt-get install -y rpmrebuild rpm >/dev/null 2>&1; then | ||
| echo "::warning::could not install rpmrebuild; dropping unpatched rpm(s) to avoid shipping the GH#563 crash" | ||
| rm -f "${rpms[@]}" | ||
| exit 0 | ||
| fi | ||
|
|
||
| WRAPPER="$(pwd)/packaging/linux/github-store-launcher.sh" | ||
| OUTDIR="$(mktemp -d)" | ||
| for rpm in "${rpms[@]}"; do | ||
| echo "Patching: $rpm" | ||
| # Overwrite the native launcher in-place with the JVM-direct wrapper. | ||
| # Keeps the %files manifest path intact, swaps ELF -> script. | ||
| if rpmrebuild --change-files="cp '$WRAPPER' ./opt/github-store/bin/GitHub-Store; chmod 0755 ./opt/github-store/bin/GitHub-Store" \ | ||
| -p -n -d "$OUTDIR" "$rpm"; then | ||
| rebuilt="$(find "$OUTDIR" -name '*.rpm' -newer "$WRAPPER" | head -n1 || true)" | ||
| if [ -n "$rebuilt" ]; then | ||
| mv -f "$rebuilt" "$rpm" |
There was a problem hiding this comment.
Silent
mv failure can ship the broken ELF rpm
The step opts out of set -e (only set -uo pipefail) so that the cleanup loop can finish after partial failures. Every explicit error branch deletes the rpm to avoid shipping the crash-inducing binary. But if mv -f "$rebuilt" "$rpm" on line 428 fails (e.g., ENOSPC or unexpected I/O error), the failure is swallowed, the original ELF at $rpm is left intact, and it gets uploaded via the artifact step — shipping the glibc 2.42 crash to rpm users instead of fixing it.
Adding || { echo "::warning::..."; rm -f "$rpm"; } after the mv closes this gap without changing the overall fault-tolerant design.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/build-desktop-platforms.yml (1)
424-437:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd an RPM post-rebuild launcher content check (and confirm the exact launcher path/name).
jpackage’s default RPM layout installs launchers under
/opt/<name>/bin/<launcher>, so the hardcoded./opt/github-store/bin/GitHub-Storetarget is plausibly correct. However, unlike the deb step, the RPM flow has no post-swap validation—if the in-place swap doesn’t actually replace the launcher content, an ELF launcher could still be shipped undetected. Add a guard after themvthat inspects./opt/github-store/bin/GitHub-Storeinside the rebuilt RPM and drops the RPM if it still starts with the ELF magic (0x7fELF); also confirm the launcher path/name from the actualgenerate-installersRPM output.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/build-desktop-platforms.yml around lines 424 - 437, After successfully moving the rebuilt RPM into place (the mv that sets "$rpm" from rebuilt), add a verification step that inspects the installed launcher file path used in the rebuild (the target "./opt/github-store/bin/GitHub-Store" derived from "$WRAPPER") inside the rebuilt package and rejects the RPM if the file still begins with the ELF magic bytes (0x7f 'E' 'L' 'F'); use the existing variables rpm, rebuilt, OUTDIR and the rpmrebuild flow to open/check the rebuilt package content and rm -f "$rpm" on failure, and also confirm the exact launcher path/name by cross-checking the actual generate-installers RPM output to ensure the checked path matches the produced package layout.
🧹 Nitpick comments (1)
.github/workflows/build-desktop-platforms.yml (1)
412-437: Dropped RPMs won't be caught by the release completeness guard.The drop-and-warn here correctly keeps the GH#563 crash out of the upload, as agreed. But note the downstream effect: the
releasejob's completeness guard counts deb and rpm together (linux_modern_count/linux_debian12_countat lines 665-668 and 706-707). When the rpm is dropped here but the deb survives, that group count stays>= 1, so the guard passes and a draft release can ship with no RPM at all — the only signal is this::warning::, which is easy to miss in a long run log.Consider surfacing the drop more visibly so a missing RPM doesn't slip into a release silently — e.g. emit it to
$GITHUB_STEP_SUMMARY, or track rpm presence separately from deb in the completeness guard.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/build-desktop-platforms.yml around lines 412 - 437, The workflow currently drops RPMs during the rpmrebuild failure paths (the loop over the rpms array and the rpmrebuild invocation) but does not update the release completeness guard counters, so a release can proceed with only DEBs; when you remove an RPM (the rm -f "$rpm" branches), also record that fact in a machine-readable/visible place: append a clear entry to $GITHUB_STEP_SUMMARY (or set a workflow output like missing_rpms or increment a missing_rpm counter) so the release job can detect missing RPMs separately from DEBs, and update the release completeness guard logic (the linux_modern_count / linux_debian12_count check) to consider that missing_rpms output/counter before allowing a draft release to be created.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/build-desktop-platforms.yml:
- Around line 424-437: After successfully moving the rebuilt RPM into place (the
mv that sets "$rpm" from rebuilt), add a verification step that inspects the
installed launcher file path used in the rebuild (the target
"./opt/github-store/bin/GitHub-Store" derived from "$WRAPPER") inside the
rebuilt package and rejects the RPM if the file still begins with the ELF magic
bytes (0x7f 'E' 'L' 'F'); use the existing variables rpm, rebuilt, OUTDIR and
the rpmrebuild flow to open/check the rebuilt package content and rm -f "$rpm"
on failure, and also confirm the exact launcher path/name by cross-checking the
actual generate-installers RPM output to ensure the checked path matches the
produced package layout.
---
Nitpick comments:
In @.github/workflows/build-desktop-platforms.yml:
- Around line 412-437: The workflow currently drops RPMs during the rpmrebuild
failure paths (the loop over the rpms array and the rpmrebuild invocation) but
does not update the release completeness guard counters, so a release can
proceed with only DEBs; when you remove an RPM (the rm -f "$rpm" branches), also
record that fact in a machine-readable/visible place: append a clear entry to
$GITHUB_STEP_SUMMARY (or set a workflow output like missing_rpms or increment a
missing_rpm counter) so the release job can detect missing RPMs separately from
DEBs, and update the release completeness guard logic (the linux_modern_count /
linux_debian12_count check) to consider that missing_rpms output/counter before
allowing a draft release to be created.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e43cd183-426a-48ee-83ed-589dba6d58ef
📒 Files selected for processing (15)
.github/workflows/build-desktop-platforms.ymlcore/presentation/src/commonMain/composeResources/files/whatsnew/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ar/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/bn/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/es/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/fr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/hi/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/it/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ja/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ko/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/pl/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ru/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/tr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/19.jsonpackaging/linux/github-store-launcher.sh
✅ Files skipped from review due to trivial changes (12)
- core/presentation/src/commonMain/composeResources/files/whatsnew/ko/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/it/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/bn/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/ar/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/es/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/pl/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/ru/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/hi/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/tr/19.json
- core/presentation/src/commonMain/composeResources/files/whatsnew/ja/19.json
🚧 Files skipped from review as they are similar to previous changes (2)
- packaging/linux/github-store-launcher.sh
- core/presentation/src/commonMain/composeResources/files/whatsnew/fr/19.json
Closes #563.
Root cause
The jpackage-generated native launcher (
bin/GitHub-Store) segfaults inside glibcsetenv()before the JVM is even mapped, on glibc >= 2.42 (Fedora 43, Ubuntu 26.04, Debian 13, Bazzite). The reporter's coredump pins it precisely:It is independent of environment contents (the reporter's
envdiff between clean/poisoned states was empty, and a minimalenv -ilaunch still crashed) and independent of distro / DE (reproduced on Fedora, Ubuntu, Debian, KDE, GNOME). The poison is in the bundled launcher'ssetenvpath against glibc 2.42's reworkedenvironhandling — nothing we can fix inside the launcher binary, which is generated by jpackage.Fix
Sidestep the broken native launcher entirely and start the bundled JVM directly — exactly what the Flatpak build already does in production (
java -jar), which is why Flatpak was never affected.New committed launcher
packaging/linux/github-store-launcher.sh:lib/runtime/bin/javarelative to its own path.lib/app/*.cfgfor classpath, main class, and JVM options (expanding$APPDIR/$ROOTDIR/$BINDIRtokens).execs the JVM directly. Works identically inside an AppImage mount,/opt/github-store(deb / rpm / Arch), or the raw Compose app-image dir.Wired into all four Linux artifacts in CI:
AppRunnow execs the wrapper instead of the native launcher.usr/binsymlink +.desktopExec repointed to the wrapper..desktopExec path unchanged, transparently runs the wrapper).rpmrebuildoverwrites the launcher in place; non-fatal if rpmrebuild is unavailable so the rest of the Linux build still ships.Validation
Simulated a jpackage layout locally (fake
.cfgwith multi-line classpath,$APPDIRtokens, multiplejava-options, plus a stubjava) and confirmed the wrapper resolves classpath + main class + options + passes through user args (deep links) correctly.bash -nclean, workflow YAML validated.Needs a
generate-installersCI run to validate the actual deb/rpm/AppImage/Arch repack against real jpackage output before merge — I can't produce signed Linux artifacts locally. cc reporter @nitinkmr333 for a confirmation build.What's-new bullet added to
19.json+ all 12 locales.Summary by CodeRabbit