Skip to content

Commit 949ecea

Browse files
committed
Auto merge of #15258 - HKalbasi:mir, r=HKalbasi
Support getrandom syscall And fix some simd intrinsic bugs and add a memory limit to prevent huge allocations from breaking the main process.
2 parents ea02f4c + 59420af commit 949ecea

File tree

8 files changed

+180
-21
lines changed

8 files changed

+180
-21
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-ty/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ bitflags = "2.1.0"
1919
smallvec.workspace = true
2020
ena = "0.14.0"
2121
either = "1.7.0"
22+
oorandom = "11.1.3"
2223
tracing = "0.1.35"
2324
rustc-hash = "1.1.0"
2425
scoped-tls = "1.0.0"

crates/hir-ty/src/consteval/tests.rs

+22
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,28 @@ fn exec_limits() {
24942494
);
24952495
}
24962496

2497+
#[test]
2498+
fn memory_limit() {
2499+
check_fail(
2500+
r#"
2501+
extern "Rust" {
2502+
#[rustc_allocator]
2503+
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
2504+
}
2505+
2506+
const GOAL: u8 = unsafe {
2507+
__rust_alloc(30_000_000_000, 1); // 30GB
2508+
2
2509+
};
2510+
"#,
2511+
|e| {
2512+
e == ConstEvalError::MirEvalError(MirEvalError::Panic(
2513+
"Memory allocation of 30000000000 bytes failed".to_string(),
2514+
))
2515+
},
2516+
);
2517+
}
2518+
24972519
#[test]
24982520
fn type_error() {
24992521
check_fail(

crates/hir-ty/src/consteval/tests/intrinsics.rs

+38
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,41 @@ fn rotate() {
602602
320192512,
603603
);
604604
}
605+
606+
#[test]
607+
fn simd() {
608+
check_number(
609+
r#"
610+
pub struct i8x16(
611+
i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,
612+
);
613+
extern "platform-intrinsic" {
614+
pub fn simd_bitmask<T, U>(x: T) -> U;
615+
}
616+
const GOAL: u16 = simd_bitmask(i8x16(
617+
0, 1, 0, 0, 2, 255, 100, 0, 50, 0, 1, 1, 0, 0, 0, 0
618+
));
619+
"#,
620+
0b0000110101110010,
621+
);
622+
check_number(
623+
r#"
624+
pub struct i8x16(
625+
i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,
626+
);
627+
extern "platform-intrinsic" {
628+
pub fn simd_lt<T, U>(x: T, y: T) -> U;
629+
pub fn simd_bitmask<T, U>(x: T) -> U;
630+
}
631+
const GOAL: u16 = simd_bitmask(simd_lt::<i8x16, i8x16>(
632+
i8x16(
633+
-105, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
634+
),
635+
i8x16(
636+
-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
637+
),
638+
));
639+
"#,
640+
0xFFFF,
641+
);
642+
}

crates/hir-ty/src/mir/eval.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ pub struct Evaluator<'a> {
137137
/// time of use.
138138
vtable_map: VTableMap,
139139
thread_local_storage: TlsData,
140+
random_state: oorandom::Rand64,
140141
stdout: Vec<u8>,
141142
stderr: Vec<u8>,
142143
layout_cache: RefCell<FxHashMap<Ty, Arc<Layout>>>,
@@ -147,6 +148,8 @@ pub struct Evaluator<'a> {
147148
execution_limit: usize,
148149
/// An additional limit on stack depth, to prevent stack overflow
149150
stack_depth_limit: usize,
151+
/// Maximum count of bytes that heap and stack can grow
152+
memory_limit: usize,
150153
}
151154

