Add OFD lock support to fcntl and fix l_pid on conflict#183
Conversation
|
1. Executed python test script
Command: Output: 2. Executed native host script Output Log: |
9ed374f to
1180fa2
Compare
|
Confirmed — verified against fs/locks.c (fcntl_getlk/fcntl_setlk both reject a nonzero request l_pid with EINVAL for F_OFD_GETLK/SETLK/SETLKW). Fixed in 1180fa2: fcntl_flock_op now reads the guest's l_pid and returns -EINVAL when is_ofd and l_pid != 0, with 3 new regression cases in test-ofd-lock.c. |
| static int64_t fcntl_flock_op(guest_t *g, | ||
| host_fd_ref_t *host_ref, | ||
| uint64_t arg, | ||
| int mac_cmd, | ||
| bool is_getlk, | ||
| bool is_ofd) | ||
| { | ||
| uint8_t lflock[32]; /* Linux struct flock is 32 bytes on aarch64 */ | ||
| if (guest_read_small(g, arg, lflock, sizeof(lflock)) < 0) | ||
| return -LINUX_EFAULT; | ||
|
|
||
| int16_t l_type, l_whence; | ||
| int64_t l_start, l_len; | ||
| int32_t l_pid; | ||
| memcpy(&l_type, lflock + 0, 2); | ||
| memcpy(&l_whence, lflock + 2, 2); | ||
| memcpy(&l_start, lflock + 8, 8); /* offset 8 due to padding */ | ||
| memcpy(&l_len, lflock + 16, 8); | ||
| memcpy(&l_pid, lflock + 24, 4); | ||
|
|
||
| /* Linux rejects F_OFD_GETLK/SETLK/SETLKW requests with a nonzero l_pid: | ||
| * OFD locks are owned by the open file description, not a process, so | ||
| * the field is reserved on input (fs/locks.c fcntl_getlk/fcntl_setlk | ||
| * both `return -EINVAL` on a nonzero request l_pid for these commands). | ||
| */ | ||
| if (is_ofd && l_pid != 0) | ||
| return -LINUX_EINVAL; | ||
|
|
||
| /* l_type constants differ between Linux and macOS/BSD: | ||
| * Linux: F_RDLCK=0, F_WRLCK=1, F_UNLCK=2 | ||
| * macOS: F_RDLCK=1, F_UNLCK=2, F_WRLCK=3 | ||
| * Passing the Linux value straight through makes a Linux F_RDLCK (0) an | ||
| * invalid type on macOS, which fcntl() rejects with EINVAL. This is the | ||
| * lock POSIX databases (e.g. SQLite) take first, so it must map. | ||
| */ | ||
| short mac_type; | ||
| switch (l_type) { | ||
| case 0: /* LINUX_F_RDLCK */ | ||
| mac_type = F_RDLCK; | ||
| break; | ||
| case 1: /* LINUX_F_WRLCK */ |
There was a problem hiding this comment.
Traditional F_GETLK Leaks Host PID to the Guest**
-
Location:
fcntl_flock_opinsidesrc/syscall/fs.c -
Problem:
For OFD locks (F_OFD_GETLK), the implementation correctly overrides the conflict PID with-1(per Linux specification) to avoid leaking host PIDs.However, for traditional locks (
F_GETLK), it reports:rp = mac_fl.l_pid;
This passes the macOS host process ID directly to the guest. Because
elfusetranslates and virtualizes guest PIDs, the guest program (e.g., SQLite or other locking monitors) receives a raw host PID (e.g.,40548) instead of the child's guest PID (e.g.,2).If the guest tries to manage or check that PID (for example, via
kill(conflict_pid, 0)), the PID translation gate insc_killlooks for the host PID in the guest table, fails to resolve it, and returnsESRCH, even though the process is alive. -
Proposed Fix:
Add aproc_host_to_guest_pidhelper to map the host PID back to its corresponding guest PID via the child process table and cross-process registry.
1180fa2 to
da96047
Compare
|
Good catch — fixed in da96047. Added |
da96047 to
915da16
Compare
There was a problem hiding this comment.
1 issue found across 6 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/syscall/proc.c">
<violation number="1" location="src/syscall/proc.c:849">
P2: A first lock-conflict PID lookup in a recycled namespace-owner process can read a stale process registry and return the wrong guest PID. This new registry reader should mirror the existing registry readers by calling `proc_registry_reset_if_owner(path)` before opening the file.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| char path[PATH_MAX]; | ||
| if (!process_registry_path(path, sizeof(path))) | ||
| return -1; | ||
| int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); |
There was a problem hiding this comment.
P2: A first lock-conflict PID lookup in a recycled namespace-owner process can read a stale process registry and return the wrong guest PID. This new registry reader should mirror the existing registry readers by calling proc_registry_reset_if_owner(path) before opening the file.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/syscall/proc.c, line 849:
<comment>A first lock-conflict PID lookup in a recycled namespace-owner process can read a stale process registry and return the wrong guest PID. This new registry reader should mirror the existing registry readers by calling `proc_registry_reset_if_owner(path)` before opening the file.</comment>
<file context>
@@ -805,6 +834,46 @@ int proc_get_namespace_targets(proc_signal_target_t *out,
+ char path[PATH_MAX];
+ if (!process_registry_path(path, sizeof(path)))
+ return -1;
+ int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd < 0)
+ return -1;
</file context>
| int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); | |
| proc_registry_reset_if_owner(path); | |
| int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); |
|
I think it good now. |
F_OFD_GETLK/SETLK/SETLKW (Linux cmd 36/37/38) fell through to the default EINVAL case, so guest software relying on open file description locks (e.g. SQLite's WAL mode) had no working lock path. Add the three cases, guarded by #ifdef so hosts whose SDK predates OFD lock support still build and fall back to EINVAL. Since OFD locks belong to the open file description rather than a process, force the returned l_pid to -1 on a conflicting F_OFD_GETLK instead of leaking the host's raw PID, and reject a nonzero request l_pid with EINVAL, matching fs/locks.c fcntl_getlk/fcntl_setlk on Linux. Traditional (non-OFD) F_GETLK had the same PID leak: a conflicting lock's l_pid was passed straight from the host's raw PID, which guest code cannot resolve (e.g. a kill(pid, 0) liveness check hits the wrong process). Add proc_host_to_guest_pid(), the mirror of the existing proc_guest_to_host_pid(), to translate the lock holder back to a guest PID via the local child table and the cross-process registry. The registry fallback uses a dedicated single-target scan rather than the existing registry_read_locked() path, since that helper's per-record kill(2) liveness probe (needed for group-signal delivery) is redundant here -- the target pid was just observed holding a live lock -- and scales O(n) with registry size for no benefit; measured 44us -> 16us at 53 live registry entries. The struct flock translation is shared with the existing F_GETLK/ F_SETLK/F_SETLKW path via a new fcntl_flock_op() helper rather than duplicated, since the two paths differ only in the macOS cmd values and in how l_pid is validated/reported. Fixes sysprog21#129 Rebased onto main's test-matrix.sh restructuring (portable tests moved out of tests/manifest.txt into test-matrix.sh's run_unit_tests); added test-ofd-lock there alongside test-flock and bumped EXPECTED_BASELINES.
915da16 to
bb90715
Compare
Adds F_OFD_GETLK/F_OFD_SETLK/F_OFD_SETLKW (Linux cmd 36/37/38) to
sys_fcntl, which previously fell through to EINVAL. The three cases are guarded by#ifdef F_OFD_GETLKetc. so hosts with older SDKs lacking these constants still build and fall back to EINVAL. Since OFD locks belong to the open file description rather than a process,F_OFD_GETLKnow forces the returnedl_pidto -1 on a conflict instead of leaking the host PID, matching Linux.The Linux<->macOS struct flock translation is shared with the existing F_GETLK/F_SETLK/F_SETLKW path via a new
fcntl_flock_op()helper rather than duplicated.Fixes #129
Test plan
tests/test-ofd-lock.c(registered intests/manifest.txt), covering cross-fd OFD conflict detection,l_pid == -1on conflict, unlock clearing the conflict, andF_OFD_SETLKW.make checkgreen, including the new test and the existingtest-flock.clang-format --dry-run --Werrorclean.Summary by cubic
Adds OFD lock support to
sys_fcntland fixes PID reporting for lock conflicts to match Linux. Restores SQLite WAL and other OFD users; fixes #129.New Features
F_OFD_GETLK/F_OFD_SETLK/F_OFD_SETLKW(guarded by#ifdef).struct flocktranslation viafcntl_flock_op().Bug Fixes
GETLK, returnl_pid = -1on conflict and reject nonzero requestl_pidwithEINVAL.F_GETLK, report the conflicting owner as the guest PID viaproc_host_to_guest_pid().tests/test-ofd-lock.c, updatedtests/test-flock.c, andtests/test-matrix.shupdated to run it.Written for commit bb90715. Summary will update on new commits.