From f22c7e43df0e69f2e7c15f0bcaca4834cfdea715 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 14:06:23 +0200 Subject: [PATCH] Store env vars where necessary --- src/eval_context.rs | 5 ++ src/terminator/mod.rs | 68 +++++++++++++++++-- src/value.rs | 8 +++ tests/run-pass-fullmir/foreign-fn-linkname.rs | 38 +++++++++++ 4 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass-fullmir/foreign-fn-linkname.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 30a4be7b4b86..8995a199f137 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -41,6 +41,10 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: u64, + + /// Environment variables set by `setenv` + /// Miri does not expose env vars from the host to the emulated program + pub(crate) env_vars: HashMap, Pointer>, } /// A stack frame. @@ -134,6 +138,7 @@ pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, + env_vars: HashMap::new(), } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d4e169cbcfb6..ee7e3a388198 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -670,12 +670,63 @@ fn call_c_abi( } "getenv" => { - { + let result = { let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let name = self.memory.read_c_str(name_ptr)?; - info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); + match self.env_vars.get(name) { + Some(&var) => PrimVal::Ptr(var), + None => PrimVal::Bytes(0), + } + }; + self.write_primval(dest, result, dest_ty)?; + } + + "unsetenv" => { + let mut success = None; + { + let name_ptr = args[0].read_ptr(&self.memory)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + success = Some(self.env_vars.remove(name)); + } + } + } + if let Some(old) = success { + if let Some(var) = old { + self.memory.deallocate(var)?; + } + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; + } + } + + "setenv" => { + let mut new = None; + { + let name_ptr = args[0].read_ptr(&self.memory)?; + let value_ptr = args[1].read_ptr(&self.memory)?.to_ptr()?; + let value = self.memory.read_c_str(value_ptr)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + new = Some((name.to_owned(), value.to_owned())); + } + } + } + if let Some((name, value)) = new { + // +1 for the null terminator + let value_copy = self.memory.allocate((value.len() + 1) as u64, 1)?; + self.memory.write_bytes(value_copy, &value)?; + self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?, &[0])?; + if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { + self.memory.deallocate(var)?; + } + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } "write" => { @@ -696,6 +747,12 @@ fn call_c_abi( self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; } + "strlen" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let n = self.memory.read_c_str(ptr)?.len(); + self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; + } + // Some things needed for sys::thread initialization to go through "signal" | "sigaction" | "sigaltstack" => { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -705,10 +762,11 @@ fn call_c_abi( let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); let result = match name { - 30 => 4096, // _SC_PAGESIZE + 30 => PrimVal::Bytes(4096), // _SC_PAGESIZE + 70 => PrimVal::from_i128(-1), // _SC_GETPW_R_SIZE_MAX _ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))) }; - self.write_primval(dest, PrimVal::Bytes(result), dest_ty)?; + self.write_primval(dest, result, dest_ty)?; } "mmap" => { diff --git a/src/value.rs b/src/value.rs index ef11c7a8e475..37cfea8058f4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -205,6 +205,14 @@ pub fn to_bool(self) -> EvalResult<'tcx, bool> { } } + pub fn is_null(self) -> EvalResult<'tcx, bool> { + match self { + PrimVal::Bytes(b) => Ok(b == 0), + PrimVal::Ptr(_) => Ok(false), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { diff --git a/tests/run-pass-fullmir/foreign-fn-linkname.rs b/tests/run-pass-fullmir/foreign-fn-linkname.rs new file mode 100644 index 000000000000..a9001a3cdcf6 --- /dev/null +++ b/tests/run-pass-fullmir/foreign-fn-linkname.rs @@ -0,0 +1,38 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + + +#![feature(std_misc, libc)] + +extern crate libc; +use std::ffi::CString; + +mod mlibc { + use libc::{c_char, size_t}; + + extern { + #[link_name = "strlen"] + pub fn my_strlen(str: *const c_char) -> size_t; + } +} + +fn strlen(str: String) -> usize { + // C string is terminated with a zero + let s = CString::new(str).unwrap(); + unsafe { + mlibc::my_strlen(s.as_ptr()) as usize + } +} + +pub fn main() { + let len = strlen("Rust".to_string()); + assert_eq!(len, 4); +}