Skip to content

link2symlink: accept O_TMPFILE anonymous inodes in linkat copy-path gate#349

Open
Ebola-Chan-bot wants to merge 1 commit into
termux:masterfrom
Ebola-Chan-bot:l2s-accept-o-tmpfile-linkat
Open

link2symlink: accept O_TMPFILE anonymous inodes in linkat copy-path gate#349
Ebola-Chan-bot wants to merge 1 commit into
termux:masterfrom
Ebola-Chan-bot:l2s-accept-o-tmpfile-linkat

Conversation

@Ebola-Chan-bot
Copy link
Copy Markdown

@Ebola-Chan-bot Ebola-Chan-bot commented Apr 19, 2026

Extend handle_linkat_from_proc_fd() so that, in addition to the classic " (deleted)" orphan-inode case (open() + unlink() followed by linkat(/proc/self/fd/N, AT_SYMLINK_FOLLOW, newpath)), a readlink target of the O_TMPFILE magic form "/<dir>/#" is also recognised and routed through the existing user-space copy branch.

The copy body (stat + open(O_CREAT|O_EXCL)+ read/write loop) is byte- identical for both source types and needs no change; only the gate is relaxed, by parsing for the "/<dir>/#" trailer as an OR with the existing " (deleted)" suffix check.

Motivation: Alpine apk's atomic-publish pattern uses

  fd = open(dir, O_TMPFILE | O_RDWR, mode);
  write(fd, ...);
  linkat(AT_FDCWD, "/proc/self/fd/<fd>", AT_FDCWD, newpath,
         AT_SYMLINK_FOLLOW);

On kernels where link2symlink is already required, that flow produces a /proc/<pid>/fd/<fd> magic symlink whose readlink resolves to "/<dir>/#<inum>" rather than "<path> (deleted)", so the original gate returned 0 and let the unsupported hardlink syscall reach the kernel, causing apk to fail its triggers/scripts/installed-db writes.

Known semantic limitation (documented in a code comment): writes to the source fd performed after the linkat call are not reflected in the target file, because the copy snapshots the data at linkat time rather than hijacking the fd. All surveyed consumers of O_TMPFILE+linkat (apk, dpkg, ld-linux) follow a write-fully -> publish -> stop-writing ordering and are unaffected; a future program relying on post-publish writes would need an fd-hijack approach instead.

Extend handle_linkat_from_proc_fd() so that, in addition to the classic
" (deleted)" orphan-inode case (open() + unlink() followed by
linkat(/proc/self/fd/N, AT_SYMLINK_FOLLOW, newpath)), a readlink target
of the O_TMPFILE magic form "/<dir>/#<inode-number>" is also recognised
and routed through the existing user-space copy branch.

The copy body (stat + open(O_CREAT|O_EXCL) + read/write loop) is byte-
identical for both source types and needs no change; only the gate is
relaxed, by parsing for the "/<dir>/#<digits>" trailer as an OR with
the existing " (deleted)" suffix check.

Motivation: Alpine apk's atomic-publish pattern uses
  fd = open(dir, O_TMPFILE | O_RDWR, mode);
  write(fd, ...);
  linkat(AT_FDCWD, "/proc/self/fd/<fd>", AT_FDCWD, newpath,
         AT_SYMLINK_FOLLOW);
On kernels where link2symlink is already required, that flow produces a
/proc/<pid>/fd/<fd> magic symlink whose readlink resolves to
"/<dir>/#<inum>" rather than "<path> (deleted)", so the original gate
returned 0 and let the unsupported hardlink syscall reach the kernel,
causing apk to fail its triggers/scripts/installed-db writes.

Known semantic limitation (documented in a code comment): writes to the
source fd performed after the linkat call are not reflected in the
target file, because the copy snapshots the data at linkat time rather
than hijacking the fd.  All surveyed consumers of O_TMPFILE+linkat
(apk, dpkg, ld-linux) follow a write-fully -> publish -> stop-writing
ordering and are unaffected; a future program relying on post-publish
writes would need an fd-hijack approach instead.

Tested on HarmonyOS NEXT / kernel 5.10.43 aarch64 inside an
aoco_untrusted_app SELinux domain that denies app_data_file:file link,
by running `apk add --no-cache` for a representative package set
(alpine-base, bash, busybox-extras, ...): pre-patch apk fails with
EPERM while writing triggers/scripts.tar.gz/installed; post-patch the
install completes and the resulting files are byte-identical to the
source O_TMPFILE content.
Copilot AI review requested due to automatic review settings April 19, 2026 14:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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.

2 participants