/* * Validate that two nacljmps are byte-for-byte identical. Note that * one of the individual jumps must be validated in isolation with * ValidateIndirect5() before this is called. */ void ValidateIndirect5Replacement(const struct NCDecoderState *mstate_old, const struct NCDecoderState *mstate_new) { do { /* check that the and-guard is 3 bytes and bit-for-bit identical */ struct NCDecoderState *andinst_old = PreviousInst(mstate_old, -1); struct NCDecoderState *andinst_new = PreviousInst(mstate_new, -1); if (andinst_old->inst.length != 3) break; if (andinst_new->inst.length != 3) break; if (memcmp(andinst_old->inst.maddr, andinst_new->inst.maddr, 3) != 0) break; /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */ if (mstate_old->inst.length != 2) break; if (mstate_new->inst.length != 2) break; if (memcmp(mstate_old->inst.maddr, mstate_new->inst.maddr, 2) != 0) break; return; } while (0); BadInstructionError(mstate_new, "Replacement indirect jump must match original"); Stats_UnsafeIndirect(mstate_new->vstate); }
/* * The NaCl five-byte safe indirect calling sequence looks like this: * 83 e0 e0 and $0xe0,%eax * ff d0 call *%eax * The call may be replaced with a ff e0 jmp. Any register may * be used, not just eax. The validator requires exactly this * sequence. * Note: The code above assumes 32-bit alignment. Change e0 as appropriate * if a different alignment is used. */ static void ValidateIndirect5(const NCDecoderInst *dinst) { NCInstBytesPtr jmpopcode; NCInstBytesPtr andopcode; uint8_t mrm; uint8_t targetreg; const uint8_t kReg_ESP = 4; NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); struct NCDecoderInst *andinst = PreviousInst(dinst, 1); if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) { NCBadInstructionError(dinst, "Unsafe indirect jump"); NCStatsUnsafeIndirect(vstate); return; } /* note: no prefixbytes allowed */ NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0); /* note: no prefixbytes allowed */ NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0); mrm = NCInstBytesByteInline(&jmpopcode, 1); /* Note that the modrm_rm field holds the * target addr the modrm_reg is the opcode. */ targetreg = modrm_rmInline(mrm); NCStatsCheckTarget(vstate); NCStatsTargetIndirect(vstate); do { /* no prefix bytes allowed */ if (dinst->inst.prefixbytes != 0) break; if (dinst->inst.prefixbytes != 0) break; /* Check all the opcodes. */ /* In GROUP5, 2 => call, 4 => jmp */ if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break; if ((modrm_regInline(mrm) != 2) && (modrm_regInline(mrm) != 4)) break; /* Issue 32: disallow unsafe call/jump indirection */ /* example: ff 12 call (*edx) */ /* Reported by defend.the.world on 11 Dec 2008 */ if (modrm_modInline(mrm) != 3) break; if (targetreg == kReg_ESP) break; if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break; /* check modrm bytes of or and and instructions */ if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break; /* check mask */ if (NCInstBytesByteInline(&andopcode, 2) != (0x0ff & ~vstate->bundle_mask)) break; /* All checks look good. Make the sequence 'atomic.' */ ForgetInstructionBoundary(dinst, vstate); /* as a courtesy, check call alignment correctness */ if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst); return; } while (0); NCBadInstructionError(dinst, "Unsafe indirect jump"); NCStatsUnsafeIndirect(vstate); }
/* * Validate that two nacljmps are byte-for-byte identical. Note that * one of the individual jumps must be validated in isolation with * ValidateIndirect5() before this is called. */ static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old, NCDecoderInst *dinst_new) { do { /* check that the and-guard is 3 bytes and bit-for-bit identical */ NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1); NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1); if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break; if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break; if (memcmp(andinst_old->inst.bytes.byte, andinst_new->inst.bytes.byte, 3) != 0) break; /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */ if (dinst_old->inst.bytes.length != 2) break; if (dinst_new->inst.bytes.length != 2) break; if (memcmp(dinst_old->inst.bytes.byte, dinst_new->inst.bytes.byte, 2) != 0) break; return; } while (0); NCBadInstructionError(dinst_new, "Replacement indirect jump must match original"); NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate)); }
/* * The NaCl five-byte safe indirect calling sequence looks like this: * 83 e0 f0 and $0xfffffff0,%eax * ff d0 call *%eax * The call may be replaced with a ff e0 jmp. Any register may * be used, not just eax. The validator requires exactly this * sequence. * TODO(brad): validate or write the masks. */ static void ValidateIndirect5(const struct NCDecoderState *mstate) { uint8_t *jmpopcode; uint8_t *andopcode; uint8_t mrm; uint8_t targetreg; const uint8_t kReg_ESP = 4; struct NCDecoderState *andinst = PreviousInst(mstate, -1); assert(andinst != NULL); if (andinst->inst.length == 0) { BadInstructionError(mstate, "Unsafe indirect jump"); Stats_UnsafeIndirect(mstate->vstate); return; } jmpopcode = mstate->inst.maddr; /* note: no prefixbytes allowed */ andopcode = andinst->inst.maddr; /* note: no prefixbytes allowed */ mrm = jmpopcode[1]; targetreg = modrm_rm(mrm); /* Note that the modrm_rm field holds the */ /* target addr the modrm_reg is the opcode. */ Stats_CheckTarget(mstate->vstate); Stats_TargetIndirect(mstate->vstate); do { /* no prefix bytes allowed */ if (mstate->inst.prefixbytes != 0) break; if (andinst->inst.prefixbytes != 0) break; /* Check all the opcodes. */ /* In GROUP5, 2 => call, 4 => jmp */ if (jmpopcode[0] != 0xff) break; if ((modrm_reg(mrm) != 2) && (modrm_reg(mrm) != 4)) break; /* Issue 32: disallow unsafe call/jump indirection */ /* example: ff 12 call (*edx) */ /* Reported by defend.the.world on 11 Dec 2008 */ if (modrm_mod(mrm) != 3) break; if (targetreg == kReg_ESP) break; if (andopcode[0] != 0x83) break; /* check modrm bytes of or and and instructions */ if (andopcode[1] != (0xe0 | targetreg)) break; /* check mask */ if (andopcode[2] != (0x0ff & ~mstate->vstate->alignmask)) break; /* All checks look good. Make the sequence 'atomic.' */ ForgetIP(mstate->inst.vaddr, mstate->vstate); /* as a courtesy, check call alignment correctness */ if (modrm_reg(mrm) == 2) ValidateCallAlignment(mstate); return; } while (0); BadInstructionError(mstate, "Unsafe indirect jump"); Stats_UnsafeIndirect(mstate->vstate); }