int NaClCopyCode(struct NaClApp *nap, uintptr_t guest_addr, uint8_t *data_old, uint8_t *data_new, size_t size) { int status; /* Fixed-feature mode disables any code copying for now. Currently * the only use of NaClCodeCopy() seems to be for dynamic code * modification, which should fail in NaClValidateCodeReplacement() * before reaching this. */ if (nap->fixed_feature_cpu_mode) { return LOAD_BAD_FILE; } status = NaClValidateStatus(nap->validator->CopyCode( guest_addr, data_old, data_new, size, &nap->cpu_features, NaClCopyInstruction)); /* * Flush the processor's instruction cache. This is not necessary * for security, because any old cached instructions will just be * safe halt instructions. It is only necessary to ensure that * untrusted code runs correctly when it tries to execute the * dynamically-loaded code. */ NaClFlushCacheForDoublyMappedCode(data_old, (uint8_t *) guest_addr, size); return status; }
int32_t NaClSysDyncodeDelete(struct NaClAppThread *natp, uint32_t dest, uint32_t size) { struct NaClApp *nap = natp->nap; uintptr_t dest_addr; uint8_t *mapped_addr; int32_t retval = -NACL_ABI_EINVAL; struct NaClDynamicRegion *region; if (!nap->enable_dyncode_syscalls) { NaClLog(LOG_WARNING, "NaClSysDyncodeDelete: Dynamic code syscalls are disabled\n"); return -NACL_ABI_ENOSYS; } if (NULL == nap->text_shm) { NaClLog(1, "NaClSysDyncodeDelete: Dynamic loading not enabled\n"); return -NACL_ABI_EINVAL; } if (0 == size) { /* Nothing to delete. Just update our generation. */ int gen; /* fetch current generation */ NaClXMutexLock(&nap->dynamic_load_mutex); gen = nap->dynamic_delete_generation; NaClXMutexUnlock(&nap->dynamic_load_mutex); /* set our generation */ NaClSetThreadGeneration(natp, gen); return 0; } dest_addr = NaClUserToSysAddrRange(nap, dest, size); if (kNaClBadAddress == dest_addr) { NaClLog(1, "NaClSysDyncodeDelete: Address out of range\n"); return -NACL_ABI_EFAULT; } NaClXMutexLock(&nap->dynamic_load_mutex); /* * this check ensures the to-be-deleted region is identical to a * previously inserted region, so no need to check for alignment/bounds/etc */ region = NaClDynamicRegionFind(nap, dest_addr, size); if (NULL == region || region->start != dest_addr || region->size != size || region->is_mmap) { NaClLog(1, "NaClSysDyncodeDelete: Can't find region to delete\n"); retval = -NACL_ABI_EFAULT; goto cleanup_unlock; } if (region->delete_generation < 0) { /* first deletion request */ if (nap->dynamic_delete_generation == INT32_MAX) { NaClLog(1, "NaClSysDyncodeDelete:" "Overflow, can only delete INT32_MAX regions\n"); retval = -NACL_ABI_EFAULT; goto cleanup_unlock; } if (!NaClTextMapWrapper(nap, dest, size, &mapped_addr)) { retval = -NACL_ABI_ENOMEM; goto cleanup_unlock; } /* make it so no new threads can enter target region */ ReplaceBundleHeadsWithHalts(mapped_addr, size, nap->bundle_size); /* * Flush the instruction cache. In principle this is needed for * security on ARM so that, when new code is loaded, it is not * possible for it to jump to stale code that remains in the * icache. */ NaClFlushCacheForDoublyMappedCode(mapped_addr, (uint8_t *) dest_addr, size); NaClTextMapClearCacheIfNeeded(nap, dest, size); /* increment and record the generation deletion was requested */ region->delete_generation = ++nap->dynamic_delete_generation; } /* update our own generation */ NaClSetThreadGeneration(natp, nap->dynamic_delete_generation); if (region->delete_generation <= NaClMinimumThreadGeneration(nap)) { /* * All threads have checked in since we marked region for deletion. * It is safe to remove the region. * * No need to memset the region to hlt since bundle heads are hlt * and thus the bodies are unreachable. */ NaClDynamicRegionDelete(nap, region); retval = 0; } else { /* * Still waiting for some threads to report in... */ retval = -NACL_ABI_EAGAIN; } cleanup_unlock: NaClXMutexUnlock(&nap->dynamic_load_mutex); return retval; }
int32_t NaClTextDyncodeCreate(struct NaClApp *nap, uint32_t dest, void *code_copy, uint32_t size, const struct NaClValidationMetadata *metadata) { uintptr_t dest_addr; uint8_t *mapped_addr; int32_t retval = -NACL_ABI_EINVAL; int validator_result; struct NaClPerfCounter time_dyncode_create; NaClPerfCounterCtor(&time_dyncode_create, "NaClTextDyncodeCreate"); if (NULL == nap->text_shm) { NaClLog(1, "NaClTextDyncodeCreate: Dynamic loading not enabled\n"); return -NACL_ABI_EINVAL; } if (0 != (dest & (nap->bundle_size - 1)) || 0 != (size & (nap->bundle_size - 1))) { NaClLog(1, "NaClTextDyncodeCreate: Non-bundle-aligned address or size\n"); return -NACL_ABI_EINVAL; } dest_addr = NaClUserToSysAddrRange(nap, dest, size); if (kNaClBadAddress == dest_addr) { NaClLog(1, "NaClTextDyncodeCreate: Dest address out of range\n"); return -NACL_ABI_EFAULT; } if (dest < nap->dynamic_text_start) { NaClLog(1, "NaClTextDyncodeCreate: Below dynamic code area\n"); return -NACL_ABI_EFAULT; } /* * We ensure that the final HLTs of the dynamic code region cannot * be overwritten, just in case of CPU bugs. */ if (dest + size > nap->dynamic_text_end - NACL_HALT_SLED_SIZE) { NaClLog(1, "NaClTextDyncodeCreate: Above dynamic code area\n"); return -NACL_ABI_EFAULT; } if (0 == size) { /* Nothing to load. Succeed trivially. */ return 0; } NaClXMutexLock(&nap->dynamic_load_mutex); /* * Validate the code before trying to create the region. This avoids the need * to delete the region if validation fails. * See: http://code.google.com/p/nativeclient/issues/detail?id=2566 */ if (!nap->skip_validator) { validator_result = NaClValidateCode(nap, dest, code_copy, size, metadata); } else { NaClLog(LOG_ERROR, "VALIDATION SKIPPED.\n"); validator_result = LOAD_OK; } NaClPerfCounterMark(&time_dyncode_create, NACL_PERF_IMPORTANT_PREFIX "DynRegionValidate"); NaClPerfCounterIntervalLast(&time_dyncode_create); if (validator_result != LOAD_OK && nap->ignore_validator_result) { NaClLog(LOG_ERROR, "VALIDATION FAILED for dynamically-loaded code: " "continuing anyway...\n"); validator_result = LOAD_OK; } if (validator_result != LOAD_OK) { NaClLog(1, "NaClTextDyncodeCreate: " "Validation of dynamic code failed\n"); retval = -NACL_ABI_EINVAL; goto cleanup_unlock; } if (NaClDynamicRegionCreate(nap, dest_addr, size, 0) != 1) { /* target addr is in use */ NaClLog(1, "NaClTextDyncodeCreate: Code range already allocated\n"); retval = -NACL_ABI_EINVAL; goto cleanup_unlock; } if (!NaClTextMapWrapper(nap, dest, size, &mapped_addr)) { retval = -NACL_ABI_ENOMEM; goto cleanup_unlock; } CopyCodeSafelyInitial(mapped_addr, code_copy, size, nap->bundle_size); /* * Flush the processor's instruction cache. This is not necessary * for security, because any old cached instructions will just be * safe halt instructions. It is only necessary to ensure that * untrusted code runs correctly when it tries to execute the * dynamically-loaded code. */ NaClFlushCacheForDoublyMappedCode(mapped_addr, (uint8_t *) dest_addr, size); retval = 0; NaClTextMapClearCacheIfNeeded(nap, dest, size); cleanup_unlock: NaClXMutexUnlock(&nap->dynamic_load_mutex); return retval; }