| 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 70df3439dfa02014133537fd5e60ce25749648fd..69cc1c28899d9f6d81f76603661cb94709b1cb94 100644
|
| --- a/src/trusted/validator_ragel/dfa_validate_common.c
|
| +++ b/src/trusted/validator_ragel/dfa_validate_common.c
|
| @@ -9,6 +9,7 @@
|
|
|
| #include <string.h>
|
|
|
| +#include "native_client/src/include/build_config.h"
|
| #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"
|
| @@ -38,8 +39,7 @@ Bool NaClDfaProcessValidationError(const uint8_t *begin, const uint8_t *end,
|
| * RewriteAndRevalidateBundle()'s two validation passes, which
|
| * validate individual instruction bundles.
|
| */
|
| -static Bool AllowErrorDuringBundleValidation(
|
| - uint32_t info, struct StubOutCallbackData *data) {
|
| +static Bool AllowErrorDuringBundleValidation(uint32_t info) {
|
| if ((info & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE) {
|
| /*
|
| * This error can occur on valid jumps because we are validating
|
| @@ -47,9 +47,99 @@ static Bool AllowErrorDuringBundleValidation(
|
| */
|
| return TRUE;
|
| }
|
| - if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION) {
|
| - return (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0;
|
| + return FALSE;
|
| +}
|
| +
|
| +#if NACL_BUILD_SUBARCH == 64
|
| +static Bool IsREXPrefix(uint8_t byte) {
|
| + return byte >= 0x40 && byte <= 0x4f;
|
| +}
|
| +#endif
|
| +
|
| +static Bool RewriteNonTemporal(uint8_t *ptr, uint8_t *end, uint32_t info) {
|
| + /*
|
| + * Instruction rewriting. Note that we only rewrite non-temporal
|
| + * instructions found in nexes and DSOs that are currently found in the
|
| + * Chrome Web Store. 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.
|
| + */
|
| + ptrdiff_t size = end - ptr;
|
| +#if NACL_BUILD_SUBARCH == 32
|
| + UNREFERENCED_PARAMETER(end);
|
| + UNREFERENCED_PARAMETER(info);
|
| +
|
| + if (size >= 2 && memcmp(ptr, "\x0f\xe7", 2) == 0) {
|
| + /* movntq => movq */
|
| + ptr[1] = 0x7f;
|
| + return TRUE;
|
| + } else if (size >= 3 && memcmp(ptr, "\x66\x0f\xe7", 3) == 0) {
|
| + /* movntdq => movdqa */
|
| + ptr[2] = 0x7f;
|
| + return TRUE;
|
| + }
|
| +#elif NACL_BUILD_SUBARCH == 64
|
| + if (size >= 3 && IsREXPrefix(ptr[0]) && ptr[1] == 0x0f) {
|
| + uint8_t opcode_byte2 = ptr[2];
|
| + switch (opcode_byte2) {
|
| + case 0x2b:
|
| + /* movntps => movaps */
|
| + ptr[2] = 0x29;
|
| + return TRUE;
|
| + case 0xc3:
|
| + /* movnti => mov, nop */
|
| + if ((info & RESTRICTED_REGISTER_USED) != 0) {
|
| + /*
|
| + * 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, size - 3);
|
| + ptr[size - 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 */
|
| + }
|
| + return TRUE;
|
| + case 0x18:
|
| + /* prefetchnta => nop...nop */
|
| + memset(ptr, 0x90, size);
|
| + return TRUE;
|
| + default:
|
| + return FALSE;
|
| + }
|
| + } else if (size >= 4 &&
|
| + ptr[0] == 0x66 &&
|
| + IsREXPrefix(ptr[1]) &&
|
| + memcmp(ptr + 2, "\x0f\xe7", 2) == 0) {
|
| + /* movntdq => movdqa */
|
| + ptr[3] = 0x7f;
|
| + return TRUE;
|
| }
|
| +#else
|
| +# error "Unknown architecture"
|
| +#endif
|
| return FALSE;
|
| }
|
|
|
| @@ -69,7 +159,15 @@ static Bool BundleValidationApplyRewrite(const uint8_t *begin,
|
| memset((uint8_t *) begin, NACL_HALT_OPCODE, end - begin);
|
| return TRUE;
|
| }
|
| - return AllowErrorDuringBundleValidation(info, data);
|
| + if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION &&
|
| + (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0) {
|
| + if (RewriteNonTemporal((uint8_t *) begin, (uint8_t *) end, info)) {
|
| + data->did_rewrite = 1;
|
| + return TRUE;
|
| + }
|
| + return FALSE;
|
| + }
|
| + return AllowErrorDuringBundleValidation(info);
|
| }
|
|
|
| /*
|
| @@ -80,11 +178,11 @@ static Bool BundleValidationCheckAfterRewrite(const uint8_t *begin,
|
| const uint8_t *end,
|
| uint32_t info,
|
| void *callback_data) {
|
| - struct StubOutCallbackData *data = callback_data;
|
| UNREFERENCED_PARAMETER(begin);
|
| UNREFERENCED_PARAMETER(end);
|
| + UNREFERENCED_PARAMETER(callback_data);
|
|
|
| - return AllowErrorDuringBundleValidation(info, data);
|
| + return AllowErrorDuringBundleValidation(info);
|
| }
|
|
|
| /*
|
| @@ -143,18 +241,12 @@ Bool NaClDfaStubOutUnsupportedInstruction(const uint8_t *begin,
|
| /* Stub-out instructions unsupported on this CPU, but valid on other CPUs. */
|
| if ((info & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
|
| return RewriteAndRevalidateBundle(begin, end, data);
|
| - } 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. */
|
| - return TRUE;
|
| - }
|
| - } else {
|
| - return FALSE;
|
| }
|
| + if ((info & VALIDATION_ERRORS_MASK) == UNSUPPORTED_INSTRUCTION &&
|
| + (data->flags & NACL_DISABLE_NONTEMPORALS_X86) == 0) {
|
| + return RewriteAndRevalidateBundle(begin, end, data);
|
| + }
|
| + return FALSE;
|
| }
|
|
|
| Bool NaClDfaProcessCodeCopyInstruction(const uint8_t *begin_new,
|
|
|