From b5d3a25b49f97301bfeaa632d1cf432b3c0e8a71 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 16 Mar 2022 20:52:01 -0400 Subject: [PATCH] detect when unused bits of a SIMD bitmask are non-0 --- src/shims/intrinsics.rs | 30 +++++++++++++------ .../intrinsics/simd-select-bitmask-invalid.rs | 15 ++++++++++ .../intrinsics/simd-select-invalid-bool.rs | 15 ++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 tests/compile-fail/intrinsics/simd-select-bitmask-invalid.rs create mode 100644 tests/compile-fail/intrinsics/simd-select-invalid-bool.rs diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index b704004e16c..495ad93951f 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -669,10 +669,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let (yes, yes_len) = this.operand_to_simd(yes)?; let (no, no_len) = this.operand_to_simd(no)?; let (dest, dest_len) = this.place_to_simd(dest)?; + let bitmask_len = dest_len.max(8); assert!(mask.layout.ty.is_integral()); - assert_eq!(dest_len.max(8), mask.layout.size.bits()); - assert!(dest_len <= 64); + assert!(bitmask_len <= 64); + assert_eq!(bitmask_len, mask.layout.size.bits()); assert_eq!(dest_len, yes_len); assert_eq!(dest_len, no_len); @@ -684,7 +685,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .unwrap(); for i in 0..dest_len { let mask = - mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian)); + mask & (1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian)); let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; let dest = this.mplace_index(&dest, i)?; @@ -692,6 +693,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let val = if mask != 0 { yes } else { no }; this.write_immediate(*val, &dest.into())?; } + for i in dest_len..bitmask_len { + // If the mask is "padded", ensure that padding is all-zero. + let mask = + mask & (1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian)); + if mask != 0 { + throw_ub_format!( + "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" + ); + } + } } #[rustfmt::skip] "simd_cast" | "simd_as" => { @@ -820,16 +831,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "simd_bitmask" => { let &[ref op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; + let bitmask_len = op_len.max(8); assert!(dest.layout.ty.is_integral()); - assert_eq!(op_len.max(8), dest.layout.size.bits()); - assert!(op_len <= 64); + assert!(bitmask_len <= 64); + assert_eq!(bitmask_len, dest.layout.size.bits()); let mut res = 0u64; for i in 0..op_len { let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; if simd_element_to_bool(op)? { - res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian); + res |= 1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian); } } this.write_int(res, dest)?; @@ -1370,10 +1382,10 @@ fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool }) } -fn simd_bitmask_index(idx: u64, len: u64, endianess: Endian) -> u64 { - assert!(idx < len); +fn simd_bitmask_index(idx: u64, bitmask_len: u64, endianess: Endian) -> u64 { + assert!(idx < bitmask_len); match endianess { Endian::Little => idx, - Endian::Big => len.max(8) - 1 - idx, // reverse order of bits + Endian::Big => bitmask_len - 1 - idx, // reverse order of bits } } diff --git a/tests/compile-fail/intrinsics/simd-select-bitmask-invalid.rs b/tests/compile-fail/intrinsics/simd-select-bitmask-invalid.rs new file mode 100644 index 00000000000..ab69072c309 --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-select-bitmask-invalid.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + fn simd_select_bitmask(m: M, yes: T, no: T) -> T; +} + +#[repr(simd)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +struct i32x2(i32, i32); + +fn main() { unsafe { + let x = i32x2(0, 1); + simd_select_bitmask(0b11111111u8, x, x); //~ERROR bitmask less than 8 bits long must be filled with 0s for the remaining bits +} } diff --git a/tests/compile-fail/intrinsics/simd-select-invalid-bool.rs b/tests/compile-fail/intrinsics/simd-select-invalid-bool.rs new file mode 100644 index 00000000000..98f67cfcd7e --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-select-invalid-bool.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + fn simd_select(m: M, yes: T, no: T) -> T; +} + +#[repr(simd)] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +struct i32x2(i32, i32); + +fn main() { unsafe { + let x = i32x2(0, 1); + simd_select(x, x, x); //~ERROR must be all-0-bits or all-1-bits +} }