Index: src/native_client/src/trusted/service_runtime/nacl_text.c |
diff --git a/src/native_client/src/trusted/service_runtime/nacl_text.c b/src/native_client/src/trusted/service_runtime/nacl_text.c |
index 1e308f2a46ca7e71e0d3755f574fa6b1dd19f1a4..2de527ef3872247390820c0427cb4d0e23b09472 100644 |
--- a/src/native_client/src/trusted/service_runtime/nacl_text.c |
+++ b/src/native_client/src/trusted/service_runtime/nacl_text.c |
@@ -7,6 +7,7 @@ |
#include <string.h> |
#include "native_client/src/include/concurrency_ops.h" |
+#include "native_client/src/include/nacl_platform.h" |
#include "native_client/src/include/portability.h" |
#include "native_client/src/shared/platform/nacl_check.h" |
#include "native_client/src/shared/platform/nacl_log.h" |
@@ -30,6 +31,25 @@ |
/* initial size of the malloced buffer for dynamic regions */ |
static const int kMinDynamicRegionsAllocated = 32; |
+static const int kBitsPerByte = 8; |
+ |
+static uint8_t *BitmapAllocate(uint32_t indexes) { |
+ uint32_t byte_count = (indexes + kBitsPerByte - 1) / kBitsPerByte; |
+ uint8_t *bitmap = malloc(byte_count); |
+ if (bitmap != NULL) { |
+ memset(bitmap, 0, byte_count); |
+ } |
+ return bitmap; |
+} |
+ |
+static int BitmapIsBitSet(uint8_t *bitmap, uint32_t index) { |
+ return (bitmap[index / kBitsPerByte] & (1 << (index % kBitsPerByte))) != 0; |
+} |
+ |
+static void BitmapSetBit(uint8_t *bitmap, uint32_t index) { |
+ bitmap[index / kBitsPerByte] |= 1 << (index % kBitsPerByte); |
+} |
+ |
/* |
* Private subclass of NaClDescEffector, used only in this file. |
*/ |
@@ -90,10 +110,11 @@ NaClErrorCode NaClMakeDynamicTextShared(struct NaClApp *nap) { |
struct NaClDescEffectorShm shm_effector; |
int shm_effector_initialized = 0; |
uintptr_t shm_vaddr_base; |
- uintptr_t shm_offset; |
+ int mmap_protections; |
uintptr_t mmap_ret; |
uintptr_t shm_upper_bound; |
+ uintptr_t text_sysaddr; |
shm_vaddr_base = NaClEndOfStaticText(nap); |
NaClLog(4, |
@@ -163,76 +184,72 @@ NaClErrorCode NaClMakeDynamicTextShared(struct NaClApp *nap) { |
} |
shm_effector_initialized = 1; |
+ text_sysaddr = NaClUserToSys(nap, shm_vaddr_base); |
+ |
+ /* Existing memory is anonymous paging file backed. */ |
+ NaCl_page_free((void *) text_sysaddr, dynamic_text_size); |
+ |
/* |
- * Map shm in place of text. We currently do this eagerly, which |
- * can result in excess memory/swap traffic. |
+ * Unix allows us to map pages with PROT_NONE initially and later |
+ * increase the mapping permissions with mprotect(). |
* |
- * TODO(bsy): consider using NX and doing this lazily, or mapping a |
- * canonical HLT-filled 64K shm repeatedly, and only remapping with |
- * a "real" shm text as needed. |
+ * Windows does not allow this, however: the initial permissions are |
+ * an upper bound on what the permissions may later be changed to |
+ * with VirtualProtect(). Given this, using PROT_NONE at this point |
+ * does not even make sense. So we map with read+exec and |
+ * immediately turn down the permissions, so that we can later |
+ * re-enable read+exec page by page. |
*/ |
- for (shm_offset = 0; |
- shm_offset < dynamic_text_size; |
- shm_offset += NACL_MAP_PAGESIZE) { |
- uintptr_t text_vaddr = shm_vaddr_base + shm_offset; |
- uintptr_t text_sysaddr = NaClUserToSys(nap, text_vaddr); |
- |
- /* Existing memory is anonymous paging file backed. */ |
- NaCl_page_free((void *) text_sysaddr, NACL_MAP_PAGESIZE); |
+#if NACL_WINDOWS |
+ mmap_protections = NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC; |
+#else |
+ mmap_protections = NACL_ABI_PROT_NONE; |
+#endif |
+ NaClLog(4, |
+ "NaClMakeDynamicTextShared: Map(,,0x%"NACL_PRIxPTR",size = 0x%x," |
+ " prot=0x%x, flags=0x%x, offset=0)\n", |
+ text_sysaddr, |
+ (int) dynamic_text_size, |
+ mmap_protections, |
+ NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED); |
+ mmap_ret = (*((struct NaClDescVtbl const *) shm->base.base.vtbl)-> |
+ Map)((struct NaClDesc *) shm, |
+ (struct NaClDescEffector *) &shm_effector, |
+ (void *) text_sysaddr, |
+ dynamic_text_size, |
+ mmap_protections, |
+ NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED, |
+ 0); |
+ if (text_sysaddr != mmap_ret) { |
+ NaClLog(LOG_FATAL, "Could not map in shm for dynamic text region\n"); |
+ } |
- NaClLog(4, |
- "NaClMakeDynamicTextShared: Map(,,0x%"NACL_PRIxPTR",size = 0x%x," |
- " prot=0x%x, flags=0x%x, offset=0x%"NACL_PRIxPTR"\n", |
- text_sysaddr, |
- NACL_MAP_PAGESIZE, |
- NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, |
- NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED, |
- shm_offset); |
- mmap_ret = (*((struct NaClDescVtbl const *) |
- shm->base.base.vtbl)-> |
- Map)((struct NaClDesc *) shm, |
- (struct NaClDescEffector *) &shm_effector, |
- (void *) text_sysaddr, |
- NACL_MAP_PAGESIZE, |
- NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, |
- NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED, |
- shm_offset); |
- if (text_sysaddr != mmap_ret) { |
- NaClLog(LOG_FATAL, |
- "Could not map in shm for dynamic text region, HLT filling.\n"); |
- } |
- NaClFillMemoryRegionWithHalt((void *) text_sysaddr, NACL_MAP_PAGESIZE); |
- if (-1 == (*((struct NaClDescVtbl const *) |
- shm->base.base.vtbl)-> |
- UnmapUnsafe)((struct NaClDesc *) shm, |
- ((struct NaClDescEffector *) |
- &shm_effector), |
- (void *) text_sysaddr, |
- NACL_MAP_PAGESIZE)) { |
- NaClLog(LOG_FATAL, |
- "Could not unmap shm for dynamic text region, post HLT fill.\n"); |
- } |
- NaClLog(4, |
- "NaClMakeDynamicTextShared: Map(,,0x%"NACL_PRIxPTR",size = 0x%x," |
- " prot=0x%x, flags=0x%x, offset=0x%"NACL_PRIxPTR"\n", |
- text_sysaddr, |
- NACL_MAP_PAGESIZE, |
- NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC, |
- NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED, |
- shm_offset); |
- mmap_ret = (*((struct NaClDescVtbl const *) |
- shm->base.base.vtbl)-> |
- Map)((struct NaClDesc *) shm, |
- (struct NaClDescEffector *) &shm_effector, |
- (void *) text_sysaddr, |
- NACL_MAP_PAGESIZE, |
- NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC, |
- NACL_ABI_MAP_SHARED | NACL_ABI_MAP_FIXED, |
- shm_offset); |
- if (text_sysaddr != mmap_ret) { |
- NaClLog(LOG_FATAL, "Could not map in shm for dynamic text region\n"); |
+#if NACL_WINDOWS |
+ { |
+ /* |
+ * We need a loop here because the Map() call above creates one |
+ * mapping per page. However, there is no need for it to do that |
+ * for the dynamic code area. |
+ * TODO(mseaborn): Create a single mapping here. |
+ */ |
+ uintptr_t offset; |
+ for (offset = 0; offset < dynamic_text_size; offset += NACL_MAP_PAGESIZE) { |
+ DWORD old_prot; |
+ if (!VirtualProtect((void *) (text_sysaddr + offset), NACL_MAP_PAGESIZE, |
+ PAGE_NOACCESS, &old_prot)) { |
+ NaClLog(LOG_FATAL, |
+ "NaClMakeDynamicTextShared: VirtualProtect() failed to " |
+ "set page permissions to PAGE_NOACCESS\n"); |
+ } |
} |
} |
+#endif |
+ |
+ nap->dynamic_page_bitmap = |
+ BitmapAllocate((uint32_t) (dynamic_text_size / NACL_MAP_PAGESIZE)); |
+ if (NULL == nap->dynamic_page_bitmap) { |
+ NaClLog(LOG_FATAL, "NaClMakeDynamicTextShared: BitmapAllocate() failed\n"); |
+ } |
nap->text_shm = &shm->base; |
retval = LOAD_OK; |
@@ -494,6 +511,30 @@ static INLINE void CopyCodeSafelyInitial(uint8_t *dest, |
*/ |
} |
+static void MakeDynamicCodePageVisible(struct NaClApp *nap, |
+ uint32_t page_index, |
+ uint8_t *writable_addr) { |
+ void *user_addr; |
+ |
+ if (BitmapIsBitSet(nap->dynamic_page_bitmap, page_index)) { |
+ /* The page is already visible: nothing to do. */ |
+ return; |
+ } |
+ user_addr = (void *) NaClUserToSys(nap, nap->dynamic_text_start |
+ + page_index * NACL_MAP_PAGESIZE); |
+ |
+ /* Sanity check: Ensure the page is not already in use. */ |
+ CHECK(*writable_addr == 0); |
+ |
+ NaClFillMemoryRegionWithHalt(writable_addr, NACL_MAP_PAGESIZE); |
+ |
+ if (NaCl_mprotect(user_addr, NACL_MAP_PAGESIZE, PROT_READ | PROT_EXEC) != 0) { |
+ NaClLog(LOG_FATAL, "MakeDynamicCodePageVisible: NaCl_mprotect() failed\n"); |
+ } |
+ |
+ BitmapSetBit(nap->dynamic_page_bitmap, page_index); |
+} |
+ |
/* |
* Maps a writable version of the code at [offset, offset+size) and returns a |
* pointer to the new mapping. Internally caches the last mapping between |
@@ -551,6 +592,10 @@ static uintptr_t CachedMapWritableText(struct NaClApp *nap, |
* update that cached version |
*/ |
if (size > 0) { |
+ uint32_t page_index; |
+ uint32_t end_page_index; |
+ uint8_t *writable_addr; |
+ |
uintptr_t mapping = (*((struct NaClDescVtbl const *) |
shm->base.vtbl)-> |
Map)(shm, |
@@ -563,6 +608,16 @@ static uintptr_t CachedMapWritableText(struct NaClApp *nap, |
if (NaClPtrIsNegErrno(&mapping)) { |
return 0; |
} |
+ |
+ writable_addr = (uint8_t *) mapping; |
+ end_page_index = (offset + size) / NACL_MAP_PAGESIZE; |
+ for (page_index = offset / NACL_MAP_PAGESIZE; |
+ page_index < end_page_index; |
+ page_index++) { |
+ MakeDynamicCodePageVisible(nap, page_index, writable_addr); |
+ writable_addr += NACL_MAP_PAGESIZE; |
+ } |
+ |
nap->dynamic_mapcache_offset = offset; |
nap->dynamic_mapcache_size = size; |
nap->dynamic_mapcache_ret = mapping; |