Thank you Vlastimil! Very helpful. So it doesn't look like an accounting bug which I was suspecting. I am really surprised the mapping in the middle doesn't have VM_GROWSDOWN. mprotect should preserve VM_GROWS* flag. And indeed mprotect does the following prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP); [...] vm_flags = calc_vm_prot_bits(prot); [...] newflags = vm_flags; newflags |= (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC)); so it should preserve everything but RWX which are taken from the given prot parameter. Maybe I am missing something though. I will be staring into the code some more.