Skip to content

v3.4.0 — privileged operations move into a native helper

Latest

Choose a tag to compare

@github-actions github-actions released this 14 Aug 16:18
· 1 commit to main since this release

The short version

Everything privileged that rEFInd_GUI does — installing your config, installing themes, and both randomizers — used to be shell and PowerShell scripts that ran as root/admin. In 3.4.0 all of it is compiled code in a small helper binary shipped alongside the app. Nothing about how you use the GUI changes; what changes is that the fragile machinery underneath it is gone.

Upgrading is automatic. On Windows the installer re-points any scheduled tasks you had enabled at the new helper; on Linux, re-run the installer.

Why this was worth doing

The old design forced three awkward things to exist:

A tamper hash-check that punished bugfixes. The GUI refused to run /etc/rEFInd/install_config_from_GUI.sh unless it SHA-256-matched a copy baked into the binary. That was there for good reasons, but it meant any fix to a privileged script — even a one-line one — blocked Install Config for every existing user as "modified" until they reinstalled. It is now a version handshake (helper --version vs the app's version), which catches the same version-skew problem without making privileged code effectively unpatchable. The security boundary never was the hash — it is root ownership of the helper, and that is unchanged.

Two implementations of the same logic, kept in sync by hand. Finding the ESP that rEFInd actually boots from existed once in bash and once in PowerShell, and they had to be kept behaviorally identical by review alone. They are now one C++ implementation used by both platforms, with a single EFI_LOAD_OPTION parser. That retires an entire class of bug — including the "efibootmgr ≥ 18 appends a tab after the label, so $-anchored regexes silently never match" trap that bit this project twice.

Elevated code that could not be tested. Shell scripts running as root are hard to unit test, so they never were. The compiled logic is: cmake -DBUILD_GUI_TESTS=ON builds six suites covering the load-option parser, the staged-publish planner, theme installation, and the randomizers. Four of them run on Windows too. They immediately earned their place — see the bugs section below.

What's new

  • rEFInd_GUI_helper — a QtCore-only console binary. On Linux it is installed root-owned in /etc/rEFInd/ and the sudoers rules whitelist two exact argument vectors (install-config, install-themes) rather than a script path; the systemd randomizer units run its subcommands. On Windows the GUI is already elevated, so it calls the same code in-process, and the helper exists for the scheduled tasks to run.
  • Scheduled tasks are registered natively through the Task Scheduler COM API instead of a PowerShell wrapper, preserving the handheld-critical settings (start-on-battery allowed, don't stop when going on battery, run with highest privileges, 5-minute limit).
  • Upgrade migration. helper migrate-tasks runs during install and re-points any randomizer task you already had enabled at the new helper — including converting the pre-3.x task name. It never creates a task you had not enabled. As a side effect it repairs tasks that were missing the battery settings, which on a handheld meant they rarely ran at all.
  • Install Config is passwordless-only on Linux. The zenity password fallback is gone; if the sudoers rule or helper is missing you get a clear "reinstall to repair" message instead of a password prompt.
  • The privilege hygiene got stricter, not looser. Root still never opens anything under your home directory: the helper forks, drops to the invoking user (setgroupssetgidsetuid), and streams the files from the unprivileged child. That replaces the old runuser/tar pipes while keeping their guarantees — symlinks refused, no .. or absolute paths, size caps enforced in-stream rather than through a pipe.

Bugs found and fixed along the way

Porting to compiled code surfaced real defects that the scripts had been hiding, most caught by the new tests:

  • Staged publishes silently failed on Windows. std::rename on the Windows CRT refuses to overwrite an existing file, so every publish over a live refind.conf returned "staging failed" instead of installing. Now uses MoveFileExW with replace semantics.
  • Windows cannot rename a file with an open handle, and QTemporaryFile keeps its native handle open even after close() — the config backup and both randomizers had to release it before publishing.
  • The Task Scheduler registration would not link on MinGW, because the COM CLSIDs live in libtaskschd, not libuuid — and it passed a BSTR where the API takes a VARIANT.
  • A Linux mount-point lookup read nothing at all, because it guarded its loop with atEnd() and procfs reports size 0. It now has a regression test.
  • Install results named a temporary path. On a letterless ESP the resolver mounts the partition on a private temp directory, and that path leaked into the success dialog — so a correct install read as "it installed into Temp". It now names the ESP.

Verified on

Linux: build + all test suites, both sudo-gated installs landing on the ESP the firmware's rEFInd entry actually points at, both randomizers running clean at boot (CachyOS desktop).

Windows: build + tests, Install Config and Install Themes publishing to the real ESP with the staged set byte-identical and no residue, native task registration matching the old wrapper's settings exactly and surviving re-registration and removal, bootnext writing the correct NVRAM entry, and the theme randomizer rotating themes on a live ESP.

Notes

  • The Windows installer removes the superseded .ps1 files from an existing installation, so nothing stale is left behind in Program Files.
  • Editing privileged logic no longer requires a re-released binary to keep working — but the app and the helper are version-matched, so they must be installed together. Re-running the installer is what does that.