https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297426

--- Comment #8 from Devin Teske <[email protected]> ---
(In reply to Alex S from comment #7)

> I don't normally mount procfs at /proc, but there is no impact either
> way.

I repeated the exact Python test from your comment 3 while changing only the
native procfs mount state. On this 16.0-CURRENT system, mount state
directly changes the result.

Test environment
----------------

  kern.osreldate:          1600019
  linux_base-rl9:          9.8
  compat.linux.emul_path:  /compat/linux

  unmodified source:       34718e01869b
  D58780 source:           bec989b72aae

The linprocfs module was rebuilt using the already-attached
linux_rebuild2.sh and installed/reloaded using linux_reload2.sh.

Exact Python reproduction
-------------------------

With no native procfs mounted:

  $ mount | grep proc
  linprocfs on /compat/linux/proc (linprocfs, local)

  $ /compat/linux/usr/bin/python3.9 -c "
  import os
  print('readlink:', os.readlink('/proc/self/exe'))
  try:
      print('stat:', os.stat('/proc/self/exe'))
  except OSError as e:
      print('stat: FAIL --', e)
  try:
      os.execv('/proc/self/exe',
          ['python3', '-c', 'print(\"execv OK\")'])
  except OSError as e:
      print('execv: FAIL --', e)
  "

  readlink: /compat/linux/usr/bin/python3.9
  stat: FAIL -- [Errno 2] No such file or directory: '/proc/self/exe'
  execv: FAIL -- [Errno 2] No such file or directory

After mounting native procfs, with no other change:

  $ sudo mount -t procfs proc /proc
  $ mount | grep proc
  linprocfs on /compat/linux/proc (linprocfs, local)
  procfs on /proc (procfs, local)

Running the same Python command then produces:

  readlink: /compat/linux/usr/bin/python3.9
  stat: OK
  execv OK

Thus native procfs mount state does affect the outcome: stat/execve
change from ENOENT to success. The successful case still exposes the
FreeBSD host path through readlink rather than the Linux-visible path.

C litmus
--------

The attached linux-proc-self-exe-litmus.c tests readlink, stat,
access(X_OK), and execve independently.

Install the Linux development tools and compile it as a Linux binary:

  $ sudo pkg install linux-rl9-devtools

  $ /compat/linux/usr/bin/gcc -O2 \
      -o ~/linux-proc-self-exe-litmus \
      ~/linux-proc-self-exe-litmus.c

Run it first with its executable text outside the emulator root:

  $ ~/linux-proc-self-exe-litmus

Create a Linux-side temporary directory under the emulator root. This
directory is not created by default:

  $ sudo mkdir -p /compat/linux/tmp
  $ sudo chmod 1777 /compat/linux/tmp

Copy the same binary beneath compat.linux.emul_path and run it using its
FreeBSD host path:

  $ cp ~/linux-proc-self-exe-litmus /compat/linux/tmp/

  $ /compat/linux/tmp/linux-proc-self-exe-litmus

The program exits nonzero on failure and prints the result of readlink,
stat, access(X_OK), and execve independently.

Complete result matrix
----------------------

The complete observed matrix is:

  executable    linprocfs    native /proc   readlink result       verdict
  ------------  -----------  -------------  --------------------  -------
  outside-emul  unmodified   absent         host path             FAIL
  outside-emul  unmodified   mounted        host path             PASS
  under-emul    unmodified   absent         /compat/linux/...     FAIL
  under-emul    unmodified   mounted        /compat/linux/...     FAIL (*)
  under-emul    D58780       absent         Linux-visible path    PASS
  under-emul    D58780       mounted        Linux-visible path    PASS

(*) With native procfs mounted, stat/access/execve succeed through
native-namespace fallback, but readlink still returns the host path
including /compat/linux. The litmus therefore correctly reports failure:
The Linuxulator should not expose FreeBSD host emulator configuration through
/proc/self/exe. To emulate native Linux accurately, it should report the path
in the process’s own namespace, improving compatibility with Linux binaries.
Returning a host-only emulator prefix through an ABI-visible pathname is a
Linuxulator implementation leak with no analogue in native Linux and is highly
irregular for a Linux compatibility layer.

Representative output
---------------------

Unmodified linprocfs, executable outside emul_path, no native procfs:

  case: outside-emul
  readlink(/proc/self/exe) =
      /home/dteske/linux-proc-self-exe-litmus
  readlink: OK
  stat(/proc/self/exe): FAIL (No such file or directory)
  access(X_OK): FAIL (No such file or directory)
  execve(/proc/self/exe): FAIL
  VERDICT: FAIL (outside-emul)

Unmodified linprocfs, executable outside emul_path, native procfs
mounted:

  case: outside-emul
  readlink(/proc/self/exe) =
      /home/dteske/linux-proc-self-exe-litmus
  readlink: OK
  stat(/proc/self/exe): OK
  access(X_OK): OK
  execve(/proc/self/exe): OK
  VERDICT: PASS (outside-emul)

Unmodified linprocfs, executable under emul_path, no native procfs:

  case: under-emul
  readlink(/proc/self/exe) =
      /compat/linux/tmp/linux-proc-self-exe-litmus
  readlink: FAIL (host path; expected the Linux namespace path)
  stat(/proc/self/exe): FAIL (No such file or directory)
  access(X_OK): FAIL (No such file or directory)
  execve(/proc/self/exe): FAIL
  VERDICT: FAIL (under-emul)

Unmodified linprocfs, executable under emul_path, native procfs mounted:

  case: under-emul
  readlink(/proc/self/exe) =
      /compat/linux/tmp/linux-proc-self-exe-litmus
  readlink: FAIL (host path; expected the Linux namespace path)
  stat(/proc/self/exe): OK
  access(X_OK): OK
  execve(/proc/self/exe): OK
  VERDICT: FAIL (under-emul)

D58780, executable under emul_path, no native procfs:

  case: under-emul
  readlink(/proc/self/exe) =
      /tmp/linux-proc-self-exe-litmus
  readlink: OK
  stat(/proc/self/exe): OK
  access(X_OK): OK
  execve(/proc/self/exe): OK
  VERDICT: PASS (under-emul)

D58780, executable under emul_path, native procfs mounted:

  case: under-emul
  readlink(/proc/self/exe) =
      /tmp/linux-proc-self-exe-litmus
  readlink: OK
  stat(/proc/self/exe): OK
  access(X_OK): OK
  execve(/proc/self/exe): OK
  VERDICT: PASS (under-emul)

Concrete consumer
-----------------

This is not only a path-format discrepancy. www/linux-brave provides a
concrete consumer of the failing follow operation.

Brave is installed outside compat.linux.emul_path, under
/usr/local/share/brave. Chromium starts helper processes by executing
/proc/self/exe. Without procfs mounted at native /proc, that operation
fails with ENOENT:

  LaunchProcess: failed to execvp:
  /proc/self/exe

The GPU helper consequently exits and Brave cannot launch successfully.
D58784 makes this requirement explicit by refusing to start
www/linux-brave without native procfs and printing the required mount
instructions.

The two fixes address complementary cases:

  * Linux binaries outside compat.linux.emul_path, such as
    www/linux-brave, require procfs mounted at native /proc.

  * Linux binaries under compat.linux.emul_path should receive a
    Linux-visible /proc/self/exe target. D58780 supplies that behavior by
    stripping the emulator prefix.

D58780 therefore does not attempt to replace native procfs for
outside-emul binaries. It fixes the distinct under-emul case where
linprocfs itself emits a target that is not valid in the process's Linux
namespace.

Mechanism and scope
-------------------

proc_get_binpath() returns a FreeBSD host path. For executable text under
the emulator root, unmodified linprocfs therefore reports a target such as:

  /compat/linux/usr/bin/python3.9

The Linux ABI first resolves that absolute path relative to
compat.linux.emul_path, producing an initial lookup equivalent to:

  /compat/linux/compat/linux/usr/bin/python3.9

Without native procfs, following /proc/self/exe consequently fails with
ENOENT. Native procfs can mask the follow operation through
native-namespace fallback, but it cannot correct the readlink result.

D58780 strips compat.linux.emul_path when the executable is beneath that
root. The symlink then reports the Linux-visible path:

  /usr/bin/python3.9

That fixes readlink and makes stat/access/execve work regardless of
whether native procfs is mounted.

D58780 intentionally does not alter paths for executables outside the
emulator root. Those require procfs mounted at native /proc. Making
arbitrary FreeBSD host paths directly meaningful inside the Linux
namespace is a separate and substantially broader problem and is not
part of D58780.

I am attaching the complete six output files underlying this matrix. If
another system produces different results, please provide both
`mount | grep proc` and the complete litmus output so the differing
lookup path can be identified. Also, please provide for test env:

1. uname -K # for kernel version
2. pkg info linux_base-rl9 # for Linuxulator version

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to