Documentation
¶
Overview ¶
Package safeexec provides drop-in replacements for selected os/exec helpers whose implementation in Go's standard library uses the faccessat2(2) Linux syscall (number 439, introduced in kernel 5.8).
Android's seccomp filter on devices that still ship a 4.x kernel (a common combination — Android 13 userland on top of a 4.14 kernel, for example) rejects faccessat2 with SIGSYS instead of the ENOSYS that Go's runtime expects to trigger its faccessat fallback. SIGSYS kills the process, so Go's fallback never fires.
The crash signature in the wild looks like:
SIGSYS: bad system call internal/syscall/unix.faccessat2(...) internal/syscall/unix.Eaccess(...) os/exec.findExecutable(...) os/exec.LookPath(...)
Use LookPath in this package instead of os/exec.LookPath anywhere the result will run on Termux/Android phones. The implementation here checks executability using os.Stat + the file's mode bits, which only goes through the fstatat(2) syscall family that Android seccomp does allow.
The .golangci.yml in this repo bans direct calls to exec.LookPath via forbidigo. If you have a genuine reason to use the stdlib version (e.g. you need euid-based AT_EACCESS semantics in a setuid context), add a //nolint:forbidigo comment with a justification.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Command ¶ added in v0.14.16
Command wraps exec.Command but pre-resolves a bare executable name via LookPath so the returned *exec.Cmd carries an absolute path. This prevents Go's internal LookPath (which calls faccessat2(2), rejected by Android/Termux seccomp on kernel < 5.8 with SIGSYS) from firing when the caller later invokes cmd.Start.
Names already containing a path separator (absolute or workspace-relative) bypass LookPath inside exec.Command anyway, so they are passed through unchanged. Resolution failures are swallowed: the returned Cmd keeps the original name, and the error surfaces at cmd.Start / cmd.Run — matching upstream semantics.
func CommandContext ¶ added in v0.14.16
CommandContext is the context-aware sibling of Command. Same pre-resolution semantics.
func LookPath ¶
LookPath searches PATH for an executable named file, mirroring os/exec.LookPath's semantics but without the faccessat2 syscall. If file contains a slash it is checked directly. On Windows, bare names without an extension are resolved against PATHEXT so "cmd" finds "cmd.exe". Returns an *exec.Error so callers can use errors.Is(err, exec.ErrNotFound).
func ResolveBin ¶
ResolveBin returns the absolute path to name. If name already contains a slash it is returned unchanged (caller is assumed to have given an absolute or workspace-relative path); otherwise the name is resolved via LookPath. Use this before passing name to exec.CommandContext to prevent Go's internal LookPath from firing during exec.Command construction — that path triggers faccessat2, which Android's seccomp on kernel < 5.8 rejects with SIGSYS.
Types ¶
This section is empty.