[Bug 297512] kern.proc.env sysctl fails with ENOMEM when last env string is near the end of the stack mapping; breaks ConsoleKit2 session lookup and GNOME screen unlock
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297512
Bug ID: 297512
Summary: kern.proc.env sysctl fails with ENOMEM when last env
string is near the end of the stack mapping; breaks
ConsoleKit2 session lookup and GNOME screen unlock
Product: Base System
Version: 15.1-RELEASE
Hardware: Any
OS: Any
Status: New
Severity: Affects Only Me
Priority: ---
Component: kern
Assignee: [email protected]
Reporter: [email protected]
Following a lock screen problem on GNOME, the source has been tracked down by
Fable as an error in the kernel component. Here is the full tested report.
On FreeBSD 15.1-RELEASE, sysctl kern.proc.env.<pid> (used by procstat
penv, kvm_getenvv(), and ConsoleKit2's GetSessionForUnixProcess) fails
with ENOMEM for a subset of processes, determined at exec time by
stack-gap ASLR.
Real-world impact: when it fails for gnome-shell, the GNOME screen
shield cannot resolve its ConsoleKit session. The visible symptom is a
lock screen that accepts no password: wrong passwords are rejected
normally, but a correct password does nothing at all (PAM
authentication succeeds, the unlock completion signal is never
delivered), permanently locking the user out of the session.
ROOT CAUSE
get_ps_strings() (sys/kern/kern_proc.c) reads each argv/env string in
256-byte chunks via proc_read_string() -> vmspace_iop(). The comment in
proc_read_string() says a short read at the end of a mapped region is
expected and handled:
This may return a short read if the string is shorter than the
chunk and is aligned at the end of the page, and the following page
is not mapped.
However vmspace_iop() (sys/kern/sys_process.c) discards partial
transfers whenever the underlying vmspace_rwmem() reports an error:
error = vmspace_rwmem(vm, &uio);
if (error != 0 || uio.uio_resid == slen)
return (-1);
return (slen - uio.uio_resid);
vmspace_rwmem() copies page by page and returns EFAULT when the next
page fault fails, even if previous pages were copied successfully. So
when the last environment string starts within 256 bytes of the end of
the stack mapping, the chunk read crosses the mapping end,
vmspace_rwmem() returns EFAULT with a partial transfer (the entire
string was actually read), and vmspace_iop() throws the data away and
returns -1. proc_read_string() turns that into ENOMEM and the whole
sysctl fails.
Whether a process is affected depends on the sub-page stack offset
chosen by ASLR at exec time (exec_new_vmspace(): stack_top -=
rounddown2(stack_off & PAGE_MASK, sizeof(void *))), combined with the
size of the fixed data above the last env string (execpath, canary,
page sizes, ps_strings). With kern.elf64.aslr.stack=0 the bug is masked
because the region above the stack top is adjacent to other mapped
pages, so the over-read succeeds.
EVIDENCE (live system, gnome-session pid 4246)
DTrace on vmspace_rwmem during a failing "procstat penv 4246" -- every
read succeeds except the last string, which is read almost completely
(249 of 256 bytes; the full string content plus NUL is included) and
then discarded:
@ 0x820d4ffa0 req=32 left=0 err=0 <- ps_strings
@ 0x820d4f858 req=208 left=0 err=0 <- env vector (26 entries)
@ 0x820d4fbf5 req=256 left=0 err=0 <- env[0]
... 24 more successful string reads ...
@ 0x820d4ff07 req=256 left=7 err=14 <- env[25]
"SSH_AGENT_PID=4255",
EFAULT crossing end of
stack mapping at
0x820d50000
Reading the same addresses via ptrace(PT_IO) succeeds and returns the
complete, intact environment, including the XDG_SESSION_COOKIE that
ConsoleKit2 needs.
Observed on two processes of one GNOME session (gnome-session-binary
and gnome-shell): last env string 249 and 241 bytes from the end of the
stack mapping respectively -- both < 256, so both unreadable. Sibling
processes with distance >= 256 read fine.
IMPACT
- procstat penv <pid> randomly fails ("Cannot allocate memory") for a
subset of processes, re-rolled at every exec.
- ConsoleKit2 GetSessionForUnixProcess fails for affected processes (it
reads XDG_SESSION_COOKIE via kvm_getenvv -> this sysctl).
- GNOME (x11/gnome-shell 47 + sysutils/consolekit2): when gnome-shell
is affected, ScreenShield._getLoginSession() fails at session start:
GNOME Shell-CRITICAL: Could not get a proxy for the current
session: ...ConsoleKit.Manager.Error.General: Unable to lookup
session information for process 'NNNN'
and the lock screen can never complete an unlock: PAM authentication
succeeds but the success is not signalled back to the shield. The
user is locked out until the session is killed or the shield is
deactivated via D-Bus by root.
REGRESSION HISTORY
FreeBSD 12.x/13.x proc_iop() deliberately ignored the proc_rwmem()
error and returned partial transfers (only failing when zero bytes were
read):
proc_rwmem(p, &uio);
if (uio.uio_resid == slen)
return (-1);
return (slen - uio.uio_resid);
The refactor that introduced vmspace_iop() (first appearing in
stable/14) added the "error != 0 ||" clause, silently changing the
short-read semantics that proc_read_string() documents and depends on.
The defect is present in stable/14, releng/15.1, and main as of
2026-08-14.
SUGGESTED FIX
Let vmspace_iop() report partial transfers instead of discarding them,
per proc_read_string()'s documented expectation:
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ vmspace_iop
error = vmspace_rwmem(vm, &uio);
- if (error != 0 || uio.uio_resid == slen)
+ if (uio.uio_resid == slen)
return (-1);
return (slen - uio.uio_resid);
Callers that require a full-length transfer (get_proc_vector()) already
compare the return value against the requested size, so their behavior
is unchanged. This also restores the historical proc_readmem()
semantics.
RELATED REPORTS (not duplicates)
- Bug 248537 "procstat -e/kvm_getenvv() fails for specific processes"
(2020, filed against 12.1, still open): same surface symptom. 12.x/
13.x tolerated partial reads, so pre-14 the sysctl only failed when a
chunk read returned zero bytes (a much rarer alignment). The
regression described here makes the same symptom far more common: any
process whose last env string starts within 256 bytes of the end of
the stack mapping now fails, with a pristine, verified-intact env
block (read back completely via ptrace(PT_IO)).
- GNOME Discourse "Gnome on FreeBSD - can't unlock desktop" (Nov 2025,
https://discourse.gnome.org/t/gnome-on-freebsd-cant-unlock-desktop/32293):
same user-facing GNOME symptom (correct password silently re-prompts,
wrong password errors normally); no root cause identified there.
WORKAROUNDS
- sysctl kern.elf64.aslr.stack=0 (masks the bug; costs stack ASLR).
- For GNOME: logging out and back in re-rolls the stack layout and
usually restores working unlock.
- Root can force-unlock a stuck shield, as the session user on their
session bus:
gdbus call --session --dest org.gnome.ScreenSaver \
--object-path /org/gnome/ScreenSaver \
--method org.gnome.ScreenSaver.SetActive false
ENVIRONMENT
FreeBSD 15.1-RELEASE-p2 amd64 (GENERIC), gnome-shell 47, gdm 47.0_2,
consolekit2 (sysutils/consolekit2).
VALIDATION OF THE PREDICTOR (before patch)
A scan of all 139 user processes on a live 15.1 system, measuring each
process's last-env-string distance to the next page boundary and
whether that page is mapped (via ptrace), matches the failure exactly:
4 processes were predicted affected (distance < 256 and next page
unmapped) and precisely those 4 -- powerd, gnome-session-binary,
gnome-shell, gsd-sound -- fail kern.proc.env with ENOMEM; the other
135 succeed. No mismatch in either direction.
PATCH TESTED
The suggested fix was applied to releng/15.1 sources and the rebuilt
GENERIC kernel was booted on the affected machine. Results:
- Whole-system scan after reboot: 112 processes, zero kern.proc.env
failures. Two processes (powerd, evolution-addressbook) landed in the
previously-failing layout (last env string < 256 bytes from an
unmapped page boundary) and both read correctly. powerd had the same
in-window layout on the unpatched kernel and failed with ENOMEM
there, so it provides a direct same-daemon before/after comparison.
- On-demand reproduction: repeatedly spawning /bin/sleep with varying
environment sizes until one instance landed in the failing layout
(distance 214, next page unmapped); procstat penv read its complete
environment without error on the patched kernel.
- GNOME: fresh session shows no ConsoleKit CRITICAL in gnome-shell's
log, GetSessionForUnixProcess resolves gnome-shell to its session,
and screen lock/unlock works. (Note: whether gnome-shell itself lands
in the failing layout is re-rolled each login; the sysctl-level tests
above are the deterministic evidence.)
--
You are receiving this mail because:
You are the assignee for the bug.
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.