Skip to content

Use sysctl KERN_PROC_PATHNAME for the executable path on the BSDs - #231

Open
hjmjohnson wants to merge 1 commit into
malaterre:masterfrom
BRAINSia:fix/bsd-exe-path
Open

hjmjohnson wants to merge 1 commit into
malaterre:masterfrom
BRAINSia:fix/bsd-exe-path

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

GetCurrentProcessFileName() reads /proc/curproc/file on FreeBSD, DragonFly, and OpenBSD. procfs is not mounted by default on modern FreeBSD, and OpenBSD has no procfs at all, so on a default install the readlink fails and the function returns nullptr. This asks the kernel through sysctl instead, keeping the procfs read as a fallback.

The BSD branch has been compiled against each BSD's real headers but not executed on a BSD host. A BSD reviewer confirming the returned path would close that gap.

The change

FreeBSD and DragonFly expose the executable path as CTL_KERN.KERN_PROC.KERN_PROC_PATHNAME; NetBSD spells it CTL_KERN.KERN_PROC_ARGS.KERN_PROC_PATHNAME:

#if defined(KERN_PROC_PATHNAME)
#if defined(__NetBSD__)
  int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
#else
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
#endif
  size_t pathlen = sizeof(path);
  if ( sysctl(mib, sizeof(mib) / sizeof(mib[0]), path, &pathlen, nullptr, 0) == 0 && pathlen > 1 )
    {
    return path;
    }
#endif
  • The procfs readlink stays as a fallback, so a system that mounts procfs behaves as it does today.
  • The inner guard is KERN_PROC_PATHNAME itself, a macro in <sys/sysctl.h>. The outer platform list remains, because <sys/sysctl.h> is only included on the BSDs.
  • OpenBSD has no KERN_PROC_PATHNAME (confirmed in its 7.9 headers), so it keeps the fallback and is no worse off than before.

The commented-out NetBSD block is dropped now that NetBSD is handled.

readlink termination: hardening, not a live bug

readlink does not NUL-terminate and returns the buffer size when it truncates. Both call sites now pass sizeof(path) - 1 and terminate explicitly. The buffers are static, hence zero-initialized, so today this only matters for a link target at or beyond PATH_MAX.

Verification
Platform Coverage
FreeBSD 15.1 headers and libc++ (clang 21 cross) full gdcmSystem.cxx compiles with 0 warnings at -Wall -Wextra; the sysctl path is selected (KERN_PROC_PATHNAME = 12)
NetBSD 11.0 headers the BSD block compiles clean at -Wall -Wextra -Werror; KERN_PROC_ARGS = 48, KERN_PROC_PATHNAME = 5
OpenBSD 7.9 headers compiles clean at -Werror; no KERN_PROC_PATHNAME, fallback only
glibc x86_64 (Ubuntu 24.04), musl (Alpine), i386 Debian built and tested; failure set identical to master with gdcmData and gdcmDataExtra
macOS arm64 built and tested; failure set identical to master
FreeBSD / DragonFly / NetBSD / OpenBSD runtime not executed

The NetBSD and OpenBSD sets carry no C++ standard library headers, so for those two the BSD block was extracted verbatim from gdcmSystem.cxx into a translation unit that needs only C headers.

Independent of #228 and #230. Touches the same file as #229 but a different function; the two merge cleanly.

GetCurrentProcessFileName() read /proc/curproc/file on FreeBSD, DragonFly
and OpenBSD. procfs is not mounted by default on modern FreeBSD, and
OpenBSD has no equivalent at all, so the readlink fails and the function
returns nullptr on a default install.

Ask the kernel instead. FreeBSD and DragonFly expose the path as
CTL_KERN.KERN_PROC.KERN_PROC_PATHNAME; NetBSD spells the same thing
CTL_KERN.KERN_PROC_ARGS.KERN_PROC_PATHNAME. The procfs readlink is kept as
a fallback for systems that do mount it, so nothing that works today
regresses. The block is guarded on KERN_PROC_PATHNAME rather than on a
platform name. It is a real macro in <sys/sysctl.h>, so the preprocessor
can test for it directly. OpenBSD, which defines neither the macro nor an
equivalent, keeps the fallback and is no worse off than before.

Also terminate the readlink results. readlink does not NUL-terminate, and
returns the buffer size when it truncates. The buffers here are static and
therefore zero-initialized, so in practice the strings are terminated and
this is hardening rather than a live bug, but the truncation case would
leave the buffer unterminated, and passing sizeof(path) - 1 with an
explicit terminator removes the question. The commented-out NetBSD block is
dropped now that NetBSD is handled.

Verified: the BSD block compiles clean at -Wall -Wextra -Werror against
the FreeBSD 15.1, NetBSD 11.0, and OpenBSD 7.9 headers (clang 21 cross),
and the whole of gdcmSystem.cxx compiles against FreeBSD's headers and
libc++. FreeBSD selects the sysctl path and OpenBSD the fallback. It has
not been executed on a BSD. glibc, musl, i386, and macOS builds show the
same test failures as master.

Assisted-by: Claude Code: cross-platform verification and patch drafting
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.

1 participant