Path: blob/main/cranelift/codegen/src/isa/x64/pcc.rs
1693 views
//! Proof-carrying-code validation for x64 VCode.12use crate::ir::pcc::*;3use crate::ir::types::*;4use crate::isa::x64::inst::Inst;5use crate::isa::x64::inst::args::{Amode, Gpr, RegMem, SyntheticAmode, ToWritableReg};6use crate::machinst::pcc::*;7use crate::machinst::{InsnIndex, VCode};8use crate::machinst::{Reg, Writable};9use crate::trace;1011fn undefined_result(12ctx: &FactContext,13vcode: &mut VCode<Inst>,14dst: Writable<Gpr>,15reg_bits: u16,16result_bits: u16,17) -> PccResult<()> {18check_output(ctx, vcode, dst.to_writable_reg(), &[], |_vcode| {19clamp_range(ctx, reg_bits, result_bits, None)20})21}2223fn ensure_no_fact(vcode: &VCode<Inst>, reg: Reg) -> PccResult<()> {24if vcode.vreg_fact(reg.into()).is_some() {25Err(PccError::UnsupportedFact)26} else {27Ok(())28}29}3031/// Flow-state between facts.32#[derive(Clone, Debug, Default)]33pub(crate) struct FactFlowState {34cmp_flags: Option<(Fact, Fact)>,35}3637pub(crate) fn check(38ctx: &FactContext,39vcode: &mut VCode<Inst>,40inst_idx: InsnIndex,41state: &mut FactFlowState,42) -> PccResult<()> {43trace!("Checking facts on inst: {:?}", vcode[inst_idx]);4445// We only persist flag state for one instruction, because we46// can't exhaustively enumerate all flags-effecting ops; so take47// the `cmp_state` here and perhaps use it below but don't let it48// remain.49let _cmp_flags = state.cmp_flags.take();5051match vcode[inst_idx] {52Inst::Args { .. } => {53// Defs on the args have "axiomatic facts": we trust the54// ABI code to pass through the values unharmed, so the55// facts given to us in the CLIF should still be true.56Ok(())57}5859Inst::CheckedSRemSeq {60dst_quotient,61dst_remainder,62..63} => {64undefined_result(ctx, vcode, dst_quotient, 64, 64)?;65undefined_result(ctx, vcode, dst_remainder, 64, 64)?;66Ok(())67}6869Inst::CheckedSRemSeq8 { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64),7071Inst::MovFromPReg { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64),72Inst::MovToPReg { .. } => Ok(()),7374Inst::XmmCmove { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()),7576Inst::StackProbeLoop { tmp, .. } => ensure_no_fact(vcode, tmp.to_reg()),7778Inst::CvtUint64ToFloatSeq {79dst,80tmp_gpr1,81tmp_gpr2,82..83} => {84ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?;85ensure_no_fact(vcode, tmp_gpr1.to_writable_reg().to_reg())?;86ensure_no_fact(vcode, tmp_gpr2.to_writable_reg().to_reg())?;87Ok(())88}8990Inst::CvtFloatToSintSeq {91dst,92tmp_gpr,93tmp_xmm,94..95} => {96undefined_result(ctx, vcode, dst, 64, 64)?;97ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?;98ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?;99Ok(())100}101102Inst::CvtFloatToUintSeq {103dst,104tmp_gpr,105tmp_xmm,106tmp_xmm2,107..108} => {109undefined_result(ctx, vcode, dst, 64, 64)?;110ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?;111ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?;112ensure_no_fact(vcode, tmp_xmm2.to_writable_reg().to_reg())?;113Ok(())114}115116Inst::XmmMinMaxSeq { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()),117118Inst::CallKnown { .. }119| Inst::ReturnCallKnown { .. }120| Inst::JmpKnown { .. }121| Inst::WinchJmpIf { .. }122| Inst::JmpCond { .. }123| Inst::JmpCondOr { .. }124| Inst::TrapIf { .. }125| Inst::TrapIfAnd { .. }126| Inst::TrapIfOr { .. } => Ok(()),127Inst::Rets { .. } => Ok(()),128129Inst::ReturnCallUnknown { .. } => Ok(()),130131Inst::CallUnknown { ref info } => match &info.dest {132RegMem::Mem { addr } => {133check_load(ctx, None, addr, vcode, I64, 64)?;134Ok(())135}136RegMem::Reg { .. } => Ok(()),137},138139Inst::JmpTableSeq { tmp1, tmp2, .. } => {140ensure_no_fact(vcode, tmp1.to_reg())?;141ensure_no_fact(vcode, tmp2.to_reg())?;142Ok(())143}144145Inst::LoadExtName { dst, .. } => {146ensure_no_fact(vcode, *dst.to_reg())?;147Ok(())148}149150Inst::AtomicRmwSeq {151ref mem,152temp,153dst_old,154..155} => {156ensure_no_fact(vcode, *dst_old.to_reg())?;157ensure_no_fact(vcode, *temp.to_reg())?;158check_store(ctx, None, mem, vcode, I64)?;159Ok(())160}161162Inst::Atomic128RmwSeq {163ref mem,164temp_low,165temp_high,166dst_old_low,167dst_old_high,168..169} => {170ensure_no_fact(vcode, *dst_old_low.to_reg())?;171ensure_no_fact(vcode, *dst_old_high.to_reg())?;172ensure_no_fact(vcode, *temp_low.to_reg())?;173ensure_no_fact(vcode, *temp_high.to_reg())?;174check_store(ctx, None, mem, vcode, I128)?;175Ok(())176}177178Inst::Atomic128XchgSeq {179ref mem,180dst_old_low,181dst_old_high,182..183} => {184ensure_no_fact(vcode, *dst_old_low.to_reg())?;185ensure_no_fact(vcode, *dst_old_high.to_reg())?;186check_store(ctx, None, mem, vcode, I128)?;187Ok(())188}189190Inst::XmmUninitializedValue { dst } => {191ensure_no_fact(vcode, dst.to_writable_reg().to_reg())192}193194Inst::GprUninitializedValue { dst } => {195ensure_no_fact(vcode, dst.to_writable_reg().to_reg())196}197198Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {199ensure_no_fact(vcode, dst.to_writable_reg().to_reg())200}201Inst::CoffTlsGetAddr { dst, tmp, .. } => {202ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?;203ensure_no_fact(vcode, tmp.to_writable_reg().to_reg())?;204Ok(())205}206207Inst::Unwind { .. } | Inst::DummyUse { .. } => Ok(()),208209Inst::StackSwitchBasic { .. } => Err(PccError::UnimplementedInst),210211Inst::LabelAddress { .. } => Err(PccError::UnimplementedInst),212213Inst::External { .. } => Ok(()), // TODO: unsure what to do about this!214}215}216217fn check_load(218ctx: &FactContext,219dst: Option<Writable<Reg>>,220src: &SyntheticAmode,221vcode: &VCode<Inst>,222ty: Type,223to_bits: u16,224) -> PccResult<Option<Fact>> {225let result_fact = dst.and_then(|dst| vcode.vreg_fact(dst.to_reg().into()));226let from_bits = u16::try_from(ty.bits()).unwrap();227check_mem(228ctx,229src,230vcode,231ty,232LoadOrStore::Load {233result_fact,234from_bits,235to_bits,236},237)238}239240fn check_store(241ctx: &FactContext,242data: Option<Reg>,243dst: &SyntheticAmode,244vcode: &VCode<Inst>,245ty: Type,246) -> PccResult<()> {247let stored_fact = data.and_then(|data| vcode.vreg_fact(data.into()));248check_mem(ctx, dst, vcode, ty, LoadOrStore::Store { stored_fact }).map(|_| ())249}250251fn check_mem<'a>(252ctx: &FactContext,253amode: &SyntheticAmode,254vcode: &VCode<Inst>,255ty: Type,256op: LoadOrStore<'a>,257) -> PccResult<Option<Fact>> {258let addr = match amode {259SyntheticAmode::Real(amode) if amode.get_flags().checked() => {260compute_addr(ctx, vcode, amode, 64).ok_or(PccError::MissingFact)?261}262_ => return Ok(None),263};264265match op {266LoadOrStore::Load {267result_fact,268from_bits,269to_bits,270} => {271let loaded_fact = clamp_range(ctx, to_bits, from_bits, ctx.load(&addr, ty)?.cloned())?;272trace!(273"loaded_fact = {:?} result_fact = {:?}",274loaded_fact, result_fact275);276if ctx.subsumes_fact_optionals(loaded_fact.as_ref(), result_fact) {277Ok(loaded_fact.clone())278} else {279Err(PccError::UnsupportedFact)280}281}282LoadOrStore::Store { stored_fact } => {283ctx.store(&addr, ty, stored_fact)?;284Ok(None)285}286}287}288289fn compute_addr(ctx: &FactContext, vcode: &VCode<Inst>, amode: &Amode, bits: u16) -> Option<Fact> {290trace!("compute_addr: {:?}", amode);291match *amode {292Amode::ImmReg { simm32, base, .. } => {293let base = get_fact_or_default(vcode, base, bits);294trace!("base = {:?}", base);295let simm32: i64 = simm32.into();296let simm32: u64 = simm32 as u64;297let offset = Fact::constant(bits, simm32);298let sum = ctx.add(&base, &offset, bits)?;299trace!("sum = {:?}", sum);300Some(sum)301}302Amode::ImmRegRegShift {303simm32,304base,305index,306shift,307..308} => {309let base = get_fact_or_default(vcode, base.into(), bits);310let index = get_fact_or_default(vcode, index.into(), bits);311trace!("base = {:?} index = {:?}", base, index);312let shifted = ctx.shl(&index, bits, shift.into())?;313let sum = ctx.add(&base, &shifted, bits)?;314let simm32: i64 = simm32.into();315let simm32: u64 = simm32 as u64;316let offset = Fact::constant(bits, simm32);317let sum = ctx.add(&sum, &offset, bits)?;318trace!("sum = {:?}", sum);319Some(sum)320}321Amode::RipRelative { .. } => None,322}323}324325326