From e6a9b2ce6878f7944f8b414d511afdb5240965b0 Mon Sep 17 00:00:00 2001
From: Noah Lev <camelidcamel@gmail.com>
Date: Sun, 3 Oct 2021 16:28:41 -0700
Subject: [PATCH] Update Miri for detecting uninitialized numbers

This commit adds a `-Zmiri-check-number-initialization` flag to check
that integers and floats are initialized.

This commit also changes some shims to write at type `MaybeUninit<...>`
in order to prevent spurious errors from the uninit check.
---
 src/bin/miri.rs         |  3 +++
 src/eval.rs             |  3 +++
 src/machine.rs          |  9 +++++++++
 src/shims/posix/sync.rs | 29 +++++++++++++++++++++++------
 4 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/src/bin/miri.rs b/src/bin/miri.rs
index 84e66db2a7d..cf32e633226 100644
--- a/src/bin/miri.rs
+++ b/src/bin/miri.rs
@@ -313,6 +313,9 @@ fn main() {
                 "-Zmiri-symbolic-alignment-check" => {
                     miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
                 }
+                "-Zmiri-check-number-validity" => {
+                    miri_config.check_number_validity = true;
+                }
                 "-Zmiri-disable-abi-check" => {
                     miri_config.check_abi = false;
                 }
diff --git a/src/eval.rs b/src/eval.rs
index 5d8d332fcd9..ac32af30c1d 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -66,6 +66,8 @@ pub struct MiriConfig {
     pub stacked_borrows: bool,
     /// Controls alignment checking.
     pub check_alignment: AlignmentCheck,
+    /// Controls integer and float validity (e.g., initialization) checking.
+    pub check_number_validity: bool,
     /// Controls function [ABI](Abi) checking.
     pub check_abi: bool,
     /// Action for an op requiring communication with the host.
@@ -104,6 +106,7 @@ impl Default for MiriConfig {
             validate: true,
             stacked_borrows: true,
             check_alignment: AlignmentCheck::Int,
+            check_number_validity: false,
             check_abi: true,
             isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
             ignore_leaks: false,
diff --git a/src/machine.rs b/src/machine.rs
index 23278a4891f..0ead28b36c7 100644
--- a/src/machine.rs
+++ b/src/machine.rs
@@ -304,6 +304,9 @@ pub struct Evaluator<'mir, 'tcx> {
     /// Whether to enforce the validity invariant.
     pub(crate) validate: bool,
 
+    /// Whether to enforce validity (e.g., initialization) of integers and floats.
+    pub(crate) enforce_number_validity: bool,
+
     /// Whether to enforce [ABI](Abi) of function calls.
     pub(crate) enforce_abi: bool,
 
@@ -356,6 +359,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
             tls: TlsData::default(),
             isolated_op: config.isolated_op,
             validate: config.validate,
+            enforce_number_validity: config.check_number_validity,
             enforce_abi: config.check_abi,
             file_handler: Default::default(),
             dir_handler: Default::default(),
@@ -426,6 +430,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
         ecx.machine.validate
     }
 
+    #[inline(always)]
+    fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        ecx.machine.enforce_number_validity
+    }
+
     #[inline(always)]
     fn enforce_abi(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
         ecx.machine.enforce_abi
diff --git a/src/shims/posix/sync.rs b/src/shims/posix/sync.rs
index 782765c7784..606f58a207e 100644
--- a/src/shims/posix/sync.rs
+++ b/src/shims/posix/sync.rs
@@ -1,5 +1,8 @@
 use std::time::SystemTime;
 
+use rustc_hir::LangItem;
+use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, subst::Subst, Ty};
+
 use crate::*;
 use thread::Time;
 
@@ -44,7 +47,7 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
     attr_op: &OpTy<'tcx, Tag>,
     kind: impl Into<ScalarMaybeUninit<Tag>>,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset(attr_op, 0, kind, ecx.machine.layouts.i32)
+    ecx.write_scalar_at_offset(attr_op, 0, kind, layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32))
 }
 
 // pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
@@ -79,7 +82,7 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
         mutex_op,
         offset,
         kind,
-        ecx.machine.layouts.i32,
+        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
         AtomicWriteOp::Relaxed,
     )
 }
@@ -100,7 +103,7 @@ fn mutex_set_id<'mir, 'tcx: 'mir>(
         mutex_op,
         4,
         id,
-        ecx.machine.layouts.u32,
+        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
         AtomicWriteOp::Relaxed,
     )
 }
@@ -144,7 +147,7 @@ fn rwlock_set_id<'mir, 'tcx: 'mir>(
         rwlock_op,
         4,
         id,
-        ecx.machine.layouts.u32,
+        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
         AtomicWriteOp::Relaxed,
     )
 }
@@ -211,7 +214,7 @@ fn cond_set_id<'mir, 'tcx: 'mir>(
         cond_op,
         4,
         id,
-        ecx.machine.layouts.u32,
+        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
         AtomicWriteOp::Relaxed,
     )
 }
@@ -244,7 +247,12 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>(
     cond_op: &OpTy<'tcx, Tag>,
     clock_id: impl Into<ScalarMaybeUninit<Tag>>,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset(cond_op, 8, clock_id, ecx.machine.layouts.i32)
+    ecx.write_scalar_at_offset(
+        cond_op,
+        8,
+        clock_id,
+        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
+    )
 }
 
 /// Try to reacquire the mutex associated with the condition variable after we
@@ -788,3 +796,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
         Ok(0)
     }
 }
+
+fn layout_of_maybe_uninit<'tcx>(tcx: TyCtxtAt<'tcx>, param: Ty<'tcx>) -> TyAndLayout<'tcx> {
+    let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
+    let def_ty = tcx.type_of(def_id);
+    let ty = def_ty.subst(*tcx, &[param.into()]);
+
+    let param_env = tcx.param_env(def_id);
+    tcx.layout_of(param_env.and(ty)).unwrap()
+}