-
Notifications
You must be signed in to change notification settings - Fork 85
Description
(I'm working on the feature/igc branch of GNU Emacs which adds MPS as a garbage collector. You haven't heard much from us since things are going very well generally, and it's usually our fault when they don't)
However, we've now seen several reports which indicate the mprotect call in protix.c:ProtSet failed, which leads to unreachable code being reached and an Emacs crash:
/* .assume.mprotect.base */
result = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags);
if (MAYBE_HARDENED_RUNTIME && result != 0 && errno == EACCES
&& (flags & PROT_WRITE) && (flags & PROT_EXEC))
{
/* Apple Hardened Runtime is enabled, so that we cannot have
* memory that is simultaneously writable and executable. Handle
* this by dropping the executable part of the request. See
* <design/prot#impl.xc.prot.exec> for details. */
prot_all = PROT_READ | PROT_WRITE;
result = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags & prot_all);
}
if (result != 0)
NOTREACHED;
protix.txt says this:
_`.fun.set.assume.mprotect`: We assume that the call to ``mprotect()``
always succeeds. We should always call the function with valid
arguments (aligned, references to mapped pages, and with an access
that is compatible with the access of the underlying object).
Our current theory is that this assumption is violated on Linux, which keeps a maximum number of contiguous maps in /proc/sys/vm/max_map_count, with the default at 65530. The way it appears to work is that when a single page in a large contiguous map is mprotected to be different than its neighbors, that map turns into three maps for the purposes of the max_map_count limit, and mprotect eventually returns ENOMEM.
Experiments with lowering the max_map_count limit in a virtual machine appear to be consistent with that theory, because it causes Emacs crashes much like the ones that were reported by our users.
Here's the Emacs bug report: