Skip to content

Commit db1d38e

Browse files
LibAFL_QEMU/librasan: Add support for reading environment (#3241)
* Add support for reading environment * Fix clippy * Review fixes
1 parent d7eb3bd commit db1d38e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+506
-116
lines changed

libafl_qemu/librasan/Justfile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fix: fix_asan fix_dummy fix_fuzz fix_gasan fix_qasan fix_runner fix_zasan
2626

2727
clippy:
2828
#!/bin/sh
29-
cargo clippy
29+
cargo clippy -F test
3030

3131
doc:
3232
#!/bin/sh
@@ -52,6 +52,8 @@ build_i386_dev:
5252

5353
build_ppc_dev:
5454
#!/bin/sh
55+
RUSTC_BOOTSTRAP=1 \
56+
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
5557
ARCH=ppc PROFILE=dev just build
5658

5759
build_arm_release:
@@ -72,6 +74,8 @@ build_i386_release:
7274

7375
build_ppc_release:
7476
#!/bin/sh
77+
RUSTC_BOOTSTRAP=1 \
78+
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
7579
ARCH=ppc PROFILE=release just build
7680

7781
build_everything_dev: \
@@ -92,6 +96,8 @@ build_everything: build_everything_dev build_everything_release
9296

9397
test_arm:
9498
#!/bin/sh
99+
RUSTC_BOOTSTRAP=1 \
100+
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
95101
ARCH=arm \
96102
PROFILE=dev \
97103
RUSTLOG=debug \
@@ -124,6 +130,8 @@ test_i386:
124130

125131
test_ppc:
126132
#!/bin/sh
133+
RUSTC_BOOTSTRAP=1 \
134+
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
127135
ARCH=ppc \
128136
PROFILE=dev \
129137
RUSTLOG=debug \
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use alloc::{
2+
fmt::Debug,
3+
string::{String, ToString},
4+
vec,
5+
};
6+
use core::{hash::BuildHasherDefault, marker::PhantomData};
7+
8+
use ahash::AHasher;
9+
use hashbrown::HashMap;
10+
use log::Level;
11+
use thiserror::Error;
12+
13+
use crate::file::FileReader;
14+
15+
type Hasher = BuildHasherDefault<AHasher>;
16+
17+
#[derive(Debug)]
18+
pub struct Env<R: FileReader> {
19+
envs: HashMap<String, String, Hasher>,
20+
phantom: PhantomData<R>,
21+
}
22+
23+
impl<R: FileReader> Env<R> {
24+
/* Environment variable buffer size, should be larger than single largest env variable */
25+
const BUFFER_SIZE: usize = 131072;
26+
/* Expected maximum number of environment variables to initialize hash table */
27+
const MAX_ENVS: usize = 4096;
28+
29+
pub fn initialize() -> Result<Env<R>, EnvError<R>> {
30+
let mut reader = R::new(c"/proc/self/environ").map_err(EnvError::FailedToCreateReader)?;
31+
32+
let mut buffer = vec![0u8; Self::BUFFER_SIZE];
33+
34+
let mut envs = HashMap::<String, String, Hasher>::with_capacity_and_hasher(
35+
Self::MAX_ENVS,
36+
Hasher::default(),
37+
);
38+
39+
let mut start = 0;
40+
let mut bytes_read = 0;
41+
loop {
42+
let skip = bytes_read - start;
43+
start = 0;
44+
bytes_read = reader
45+
.read(&mut buffer[skip..])
46+
.map_err(EnvError::FailedToRead)?
47+
+ skip;
48+
49+
let mut i = 0;
50+
51+
while i < bytes_read {
52+
if buffer[i] == 0 {
53+
if i == start {
54+
/* We found the null string at the end */
55+
return Ok(Env {
56+
envs,
57+
phantom: PhantomData,
58+
});
59+
}
60+
61+
let pair = String::from_utf8_lossy(&buffer[start..i]).to_string();
62+
log::debug!("pair: {pair}");
63+
64+
let (key, value) = pair
65+
.split_once('=')
66+
.map(|(k, v)| (k.to_string(), v.to_string()))
67+
.ok_or(EnvError::Split(pair))?;
68+
log::debug!("key: {key} value: {value}");
69+
70+
envs.insert(key, value);
71+
start = i + 1;
72+
}
73+
i += 1;
74+
}
75+
buffer.copy_within(start..bytes_read, 0);
76+
77+
if bytes_read == 0 {
78+
if start == bytes_read {
79+
break;
80+
}
81+
let fragment = String::from_utf8_lossy(&buffer[start..bytes_read]).to_string();
82+
return Err(EnvError::StringFragment(fragment));
83+
}
84+
}
85+
Ok(Env {
86+
envs,
87+
phantom: PhantomData,
88+
})
89+
}
90+
91+
pub fn get(&self, name: &str) -> Option<&str> {
92+
self.envs.get(name).map(|s| s.as_str())
93+
}
94+
95+
pub fn log_level(&self) -> Option<Level> {
96+
self.get("RUST_LOG").and_then(|s| s.parse().ok())
97+
}
98+
}
99+
100+
impl<R: FileReader> IntoIterator for Env<R> {
101+
type Item = (String, String);
102+
type IntoIter = hashbrown::hash_map::IntoIter<String, String>;
103+
104+
fn into_iter(self) -> Self::IntoIter {
105+
self.envs.into_iter()
106+
}
107+
}
108+
109+
#[derive(Error, Debug, PartialEq)]
110+
pub enum EnvError<R: FileReader> {
111+
#[error("Failed to create reader: {0}")]
112+
FailedToCreateReader(R::Error),
113+
#[error("Failed to read: {0}")]
114+
FailedToRead(R::Error),
115+
#[error("Failed to split")]
116+
Split(String),
117+
#[error("String framgent: {0}")]
118+
StringFragment(String),
119+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use core::{
2+
ffi::{CStr, c_char, c_int},
3+
marker::PhantomData,
4+
};
5+
6+
use libc::{O_NONBLOCK, O_RDONLY};
7+
use log::trace;
8+
use thiserror::Error;
9+
10+
use crate::{
11+
asan_swap,
12+
file::FileReader,
13+
size_t, ssize_t,
14+
symbols::{AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols},
15+
};
16+
17+
#[derive(Debug)]
18+
struct FunctionOpen;
19+
20+
impl Function for FunctionOpen {
21+
const NAME: &CStr = c"open";
22+
type Func = unsafe extern "C" fn(*const c_char, c_int, c_int) -> c_int;
23+
}
24+
25+
#[derive(Debug)]
26+
struct FunctionClose;
27+
28+
impl Function for FunctionClose {
29+
const NAME: &CStr = c"close";
30+
type Func = unsafe extern "C" fn(c_int) -> c_int;
31+
}
32+
33+
#[derive(Debug)]
34+
struct FunctionRead;
35+
36+
impl Function for FunctionRead {
37+
const NAME: &CStr = c"read";
38+
type Func = unsafe extern "C" fn(c_int, *mut c_char, size_t) -> ssize_t;
39+
}
40+
41+
#[derive(Debug)]
42+
struct FunctionErrnoLocation;
43+
44+
impl Function for FunctionErrnoLocation {
45+
const NAME: &CStr = c"__errno_location";
46+
type Func = unsafe extern "C" fn() -> *mut c_int;
47+
}
48+
49+
static OPEN_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
50+
static CLOSE_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
51+
static READ_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
52+
static GET_ERRNO_LOCATION_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
53+
54+
#[derive(Debug)]
55+
pub struct LibcFileReader<S: Symbols> {
56+
fd: c_int,
57+
_phantom: PhantomData<S>,
58+
}
59+
60+
impl<S: Symbols> LibcFileReader<S> {
61+
fn get_open() -> Result<<FunctionOpen as Function>::Func, LibcFileReaderError<S>> {
62+
let addr = OPEN_ADDR.try_get_or_insert_with(|| {
63+
S::lookup(FunctionOpen::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
64+
})?;
65+
let f =
66+
FunctionOpen::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
67+
Ok(f)
68+
}
69+
70+
fn get_close() -> Result<<FunctionClose as Function>::Func, LibcFileReaderError<S>> {
71+
let addr = CLOSE_ADDR.try_get_or_insert_with(|| {
72+
S::lookup(FunctionClose::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
73+
})?;
74+
let f =
75+
FunctionClose::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
76+
Ok(f)
77+
}
78+
79+
fn get_read() -> Result<<FunctionRead as Function>::Func, LibcFileReaderError<S>> {
80+
let addr = READ_ADDR.try_get_or_insert_with(|| {
81+
S::lookup(FunctionRead::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
82+
})?;
83+
let f =
84+
FunctionRead::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
85+
Ok(f)
86+
}
87+
88+
fn get_errno_location()
89+
-> Result<<FunctionErrnoLocation as Function>::Func, LibcFileReaderError<S>> {
90+
let addr = GET_ERRNO_LOCATION_ADDR.try_get_or_insert_with(|| {
91+
S::lookup(FunctionErrnoLocation::NAME)
92+
.map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
93+
})?;
94+
let f = FunctionErrnoLocation::as_ptr(addr)
95+
.map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
96+
Ok(f)
97+
}
98+
99+
fn errno() -> Result<c_int, LibcFileReaderError<S>> {
100+
unsafe { asan_swap(false) };
101+
let errno_location = Self::get_errno_location()?;
102+
unsafe { asan_swap(true) };
103+
let errno = unsafe { *errno_location() };
104+
Ok(errno)
105+
}
106+
}
107+
108+
impl<S: Symbols> FileReader for LibcFileReader<S> {
109+
type Error = LibcFileReaderError<S>;
110+
fn new(path: &CStr) -> Result<LibcFileReader<S>, Self::Error> {
111+
let fn_open = Self::get_open()?;
112+
unsafe { asan_swap(false) };
113+
let fd = unsafe { fn_open(path.as_ptr() as *const c_char, O_NONBLOCK | O_RDONLY, 0) };
114+
unsafe { asan_swap(true) };
115+
if fd < 0 {
116+
let errno = Self::errno().unwrap();
117+
return Err(LibcFileReaderError::FailedToOpen(errno));
118+
}
119+
Ok(LibcFileReader {
120+
fd,
121+
_phantom: PhantomData,
122+
})
123+
}
124+
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
125+
let fn_read = Self::get_read()?;
126+
unsafe { asan_swap(false) };
127+
let ret = unsafe { fn_read(self.fd, buf.as_mut_ptr() as *mut c_char, buf.len()) };
128+
unsafe { asan_swap(true) };
129+
if ret < 0 {
130+
let errno = Self::errno().unwrap();
131+
return Err(LibcFileReaderError::FailedToRead(self.fd, errno));
132+
}
133+
Ok(ret as usize)
134+
}
135+
}
136+
137+
impl<S: Symbols> Drop for LibcFileReader<S> {
138+
fn drop(&mut self) {
139+
let fn_close = Self::get_close().unwrap();
140+
unsafe { asan_swap(false) };
141+
let ret = unsafe { fn_close(self.fd) };
142+
unsafe { asan_swap(true) };
143+
if ret < 0 {
144+
let errno = Self::errno().unwrap();
145+
panic!("Failed to close: {}, Errno: {}", self.fd, errno);
146+
}
147+
trace!("Closed fd: {}", self.fd);
148+
}
149+
}
150+
151+
#[derive(Error, Debug, PartialEq)]
152+
pub enum LibcFileReaderError<S: Symbols> {
153+
#[error("Failed to find mmap functions")]
154+
FailedToFindSymbol(S::Error),
155+
#[error("Invalid pointer type: {0:?}")]
156+
InvalidPointerType(FunctionPointerError),
157+
#[error("Failed to read - fd: {0}, errno: {1}")]
158+
FailedToRead(c_int, c_int),
159+
#[error("Failed to open - errno: {0}")]
160+
FailedToOpen(c_int),
161+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use core::ffi::CStr;
2+
3+
use rustix::{
4+
fd::OwnedFd,
5+
fs::{Mode, OFlags, open},
6+
io::{Errno, read},
7+
};
8+
use thiserror::Error;
9+
10+
use crate::file::FileReader;
11+
12+
#[derive(Debug)]
13+
pub struct LinuxFileReader {
14+
fd: OwnedFd,
15+
}
16+
17+
impl FileReader for LinuxFileReader {
18+
type Error = LinuxFileReaderError;
19+
fn new(path: &'static CStr) -> Result<LinuxFileReader, Self::Error> {
20+
let fd = open(path, OFlags::RDONLY | OFlags::NONBLOCK, Mode::empty())
21+
.map_err(LinuxFileReaderError::FailedToOpen)?;
22+
23+
Ok(LinuxFileReader { fd })
24+
}
25+
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
26+
read(&self.fd, buf).map_err(LinuxFileReaderError::FailedToRead)
27+
}
28+
}
29+
30+
#[derive(Error, Debug, PartialEq)]
31+
pub enum LinuxFileReaderError {
32+
#[error("Failed to open - errno: {0}")]
33+
FailedToOpen(Errno),
34+
#[error("Failed to read - errno: {0}")]
35+
FailedToRead(Errno),
36+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use alloc::fmt::Debug;
2+
use core::ffi::CStr;
3+
4+
#[cfg(feature = "libc")]
5+
pub mod libc;
6+
7+
#[cfg(feature = "linux")]
8+
pub mod linux;
9+
10+
pub trait FileReader: Debug + Send + Sized {
11+
type Error: Debug;
12+
fn new(path: &'static CStr) -> Result<Self, Self::Error>;
13+
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
14+
}

libafl_qemu/librasan/asan/src/hooks/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl PatchedHook {
8989
}
9090

9191
pub fn lookup<S: Symbols>(&self) -> Result<GuestAddr, S::Error> {
92-
S::lookup(self.name.as_ptr() as *const c_char)
92+
unsafe { S::lookup_raw(self.name.as_ptr() as *const c_char) }
9393
}
9494
}
9595

0 commit comments

Comments
 (0)