152155
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -520,13 +523,15 @@ impl Evaluator<'_> {
520523
thread_local_storage: TlsData::default(),
521524
static_locations: HashMap::default(),
522525
db,
526+
random_state: oorandom::Rand64::new(0),
523527
trait_env,
524528
crate_id,
525529
stdout: vec![],
526530
stderr: vec![],
527531
assert_placeholder_ty_is_unused,
528532
stack_depth_limit: 100,
529533
execution_limit: 1000_000,
534+
memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap
530535
layout_cache: RefCell::new(HashMap::default()),
531536
}
532537
}
@@ -938,6 +943,11 @@ impl Evaluator<'_> {
938943
};
939944
locals.ptr = locals_ptr;
940945
let prev_stack_pointer = self.stack.len();
946+
if stack_size > self.memory_limit {
947+
return Err(MirEvalError::Panic(format!(
948+
"Stack overflow. Tried to grow stack to {stack_size} bytes"
949+
)));
950+
}
941951
self.stack.extend(iter::repeat(0).take(stack_size));
942952
Ok((locals, prev_stack_pointer))
943953
}
@@ -1180,7 +1190,7 @@ impl Evaluator<'_> {
11801190
let Some((size, align)) = self.size_align_of(ty, locals)? else {
11811191
not_supported!("unsized box initialization");
11821192
};
1183-
let addr = self.heap_allocate(size, align);
1193+
let addr = self.heap_allocate(size, align)?;
11841194
Owned(addr.to_bytes())
11851195
}
11861196
Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
@@ -1565,7 +1575,7 @@ impl Evaluator<'_> {
15651575
ConstScalar::Bytes(v, memory_map) => {
15661576
let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
15671577
let patch_map = memory_map.transform_addresses(|b, align| {
1568-
let addr = self.heap_allocate(b.len(), align);
1578+
let addr = self.heap_allocate(b.len(), align)?;
15691579
self.write_memory(addr, b)?;
15701580
Ok(addr.to_usize())
15711581
})?;
@@ -1580,7 +1590,7 @@ impl Evaluator<'_> {
15801590
return Err(MirEvalError::InvalidConst(konst.clone()));
15811591
}
15821592
}
1583-
let addr = self.heap_allocate(size, align);
1593+
let addr = self.heap_allocate(size, align)?;
15841594
self.write_memory(addr, &v)?;
15851595
self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
15861596
Interval::new(addr, size)
@@ -1683,13 +1693,19 @@ impl Evaluator<'_> {
16831693
}
16841694
}
16851695

1686-
fn heap_allocate(&mut self, size: usize, align: usize) -> Address {
1696+
fn heap_allocate(&mut self, size: usize, align: usize) -> Result<Address> {
1697+
if !align.is_power_of_two() || align > 10000 {
1698+
return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid")));
1699+
}
16871700
while self.heap.len() % align != 0 {
16881701
self.heap.push(0);
16891702
}
1703+
if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) {
1704+
return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed")));
1705+
}
16901706
let pos = self.heap.len();
16911707
self.heap.extend(iter::repeat(0).take(size));
1692-
Address::Heap(pos)
1708+
Ok(Address::Heap(pos))
16931709
}
16941710

