Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(692)

Unified Diff: src/trusted/validator_ragel/dfa_validate_common.c

Issue 1269113003: Rewrite non-temporal instructions Base URL: https://chromium.googlesource.com/native_client/src/native_client.git@master
Patch Set: Format Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/trusted/validator_ragel/dfa_validate_common.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/trusted/validator_ragel/dfa_validate_common.c
diff --git a/src/trusted/validator_ragel/dfa_validate_common.c b/src/trusted/validator_ragel/dfa_validate_common.c
index 4972cbd654b3fcd0d884dec0099db63750991802..4d489808e4de8eb278c1bdabde51246a32813399 100644
--- a/src/trusted/validator_ragel/dfa_validate_common.c
+++ b/src/trusted/validator_ragel/dfa_validate_common.c
@@ -11,7 +11,7 @@
#include "native_client/src/shared/platform/nacl_check.h"
#include "native_client/src/trusted/service_runtime/nacl_config.h"
-#include "native_client/src/trusted/validator_ragel/validator.h"
+#include "native_client/src/include/build_config.h"
/* Used as an argument to copy_func when unsupported instruction must be
replaced with HLTs. */
@@ -33,29 +33,217 @@ Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end,
return FALSE;
}
-Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin,
- const uint8_t *end,
- uint32_t info,
- void *callback_data) {
+Bool NaClDfaProcessPostRewriteValidationError(const uint8_t *begin,
+ const uint8_t *end,
+ uint32_t info,
+ void *callback_data) {
+ UNREFERENCED_PARAMETER(begin);
+ UNREFERENCED_PARAMETER(end);
+ UNREFERENCED_PARAMETER(callback_data);
+ /*
+ * Similar to NaClDfaRewriteUnsupportedInstruction(), we don't consider
+ * DIRECT_JUMP_OUT_OF_RANGE as an error. But if we still get
+ * CPUID_UNSUPPORTED_INSTRUCTION or UNSUPPORTED_INSTRUCTION, the validation
+ * should fail, because these errors should have already been fixed
+ * by NaClDfaRewriteUnsupportedInstruction().
+ */
+ return (info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE;
+}
+
+#if NACL_BUILD_SUBARCH == 64
+static Bool IsREX(uint8_t byte) {
+ return byte >= 0x40 && byte <= 0x4f;
+}
+#endif
+
+static Bool NaClDfaRewriteUnsupportedInstruction(const uint8_t *begin,
+ const uint8_t *end,
+ uint32_t info,
+ void *callback_data) {
+ uint8_t *ptr = (uint8_t *) begin;
struct StubOutCallbackData *data = callback_data;
- /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */
- if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
+ /*
+ * Clear DIRECT_JUMP_OUT_OF_RANGE error, because it may be introduced by
+ * validating a bundle which is smaller than the original chunk size. Even if
+ * the orignal chunk has this error, it can be detected when validating the
+ * whole chunk.
+ */
+ info &= ~DIRECT_JUMP_OUT_OF_RANGE;
+ if ((info & VALIDATION_ERRORS_MASK) == 0) {
+ return TRUE;
+ } else if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
+ /* Stub-out instructions unsupported on this CPU, but valid on other CPUs.*/
data->did_rewrite = 1;
memset((uint8_t *)begin, NACL_HALT_OPCODE, end - begin);
return TRUE;
- } else if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) {
- if (data->flags & NACL_DISABLE_NONTEMPORALS_X86) {
- return FALSE;
- } else {
- /* TODO(ruiq): rewrite instruction. For now, we keep the original
- * instruction and indicate validation success, which is consistent
- * with current validation results. */
- data->did_rewrite = 0;
- return TRUE;
- }
- } else {
+ } else if ((info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION) {
return FALSE;
}
+ /*
+ * Instruction rewriting. Note that we only rewrite non-temporal instructions
+ * found in current webstore nexes so that validation succeeds and we don't
+ * break them. If future nexes use other non-temporal instructions, they will
+ * fail validation.
+ *
+ * We usually only check and rewrite the first few bytes without examining
+ * further because this function is only called when the validator tells us
+ * that it is an 'unsupported instruction' and there are no other validation
+ * failures.
+ */
+#if NACL_BUILD_SUBARCH == 32
+ UNREFERENCED_PARAMETER(end);
+ if (memcmp(ptr, "\x0f\xe7", 2) == 0) {
+ /* movntq => movq */
+ ptr[1] = 0x7f;
+ data->did_rewrite = 1;
+ return TRUE;
+ } else if (memcmp(ptr, "\x66\x0f\xe7", 3) == 0) {
+ /* movntdq => movdqa */
+ ptr[2] = 0x7f;
+ data->did_rewrite = 1;
+ return TRUE;
+ }
+#elif NACL_BUILD_SUBARCH == 64
+ if (IsREX(ptr[0]) && ptr[1] == 0x0f) {
+ uint8_t opcode_byte2 = ptr[2];
+ switch (opcode_byte2) {
+ case 0x2b:
+ /* movntps => movaps */
+ ptr[2] = 0x29;
+ data->did_rewrite = 1;
+ return TRUE;
+ case 0xc3:
+ /* movnti => mov, nop */
+ if (info & RESTRICTED_REGISTER_USED) {
+ /*
+ * The rewriting for movnti is special because it changes instruction
+ * boundary: movnti is replaced by a mov and a nop so that the total
+ * size does not change. Therefore, special care needs to be taken:
+ * if restricted register is used in this instruction, we have to put
+ * nop at the end so that the rewritten restricted register consuming
+ * instruction follows closely with the restricted register producing
+ * instruction (if there is one).
+ */
+ ptr[1] = 0x89;
+ memmove(ptr + 2, ptr + 3, end - ptr - 3);
+ ptr[end - ptr - 1] = 0x90; /* NOP */
+ } else {
+ /*
+ * There are cases where we need to preserve instruction end position,
+ * for example, when RIP-relative address is used. Fortunately, RIP-
+ * relative addressing cannot use an index register, and therefore
+ * RESTRICTED_REGISTER_USED cannot be set. Therefore, no matter
+ * whether RIP-relative addressing is used, as long as restricted
+ * register is not used, we are safe to put nop in the beginning and
+ * preserve instruction end position.
+ */
+ ptr[2] = 0x89;
+ ptr[1] = ptr[0];
+ ptr[0] = 0x90; /* NOP */
+ }
+ data->did_rewrite = 1;
+ return TRUE;
+ case 0x18:
+ /* prefetchnta => nop...nop */
+ memset(ptr, 0x90, end - ptr);
+ data->did_rewrite = 1;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ } else if (ptr[0] == 0x66 && IsREX(ptr[1]) &&
+ memcmp(ptr + 2, "\x0f\xe7", 2) == 0) {
+ /* movntdq => movdqa */
+ ptr[3] = 0x7f;
+ data->did_rewrite = 1;
+ return TRUE;
+ }
+#else
+ #error "Unknown architecture"
+#endif
+ return FALSE;
+}
+
+Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin,
+ const uint8_t *end,
+ uint32_t info,
+ void *callback_data) {
+ struct StubOutCallbackData *data = callback_data;
+ uintptr_t temp_addr;
+ uint8_t *bundle_begin;
+ Bool rc;
+ UNREFERENCED_PARAMETER(end);
+
+ /*
+ * We can only handle two types of errors by rewriting:
+ * CPUID_UNSUPPORTED_INSTRUCTION and UNSUPPORTED_INSTRUCTION.
+ */
+ if ((info & VALIDATION_ERRORS_MASK) != CPUID_UNSUPPORTED_INSTRUCTION &&
+ (info & VALIDATION_ERRORS_MASK) != UNSUPPORTED_INSTRUCTION)
+ return FALSE;
+ if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION &&
+ (data->flags & NACL_DISABLE_NONTEMPORALS_X86))
+ return FALSE;
+
+ CHECK(!data->chunk_processed_as_a_contiguous_stream);
+ /*
+ * Compute bundle begin. Note that the validator does not enforce the
+ * validated chunk to start at a 32-byte aligned address, although it checks
+ * the chunk size to be a multiple of 32 (kBundleSize). Therefore, we cannot
+ * simply compute bundle_begin from "begin & ~kBundleMask", but have to
+ * consider to bundle_begin_offset. Alternatively, bundle begin can be passed
+ * to the user callback by the validator. However, the callback interface
+ * needs to be changed for this additional parameter, because "callback_data"
+ * is opaque to the validator and should not be used for this purpose.
+ */
+ temp_addr = ((uintptr_t) begin & ~kBundleMask) + data->bundle_begin_offset;
+ if (temp_addr <= (uintptr_t) begin)
+ bundle_begin = (uint8_t *) temp_addr;
+ else
+ bundle_begin = (uint8_t *) (temp_addr - kBundleSize);
+
+ /*
+ * Rewrite the bundle containing current instruction. We want to rewrite the
+ * whole bundle primarily because this eases revalidation. We should not
+ * revalidate a single instruction because it may use a restricted register,
+ * and is prefixed by a restricted register producing instruction in the
+ * bundle. If we just revalidate this restricted register consuming
+ * instruction, we will erroneously get an UNRESTRICTED_INDEX_REGISTER error.
+ * On the other hand, we don't want to revalidate the whole chunk because it
+ * can be expensive. Therefore, the only choice left is to revalidate the
+ * current bundle. This requires us to rewrite the whole bundle first, because
+ * there could be to-be-rewritten instructions after current instruction, and
+ * the revalidation of current bundle would fail if we just rewrite current
+ * instruction without rewriting those after.
+ */
+#if NACL_BUILD_SUBARCH == 32
+ rc = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
+ data->cpu_features,
+ NaClDfaRewriteUnsupportedInstruction,
+ callback_data);
+#elif NACL_BUILD_SUBARCH == 64
+ rc = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
+ data->cpu_features,
+ NaClDfaRewriteUnsupportedInstruction,
+ callback_data);
+#endif
+
+ if (!rc)
+ return FALSE;
+
+ /* Revalidate the bundle after rewriting. */
+#if NACL_BUILD_SUBARCH == 32
+ rc = ValidateChunkIA32(bundle_begin, kBundleSize, 0 /*options*/,
+ data->cpu_features,
+ NaClDfaProcessPostRewriteValidationError,
+ NULL);
+#elif NACL_BUILD_SUBARCH == 64
+ rc = ValidateChunkAMD64(bundle_begin, kBundleSize, 0 /*options*/,
+ data->cpu_features,
+ NaClDfaProcessPostRewriteValidationError,
+ NULL);
+#endif
+ return rc;
}
Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new,
« no previous file with comments | « src/trusted/validator_ragel/dfa_validate_common.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698