The native thread stack (not the IA2 per-compartment stacks) for child threads are allocated inside pthread_create, and are considered to be owned by whichever compartment calls pthread_create, as the new thread inherits the calling compartment. This unfortunately creates a conflict with the requirement (enforced by the tracer) that compartments cannot change the protection of memory owned by other compartments: each compartment sets up TLS by protecting its own TLS variables, changing their ownership from the thread-starting compartment to itself.
At program startup, the initial stack is not protected, so we don't see this problem, but if later in execution e.g. compartment 1 spawns a thread, then compartment 2's TLS variables will be part of the newly-allocated native stack and the tracer will see this as a compartment trying to steal memory.
We currently (as of #661) work around this by treating all stack allocations (mmap calls with the MAP_STACK flag) as owned by compartment 0 in the tracer:
track_memory_map.c:
// XXX: glibc maps stacks for new threads inside pthread_create, and by default they would
// inherit the compartment that starts them, but we need to partition out their TLS segments for
// each compartment, which the tracer would reject as a compartment trying to steal another's
// memory. for now, override the compartment to 0 for stacks so the later TLS protection
// succeeds, but note that this is undermining compartment safety of the thread's initial
// stack itself
if (info->mmap.flags & MAP_STACK) {
info->mmap.pkey = 0;
}
But this is not secure, so we should find some other, watertight way to handle this case. We may need to do something like #283 where we validate that the TLS regions for other compartments have not been modified by the thread-spawning compartment after protecting them.
The native thread stack (not the IA2 per-compartment stacks) for child threads are allocated inside
pthread_create, and are considered to be owned by whichever compartment callspthread_create, as the new thread inherits the calling compartment. This unfortunately creates a conflict with the requirement (enforced by the tracer) that compartments cannot change the protection of memory owned by other compartments: each compartment sets up TLS by protecting its own TLS variables, changing their ownership from the thread-starting compartment to itself.At program startup, the initial stack is not protected, so we don't see this problem, but if later in execution e.g. compartment 1 spawns a thread, then compartment 2's TLS variables will be part of the newly-allocated native stack and the tracer will see this as a compartment trying to steal memory.
We currently (as of #661) work around this by treating all stack allocations (
mmapcalls with theMAP_STACKflag) as owned by compartment 0 in the tracer:track_memory_map.c:But this is not secure, so we should find some other, watertight way to handle this case. We may need to do something like #283 where we validate that the TLS regions for other compartments have not been modified by the thread-spawning compartment after protecting them.