16951711
fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
@@ -2200,7 +2216,7 @@ impl Evaluator<'_> {
22002216
)?;
22012217
// FIXME: there is some leak here
22022218
let size = layout.size.bytes_usize();
2203-
let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize);
2219+
let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?;
22042220
self.write_memory(addr, &result)?;
22052221
IntervalAndTy { interval: Interval { addr, size }, ty }
22062222
};
@@ -2235,10 +2251,10 @@ impl Evaluator<'_> {
22352251
let Some((size, align)) = self.size_align_of(&ty, locals)? else {
22362252
not_supported!("unsized extern static");
22372253
};
2238-
let addr = self.heap_allocate(size, align);
2254+
let addr = self.heap_allocate(size, align)?;
22392255
Interval::new(addr, size)
22402256
};
2241-
let addr = self.heap_allocate(self.ptr_size(), self.ptr_size());
2257+
let addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
22422258
self.write_memory(addr, &result.addr.to_bytes())?;
22432259
self.static_locations.insert(st, addr);
22442260
Ok(addr)
@@ -2398,11 +2414,11 @@ pub fn render_const_using_debug_impl(
23982414
not_supported!("core::fmt::Debug::fmt not found");
23992415
};
24002416
// a1 = &[""]
2401-
let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size());
2417+
let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?;
24022418
// a2 = &[::core::fmt::ArgumentV1::new(&(THE_CONST), ::core::fmt::Debug::fmt)]
24032419
// FIXME: we should call the said function, but since its name is going to break in the next rustc version
24042420
// and its ABI doesn't break yet, we put it in memory manually.
2405-
let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size());
2421+
let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?;
24062422
evaluator.write_memory(a2, &data.addr.to_bytes())?;
24072423
let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef(
24082424
db.intern_callable_def(debug_fmt_fn.into()).into(),
@@ -2412,7 +2428,7 @@ pub fn render_const_using_debug_impl(
24122428
evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?;
24132429
// a3 = ::core::fmt::Arguments::new_v1(a1, a2)
24142430
// FIXME: similarly, we should call function here, not directly working with memory.
2415-
let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size());
2431+
let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?;
24162432
evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?;
24172433
evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
24182434
evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?;

crates/hir-ty/src/mir/eval/shim.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl Evaluator<'_> {
140140
};
141141
let size = from_bytes!(usize, size.get(self)?);
142142
let align = from_bytes!(usize, align.get(self)?);
143-
let result = self.heap_allocate(size, align);
143+
let result = self.heap_allocate(size, align)?;
144144
destination.write_from_bytes(self, &result.to_bytes())?;
145145
}
146146
"rustc_deallocator" => { /* no-op for now */ }
@@ -155,7 +155,7 @@ impl Evaluator<'_> {
155155
} else {
156156
let ptr = Address::from_bytes(ptr.get(self)?)?;
157157
let align = from_bytes!(usize, align.get(self)?);
158-
let result = self.heap_allocate(new_size, align);
158+
let result = self.heap_allocate(new_size, align)?;
159159
Interval { addr: result, size: old_size }
160160
.write_from_interval(self, Interval { addr: ptr, size: old_size })?;
161161
destination.write_from_bytes(self, &result.to_bytes())?;
@@ -239,14 +239,42 @@ impl Evaluator<'_> {
239239
}
240240
}
241241

242+
fn exec_syscall(
243+
&mut self,
244+
id: i64,
245+
args: &[IntervalAndTy],
246+
destination: Interval,
247+
_locals: &Locals,
248+
_span: MirSpan,
249+
) -> Result<()> {
250+
match id {
251+
318 => {
252+
// SYS_getrandom
253+
let [buf, len, _flags] = args else {
254+
return Err(MirEvalError::TypeError("SYS_getrandom args are not provided"));
255+
};
256+
let addr = Address::from_bytes(buf.get(self)?)?;
257+
let size = from_bytes!(usize, len.get(self)?);
258+
for i in 0..size {
259+
let rand_byte = self.random_state.rand_u64() as u8;
260+
self.write_memory(addr.offset(i), &[rand_byte])?;
261+
}
262+
destination.write_from_interval(self, len.interval)
263+
}
264+
_ => {
265+
not_supported!("Unknown syscall id {id:?}")
266+
}
267+
}
268+
}
269+
242270
fn exec_extern_c(
243271
&mut self,
244272
as_str: &str,
245273
args: &[IntervalAndTy],
246274
_generic_args: &Substitution,
247275
destination: Interval,
248276
locals: &Locals,
249-
_span: MirSpan,
277+
span: MirSpan,
250278
) -> Result<()> {
251279
match as_str {
252280
"memcmp" => {
@@ -343,6 +371,15 @@ impl Evaluator<'_> {
343371
destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
344372
Ok(())
345373
}
374+
"syscall" => {
375+
let Some((id, rest)) = args.split_first() else {
376+
return Err(MirEvalError::TypeError(
377+
"syscall arg1 is not provided",
378+
));
379+
};
380+
let id = from_bytes!(i64, id.get(self)?);
381+
self.exec_syscall(id, rest, destination, locals, span)
382+
}
346383
_ => not_supported!("unknown external function {as_str}"),
347384
}
348385
}

