Make the CPython bootstrap W^X compatible. Index: rpython/rlib/rmmap.py --- rpython/rlib/rmmap.py.orig +++ rpython/rlib/rmmap.py @@ -166,6 +166,9 @@ if _POSIX: _, c_madvise_safe = external('madvise', [PTR, size_t, rffi.INT], rffi.INT, _nowrapper=True) + c_mprotect, _ = external('mprotect', + [PTR, size_t, rffi.INT], rffi.INT) + # this one is always safe _pagesize = rffi_platform.getintegerfunctionresult('getpagesize', includes=includes) @@ -717,6 +720,25 @@ if _POSIX: prot = NonConstant(prot) return c_mmap_safe(hintp, map_size, prot, flags, -1, 0) + def alloc_hinted_noexec(hintp, map_size): + """Same as alloc_hinted, but allocates pages non-executable. + Duplicated because of constancy constraints on prot.""" + + flags = MAP_PRIVATE | MAP_ANONYMOUS + prot = PROT_READ | PROT_WRITE + + if we_are_translated(): + flags = NonConstant(flags) + prot = NonConstant(prot) + return c_mmap_safe(hintp, map_size, prot, flags, -1, 0) + + def set_pages_executable(addr, size): + from rpython.rlib import debug + + rv = c_mprotect(addr, size, PROT_EXEC) + if rv < 0: + debug.fatalerror_notb("set_pages_executable failed") + def clear_large_memory_chunk_aligned(addr, map_size): addr = rffi.cast(PTR, addr) flags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS @@ -732,10 +754,44 @@ if _POSIX: pos = -0x4fff0000 # for reproducible results hint = Hint() + def alloc_noexec(map_size): + """The same as `alloc`, but doesn't set the executable flag. + Duplicated because of constancy constraints on prot.""" + from errno import ENOMEM + from rpython.rlib import debug + + if _CYGWIN: + # XXX: JIT memory should be using mmap MAP_PRIVATE with + # PROT_EXEC but Cygwin's fork() fails. mprotect() + # cannot be used, but seems to be unnecessary there. + res = c_malloc_safe(map_size) + if res == rffi.cast(PTR, 0): + raise MemoryError + return res + res = alloc_hinted_noexec(rffi.cast(PTR, hint.pos), map_size) + if res == rffi.cast(PTR, -1): + # some systems (some versions of OS/X?) complain if they + # are passed a non-zero address. Try again. + res = alloc_hinted_noexec(rffi.cast(PTR, 0), map_size) + if res == rffi.cast(PTR, -1): + # ENOMEM simply raises MemoryError, but other errors are fatal + if rposix.get_saved_errno() != ENOMEM: + debug.fatalerror_notb( + "Got an unexpected error trying to allocate some " + "memory for the JIT (tried to do mmap() with " + "PROT_EXEC|PROT_READ|PROT_WRITE). This can be caused " + "by a system policy like PAX. You need to find how " + "to work around the policy on your system.") + raise MemoryError + else: + hint.pos += map_size + return res + alloc_noexec._annenforceargs_ = (int,) + def alloc(map_size): """Allocate memory. This is intended to be used by the JIT, - so the memory has the executable bit set and gets allocated - internally in case of a sandboxed process. + so the memory has the executable bit set. + and gets allocated internally in case of a sandboxed process. """ from errno import ENOMEM from rpython.rlib import debug @@ -936,6 +992,17 @@ elif _MS_WINDOWS: pos = -0x4fff0000 # for reproducible results hint = Hint() # XXX this has no effect on windows + def alloc_noexec(map_size): + """Allocate memory. This is intended to be used by the JIT, + so the memory has the executable bit set. + XXX implement me: it should get allocated internally in + case of a sandboxed process + + XXX no_exec ignored on windows + """ + return alloc(map_size) + alloc_noexec._annenforceargs_ = (int,) + def alloc(map_size): """Allocate memory. This is intended to be used by the JIT,