use crate::cstr_util::c_str_len_ptr;
use crate::{AuxVar, AuxVarSerialized, AuxVarType};
use core::fmt::Debug;
use core::marker::PhantomData;
#[derive(Debug)]
pub struct InitialLinuxLibcStackLayout<'a> {
bytes: &'a [u8],
}
impl<'a> From<&'a [u8]> for InitialLinuxLibcStackLayout<'a> {
fn from(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}
impl<'a> InitialLinuxLibcStackLayout<'a> {
#[allow(clippy::missing_const_for_fn)]
pub fn argc(&self) -> usize {
unsafe { *self.bytes.as_ptr().cast() }
}
pub fn envc(&self) -> usize {
self.envv_ptr_iter().count()
}
fn get_argv_ptr(&self) -> *const *const u8 {
let ptr = unsafe { self.bytes.as_ptr().cast::<u64>().add(1) };
ptr as *const *const u8
}
pub unsafe fn argv_iter(&self) -> CstrIter {
CstrIter::new(self.get_argv_ptr())
}
pub fn argv_ptr_iter(&self) -> NullTerminatedArrIter {
NullTerminatedArrIter {
ptr: self.get_argv_ptr(),
}
}
fn get_envv_ptr(&self) -> *const *const u8 {
unsafe {
self.get_argv_ptr()
.add(self.argc())
.add(1)
}
}
pub unsafe fn envv_iter(&self) -> CstrIter {
CstrIter::new(self.get_envv_ptr())
}
pub fn envv_ptr_iter(&self) -> NullTerminatedArrIter {
NullTerminatedArrIter {
ptr: self.get_envv_ptr(),
}
}
pub unsafe fn aux_var_iter(&self) -> AuxVarIter {
AuxVarIter::new(self.aux_serialized_iter())
}
pub fn aux_serialized_iter(&self) -> AuxVarSerializedIter {
AuxVarSerializedIter::new(self.get_auxv_ptr())
}
fn get_auxv_ptr(&self) -> *const AuxVarSerialized {
unsafe {
self.get_envv_ptr()
.add(self.envv_ptr_iter().count())
.add(1)
.cast()
}
}
}
#[derive(Debug)]
pub struct NullTerminatedArrIter {
ptr: *const *const u8,
}
impl Iterator for NullTerminatedArrIter {
type Item = *const u8;
fn next(&mut self) -> Option<Self::Item> {
if unsafe { (*self.ptr).is_null() } {
None
} else {
let c_str_ptr = unsafe { *self.ptr };
self.ptr = unsafe { self.ptr.add(1) };
Some(c_str_ptr)
}
}
}
#[derive(Debug)]
pub struct CstrIter<'a> {
arr_iter: NullTerminatedArrIter,
_marker: PhantomData<&'a ()>,
}
impl<'a> CstrIter<'a> {
unsafe fn new(ptr: *const *const u8) -> Self {
Self {
arr_iter: NullTerminatedArrIter { ptr },
_marker: PhantomData::default(),
}
}
}
impl<'a> Iterator for CstrIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.arr_iter.next().map(|c_str_ptr| {
let c_str_bytes =
unsafe { core::slice::from_raw_parts(c_str_ptr, c_str_len_ptr(c_str_ptr) + 1) };
unsafe { core::str::from_utf8_unchecked(c_str_bytes) }
})
}
}
#[derive(Debug)]
pub struct AuxVarSerializedIter<'a> {
ptr: *const AuxVarSerialized<'a>,
done: bool,
_marker: PhantomData<&'a ()>,
}
impl<'a> AuxVarSerializedIter<'a> {
fn new(ptr: *const AuxVarSerialized<'a>) -> Self {
Self {
ptr,
done: false,
_marker: PhantomData::default(),
}
}
}
impl<'a> Iterator for AuxVarSerializedIter<'a> {
type Item = AuxVarSerialized<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
None
} else {
let aux_var_ser = unsafe { self.ptr.as_ref().unwrap() };
if aux_var_ser.key() == AuxVarType::Null {
if aux_var_ser.val() != 0 {
panic!(
"val of end key is not null but {}! Probably read wrong memory!",
aux_var_ser.val()
);
}
self.done = true;
}
self.ptr = unsafe { self.ptr.add(1) };
Some(*aux_var_ser)
}
}
}
#[derive(Debug)]
pub struct AuxVarIter<'a> {
serialized_iter: AuxVarSerializedIter<'a>,
}
impl<'a> AuxVarIter<'a> {
const fn new(serialized_iter: AuxVarSerializedIter<'a>) -> Self {
Self { serialized_iter }
}
}
impl<'a> Iterator for AuxVarIter<'a> {
type Item = AuxVar<'a>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
self.serialized_iter
.next()
.map(|ref x| AuxVar::from_serialized(x))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AuxVar, InitialLinuxLibcStackLayoutBuilder};
use std::vec::Vec;
#[test]
fn test_parser_with_dereference_data() {
let builder = InitialLinuxLibcStackLayoutBuilder::new()
.add_arg_v("first_arg\0")
.add_arg_v("second_arg")
.add_arg_v("third__arg")
.add_env_v("ENV1=FOO")
.add_env_v("ENV2=BAR")
.add_env_v("ENV3=FOOBAR\0")
.add_aux_v(AuxVar::Platform("x86_64"))
.add_aux_v(AuxVar::Uid(0xdeadbeef));
let mut buf = vec![0; builder.total_size()];
unsafe {
let user_ptr = buf.as_ptr() as u64;
builder.serialize_into_buf(buf.as_mut_slice(), user_ptr);
}
let parsed = InitialLinuxLibcStackLayout::from(buf.as_slice());
dbg!(parsed.argc());
dbg!(parsed.argv_ptr_iter().collect::<Vec<_>>());
unsafe {
dbg!(parsed.argv_iter().collect::<Vec<_>>());
}
dbg!(parsed.envv_ptr_iter().collect::<Vec<_>>());
unsafe {
dbg!(parsed.envv_iter().collect::<Vec<_>>());
}
dbg!(parsed.aux_serialized_iter().collect::<Vec<_>>());
}
#[test]
fn test_parser_different_user_ptr() {
let builder = InitialLinuxLibcStackLayoutBuilder::new()
.add_arg_v("first_arg\0")
.add_arg_v("second_arg")
.add_arg_v("third__arg")
.add_env_v("ENV1=FOO")
.add_env_v("ENV2=BAR")
.add_env_v("ENV3=FOOBAR\0")
.add_aux_v(AuxVar::Platform("x86_64\0"))
.add_aux_v(AuxVar::Uid(0xdeadbeef));
let mut buf = Vec::with_capacity(builder.total_size());
#[allow(clippy::uninit_vec)]
unsafe {
buf.set_len(buf.capacity());
buf.fill(0);
}
unsafe {
builder.serialize_into_buf(buf.as_mut_slice(), 0x1000);
}
let parsed = InitialLinuxLibcStackLayout::from(buf.as_slice());
dbg!(parsed.argv_ptr_iter().collect::<Vec<_>>());
dbg!(parsed.envv_ptr_iter().collect::<Vec<_>>());
}
}