/* * 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); }
/* Returns the corresponding segment register for the given index (0..7) */ static NaClExp* NaClAppendModRmSegmentReg(NaClInstState* state) { static NaClOpKind seg[8] = { RegES, RegCS, RegSS, RegDS, RegFS, RegGS, /* These should not happen. */ RegUnknown, RegUnknown }; return NaClAppendReg(seg[modrm_regInline(state->modrm)], &state->nodes); }
/* Returns the index into the general purpose registers based on the * value in the modrm.rm field of the instruction (taking into account * the REX prefix if it appears). */ static INLINE uint32_t state_modrm_reg(const NCDecoderInst* dinst) { /* TODO(karl) This is no where near close to being correct. Currently, * It only models r/m64 entry for instructions in the Intel documentation, * which requires the W bit to be set (r/m32 entries do not use REX.w, * and a different set of registers). */ uint32_t index = modrm_regInline(dinst->inst.mrm); #if NACL_TARGET_SUBARCH == 64 if (GetRexPrefixW(dinst) && GetRexPrefixR(dinst)) { index += 8; } #endif return index; }
/* Return the general purpose register associated with the modrm.reg * field. */ static INLINE int NaClGetGenRegRegister(NaClInstState* state) { DEBUG(NaClLog(LOG_INFO, "Get GenRegRegister\n")); return NaClGetRexRReg(state, modrm_regInline(state->modrm)); }
uint8_t modrm_reg(uint8_t modrm) { return modrm_regInline(modrm); }
static void InstFormat(const char* format, const NCDecoderInst *dinst, struct Gio* fp) { char token_buf[128]; char* fmt = token_buf; int pos = 0; strncpy(token_buf, format, sizeof(token_buf)); while (1) { char* token = strtok(fmt, " ,\n"); DEBUG( printf("\ntoken = '%s'\n", token) ); if (NULL == token) { break; } if (pos > 1) { gprintf(fp, ", "); } else if (pos > 0) { gprintf(fp, " "); } if ('$' == token[0]) { NaClMRMGroups group = ParseGroupName(token+1); if (NOGROUP != group) { int mrm = modrm_regInline(dinst->inst.mrm); const char* opname = kDisasmModRMOp[group][mrm]; DEBUG( printf("case: group %d, opname = %s\n", group, opname) ); gprintf(fp, "%s", opname); } else { /* Tokens starting with a $ but not $group need formatting */ DEBUG( printf("case: $ and not group\n") ); switch (token[1]) { case 'A': gprintf(fp, "$A"); break; case 'C': gprintf(fp, "%%cr%d", modrm_regInline(dinst->inst.mrm)); break; case 'D': gprintf(fp, "%%dr%d", modrm_regInline(dinst->inst.mrm)); break; case 'E': case 'M': /* mod should never be 3 for 'M' */ /* TODO(sehr): byte and word accesses */ RegMemPrint(dinst, gp_regs, 1, fp); break; case 'F': gprintf(fp, "eflags"); break; case 'G': gprintf(fp, "%s", gp_regs[modrm_regInline(dinst->inst.mrm)]); break; case 'I': gprintf(fp, "0x%"NACL_PRIx64, ImmedValue64(dinst)); break; case 'J': gprintf(fp, "0x%"NACL_PRIxNaClPcAddress, NCPrintableInstructionAddress(dinst) + dinst->inst.bytes.length + ImmedValue32(dinst)); break; case 'O': gprintf(fp, "[0x%"NACL_PRIx64"]", ImmedValue64(dinst)); break; case 'P': if ('R' == token[2]) { gprintf(fp, "%%mm%d", modrm_rmInline(dinst->inst.mrm)); } else { gprintf(fp, "%%mm%d", modrm_regInline(dinst->inst.mrm)); } break; case 'Q': RegMemPrint(dinst, mmx_regs, 0, fp); break; case 'R': gprintf(fp, "%s", gp_regs[modrm_rmInline(dinst->inst.mrm)]); break; case 'S': gprintf(fp, "%s", seg_regs[modrm_regInline(dinst->inst.mrm)]); break; case 'V': if ('R' == token[2]) { gprintf(fp, "%%xmm%d", modrm_rmInline(dinst->inst.mrm)); } else { gprintf(fp, "%%xmm%d", modrm_regInline(dinst->inst.mrm)); } break; case 'W': RegMemPrint(dinst, xmm_regs, 0, fp); break; case 'X': gprintf(fp, "ds:[esi]"); break; case 'Y': gprintf(fp, "es:[edi]"); break; default: gprintf(fp, "token('%s')", token); break; } } } else { /* Print the token as is */ gprintf(fp, "%s", token); } fmt = NULL; ++pos; } }