Let’s say I have the following pseudocode in the trusted part of a sandbox which prevent untrusted code calling mprotect()
and mmap()
and ptrace()
directly (mutext isn’t accessible from sandboxed memory)…
//src and dest are user controlled but must be valid.
TrustedPart_for_safe_jit(void * mutext, uint8_t *src,uint8_t *dest, uint32_t size) // in the current case, *dest targets a PROT_NONE memory region
{
MutexLock(mutext);
ValidateOpcodesOrCrash(src,size); // uses calls to mmap on size internally. Contains many different loops and use several 10k thousands lines of codes in the trusted part of the sandbox : this is the longest part. Please also note that src is write protected while being in this function.
unwriteprotect(dest,size); // calls many sandbox’s internal functions
SafeMemcpy(src,dest,size); // THIS IS the function which contains the race condition
asm("mfence");
unEXECprotect(dest,size); // involve write protecting as well as allowing reading
MutexUnlock(mutext);
}
SafeMemcpy(uint8_t *src,uint8_t *dest, uint32_t size) // the data to be copied cannot exceed 128Mb
{
if(!CheckUserTargetPointToValidMemroyRange(dest,size) {
uint8_t *src_ptr=src;
uint8_t *dest_ptr=dest;
uint8_t *end_ptr=des+size;
while (dest_ptr < end_ptr) { // that loop should execute very fast
*(uint32_t *) dest_ptr = *(uint32_t *) src_ptr;
dest_ptr += 4;
src_ptr += 4;
}
}
}
That part is responsible for allowing untrusted code to use ᴊɪᴛ compilation.
The point is untrusted thread aren’t suspended.
As you know, when 2 threads use memcpy()
with the same destination, they generate random data. In that case, such data could potentially contains instructions like int 0x80
, thus allowing to escape the sandbox.
Things I thought to so far :
- Write data to a file and read it with the read system call through the sandbox. If the memory is still write protected the program doesn’t crash. This would involve looping and even if the data to be copied can be 128Mb large I’m not sure it would works because of syscall overhead.
An Alternative would be to create code several times and try reading with several timing, but I have no idea on how to select the initial timing window. - Use futex… But I couldn’t found if futex can be used to check the value of non allocated memory. I’m also unsure if the thread could wake up before memory become write protected.
So, is it possible to plan the timing window for memcpy race conditions ?