Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/x64/pcc.rs
3073 views
1
//! Proof-carrying-code validation for x64 VCode.
2
3
use crate::ir::pcc::*;
4
use crate::ir::types::*;
5
use crate::isa::x64::inst::Inst;
6
use crate::isa::x64::inst::args::{Amode, Gpr, RegMem, SyntheticAmode, ToWritableReg};
7
use crate::machinst::pcc::*;
8
use crate::machinst::{InsnIndex, VCode};
9
use crate::machinst::{Reg, Writable};
10
use crate::trace;
11
12
fn undefined_result(
13
ctx: &FactContext,
14
vcode: &mut VCode<Inst>,
15
dst: Writable<Gpr>,
16
reg_bits: u16,
17
result_bits: u16,
18
) -> PccResult<()> {
19
check_output(ctx, vcode, dst.to_writable_reg(), &[], |_vcode| {
20
clamp_range(ctx, reg_bits, result_bits, None)
21
})
22
}
23
24
fn ensure_no_fact(vcode: &VCode<Inst>, reg: Reg) -> PccResult<()> {
25
if vcode.vreg_fact(reg.into()).is_some() {
26
Err(PccError::UnsupportedFact)
27
} else {
28
Ok(())
29
}
30
}
31
32
/// Flow-state between facts.
33
#[derive(Clone, Debug, Default)]
34
pub(crate) struct FactFlowState {
35
cmp_flags: Option<(Fact, Fact)>,
36
}
37
38
pub(crate) fn check(
39
ctx: &FactContext,
40
vcode: &mut VCode<Inst>,
41
inst_idx: InsnIndex,
42
state: &mut FactFlowState,
43
) -> PccResult<()> {
44
trace!("Checking facts on inst: {:?}", vcode[inst_idx]);
45
46
// We only persist flag state for one instruction, because we
47
// can't exhaustively enumerate all flags-effecting ops; so take
48
// the `cmp_state` here and perhaps use it below but don't let it
49
// remain.
50
let _cmp_flags = state.cmp_flags.take();
51
52
match vcode[inst_idx] {
53
Inst::Args { .. } => {
54
// Defs on the args have "axiomatic facts": we trust the
55
// ABI code to pass through the values unharmed, so the
56
// facts given to us in the CLIF should still be true.
57
Ok(())
58
}
59
60
Inst::CheckedSRemSeq {
61
dst_quotient,
62
dst_remainder,
63
..
64
} => {
65
undefined_result(ctx, vcode, dst_quotient, 64, 64)?;
66
undefined_result(ctx, vcode, dst_remainder, 64, 64)?;
67
Ok(())
68
}
69
70
Inst::CheckedSRemSeq8 { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64),
71
72
Inst::MovFromPReg { dst, .. } => undefined_result(ctx, vcode, dst, 64, 64),
73
Inst::MovToPReg { .. } => Ok(()),
74
75
Inst::XmmCmove { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()),
76
77
Inst::StackProbeLoop { tmp, .. } => ensure_no_fact(vcode, tmp.to_reg()),
78
79
Inst::CvtUint64ToFloatSeq {
80
dst,
81
tmp_gpr1,
82
tmp_gpr2,
83
..
84
} => {
85
ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?;
86
ensure_no_fact(vcode, tmp_gpr1.to_writable_reg().to_reg())?;
87
ensure_no_fact(vcode, tmp_gpr2.to_writable_reg().to_reg())?;
88
Ok(())
89
}
90
91
Inst::CvtFloatToSintSeq {
92
dst,
93
tmp_gpr,
94
tmp_xmm,
95
..
96
} => {
97
undefined_result(ctx, vcode, dst, 64, 64)?;
98
ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?;
99
ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?;
100
Ok(())
101
}
102
103
Inst::CvtFloatToUintSeq {
104
dst,
105
tmp_gpr,
106
tmp_xmm,
107
tmp_xmm2,
108
..
109
} => {
110
undefined_result(ctx, vcode, dst, 64, 64)?;
111
ensure_no_fact(vcode, tmp_gpr.to_writable_reg().to_reg())?;
112
ensure_no_fact(vcode, tmp_xmm.to_writable_reg().to_reg())?;
113
ensure_no_fact(vcode, tmp_xmm2.to_writable_reg().to_reg())?;
114
Ok(())
115
}
116
117
Inst::XmmMinMaxSeq { dst, .. } => ensure_no_fact(vcode, dst.to_writable_reg().to_reg()),
118
119
Inst::CallKnown { .. }
120
| Inst::ReturnCallKnown { .. }
121
| Inst::JmpKnown { .. }
122
| Inst::WinchJmpIf { .. }
123
| Inst::JmpCond { .. }
124
| Inst::JmpCondOr { .. }
125
| Inst::TrapIf { .. }
126
| Inst::TrapIfAnd { .. }
127
| Inst::TrapIfOr { .. } => Ok(()),
128
Inst::Rets { .. } => Ok(()),
129
130
Inst::ReturnCallUnknown { .. } => Ok(()),
131
132
Inst::CallUnknown { ref info } => match &info.dest {
133
RegMem::Mem { addr } => {
134
check_load(ctx, None, addr, vcode, I64, 64)?;
135
Ok(())
136
}
137
RegMem::Reg { .. } => Ok(()),
138
},
139
140
Inst::JmpTableSeq { tmp1, tmp2, .. } => {
141
ensure_no_fact(vcode, tmp1.to_reg())?;
142
ensure_no_fact(vcode, tmp2.to_reg())?;
143
Ok(())
144
}
145
146
Inst::LoadExtName { dst, .. } => {
147
ensure_no_fact(vcode, *dst.to_reg())?;
148
Ok(())
149
}
150
151
Inst::AtomicRmwSeq {
152
ref mem,
153
temp,
154
dst_old,
155
..
156
} => {
157
ensure_no_fact(vcode, *dst_old.to_reg())?;
158
ensure_no_fact(vcode, *temp.to_reg())?;
159
check_store(ctx, None, mem, vcode, I64)?;
160
Ok(())
161
}
162
163
Inst::Atomic128RmwSeq {
164
ref mem,
165
temp_low,
166
temp_high,
167
dst_old_low,
168
dst_old_high,
169
..
170
} => {
171
ensure_no_fact(vcode, *dst_old_low.to_reg())?;
172
ensure_no_fact(vcode, *dst_old_high.to_reg())?;
173
ensure_no_fact(vcode, *temp_low.to_reg())?;
174
ensure_no_fact(vcode, *temp_high.to_reg())?;
175
check_store(ctx, None, mem, vcode, I128)?;
176
Ok(())
177
}
178
179
Inst::Atomic128XchgSeq {
180
ref mem,
181
dst_old_low,
182
dst_old_high,
183
..
184
} => {
185
ensure_no_fact(vcode, *dst_old_low.to_reg())?;
186
ensure_no_fact(vcode, *dst_old_high.to_reg())?;
187
check_store(ctx, None, mem, vcode, I128)?;
188
Ok(())
189
}
190
191
Inst::XmmUninitializedValue { dst } => {
192
ensure_no_fact(vcode, dst.to_writable_reg().to_reg())
193
}
194
195
Inst::GprUninitializedValue { dst } => {
196
ensure_no_fact(vcode, dst.to_writable_reg().to_reg())
197
}
198
199
Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {
200
ensure_no_fact(vcode, dst.to_writable_reg().to_reg())
201
}
202
Inst::CoffTlsGetAddr { dst, tmp, .. } => {
203
ensure_no_fact(vcode, dst.to_writable_reg().to_reg())?;
204
ensure_no_fact(vcode, tmp.to_writable_reg().to_reg())?;
205
Ok(())
206
}
207
208
Inst::Unwind { .. } | Inst::DummyUse { .. } => Ok(()),
209
210
Inst::StackSwitchBasic { .. } => Err(PccError::UnimplementedInst),
211
212
Inst::LabelAddress { .. } => Err(PccError::UnimplementedInst),
213
214
Inst::SequencePoint { .. } => Ok(()),
215
216
Inst::External { .. } => Ok(()), // TODO: unsure what to do about this!
217
}
218
}
219
220
fn check_load(
221
ctx: &FactContext,
222
dst: Option<Writable<Reg>>,
223
src: &SyntheticAmode,
224
vcode: &VCode<Inst>,
225
ty: Type,
226
to_bits: u16,
227
) -> PccResult<Option<Fact>> {
228
let result_fact = dst.and_then(|dst| vcode.vreg_fact(dst.to_reg().into()));
229
let from_bits = u16::try_from(ty.bits()).unwrap();
230
check_mem(
231
ctx,
232
src,
233
vcode,
234
ty,
235
LoadOrStore::Load {
236
result_fact,
237
from_bits,
238
to_bits,
239
},
240
)
241
}
242
243
fn check_store(
244
ctx: &FactContext,
245
data: Option<Reg>,
246
dst: &SyntheticAmode,
247
vcode: &VCode<Inst>,
248
ty: Type,
249
) -> PccResult<()> {
250
let stored_fact = data.and_then(|data| vcode.vreg_fact(data.into()));
251
check_mem(ctx, dst, vcode, ty, LoadOrStore::Store { stored_fact }).map(|_| ())
252
}
253
254
fn check_mem<'a>(
255
ctx: &FactContext,
256
amode: &SyntheticAmode,
257
vcode: &VCode<Inst>,
258
ty: Type,
259
op: LoadOrStore<'a>,
260
) -> PccResult<Option<Fact>> {
261
let addr = match amode {
262
SyntheticAmode::Real(amode) if amode.get_flags().checked() => {
263
compute_addr(ctx, vcode, amode, 64).ok_or(PccError::MissingFact)?
264
}
265
_ => return Ok(None),
266
};
267
268
match op {
269
LoadOrStore::Load {
270
result_fact,
271
from_bits,
272
to_bits,
273
} => {
274
let loaded_fact = clamp_range(ctx, to_bits, from_bits, ctx.load(&addr, ty)?.cloned())?;
275
trace!(
276
"loaded_fact = {:?} result_fact = {:?}",
277
loaded_fact, result_fact
278
);
279
if ctx.subsumes_fact_optionals(loaded_fact.as_ref(), result_fact) {
280
Ok(loaded_fact.clone())
281
} else {
282
Err(PccError::UnsupportedFact)
283
}
284
}
285
LoadOrStore::Store { stored_fact } => {
286
ctx.store(&addr, ty, stored_fact)?;
287
Ok(None)
288
}
289
}
290
}
291
292
fn compute_addr(ctx: &FactContext, vcode: &VCode<Inst>, amode: &Amode, bits: u16) -> Option<Fact> {
293
trace!("compute_addr: {:?}", amode);
294
match *amode {
295
Amode::ImmReg { simm32, base, .. } => {
296
let base = get_fact_or_default(vcode, base, bits);
297
trace!("base = {:?}", base);
298
let simm32: i64 = simm32.into();
299
let simm32: u64 = simm32 as u64;
300
let offset = Fact::constant(bits, simm32);
301
let sum = ctx.add(&base, &offset, bits)?;
302
trace!("sum = {:?}", sum);
303
Some(sum)
304
}
305
Amode::ImmRegRegShift {
306
simm32,
307
base,
308
index,
309
shift,
310
..
311
} => {
312
let base = get_fact_or_default(vcode, base.into(), bits);
313
let index = get_fact_or_default(vcode, index.into(), bits);
314
trace!("base = {:?} index = {:?}", base, index);
315
let shifted = ctx.shl(&index, bits, shift.into())?;
316
let sum = ctx.add(&base, &shifted, bits)?;
317
let simm32: i64 = simm32.into();
318
let simm32: u64 = simm32 as u64;
319
let offset = Fact::constant(bits, simm32);
320
let sum = ctx.add(&sum, &offset, bits)?;
321
trace!("sum = {:?}", sum);
322
Some(sum)
323
}
324
Amode::RipRelative { .. } => None,
325
}
326
}
327
328