crates/hir-ty/src/mir/eval/shim/simd.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,29 @@ macro_rules! not_supported {
2222
}
2323

2424
impl Evaluator<'_> {
25-
fn detect_simd_ty(&self, ty: &Ty) -> Result<usize> {
25+
fn detect_simd_ty(&self, ty: &Ty) -> Result<(usize, Ty)> {
2626
match ty.kind(Interner) {
2727
TyKind::Adt(id, subst) => {
2828
let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner))
2929
{
3030
Some(len) => len,
3131
_ => {
3232
if let AdtId::StructId(id) = id.0 {
33-
return Ok(self.db.struct_data(id).variant_data.fields().len());
33+
let struct_data = self.db.struct_data(id);
34+
let fields = struct_data.variant_data.fields();
35+
let Some((first_field, _)) = fields.iter().next() else {
36+
not_supported!("simd type with no field");
37+
};
38+
let field_ty = self.db.field_types(id.into())[first_field]
39+
.clone()
40+
.substitute(Interner, subst);
41+
return Ok((fields.len(), field_ty));
3442
}
3543
return Err(MirEvalError::TypeError("simd type with no len param"));
3644
}
3745
};
3846
match try_const_usize(self.db, len) {
39-
Some(it) => Ok(it as usize),
47+
Some(_) => not_supported!("array like simd type"),
4048
None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")),
4149
}
4250
}
@@ -75,7 +83,8 @@ impl Evaluator<'_> {
7583
let [left, right] = args else {
7684
return Err(MirEvalError::TypeError("simd args are not provided"));
7785
};
78-
let len = self.detect_simd_ty(&left.ty)?;
86+
let (len, ty) = self.detect_simd_ty(&left.ty)?;
87+
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
7988
let size = left.interval.size / len;
8089
let dest_size = destination.size / len;
8190
let mut destination_bytes = vec![];
@@ -89,6 +98,13 @@ impl Evaluator<'_> {
8998
break;
9099
}
91100
}
101+
if is_signed {
102+
if let Some((&l, &r)) = l.iter().zip(r).rev().next() {
103+
if l != r {
104+
result = (l as i8).cmp(&(r as i8));
105+
}
106+
}
107+
}
92108
let result = match result {
93109
Ordering::Less => ["lt", "le", "ne"].contains(&name),
94110
Ordering::Equal => ["ge", "le", "eq"].contains(&name),
@@ -102,9 +118,9 @@ impl Evaluator<'_> {
102118
}
103119
"bitmask" => {
104120
let [op] = args else {
105-
return Err(MirEvalError::TypeError("simd_shuffle args are not provided"));
121+
return Err(MirEvalError::TypeError("simd_bitmask args are not provided"));
106122
};
107-
let op_len = self.detect_simd_ty(&op.ty)?;
123+
let (op_len, _) = self.detect_simd_ty(&op.ty)?;
108124
let op_count = op.interval.size / op_len;
109125
let mut result: u64 = 0;
110126
for (i, val) in op.get(self)?.chunks(op_count).enumerate() {
@@ -131,7 +147,7 @@ impl Evaluator<'_> {
131147
))
132148
}
133149
};
134-
let left_len = self.detect_simd_ty(&left.ty)?;
150+
let (left_len, _) = self.detect_simd_ty(&left.ty)?;
135151
let left_size = left.interval.size / left_len;
136152
let vector =
137153
left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size));

0 commit comments

Comments
 (0)