add safe wrapper to read union fields

This commit is contained in:
Jianfeng Jiang 2022-11-23 18:13:40 +08:00
parent 41b79cf823
commit 8ebaaa283a
13 changed files with 274 additions and 36 deletions

1
src/Cargo.lock generated
View File

@ -144,6 +144,7 @@ dependencies = [
"jinux-frame",
"jinux-pci",
"jinux-rights-proc",
"jinux-util",
"jinux-virtio",
"lazy_static",
"pod",

View File

@ -176,9 +176,7 @@ pub(crate) fn init() {
// set gate type to 1110: 64 bit Interrupt Gate, Present bit to 1, DPL to Ring 0
let p_low = (((p >> 16) & 0xFFFF) << 48) | (p & 0xFFFF);
let trap_entry_option: usize = 0b1000_1110_0000_0000;
let low = (trap_entry_option << 32)
| ((kcs.0 as usize) << 16)
| p_low;
let low = (trap_entry_option << 32) | ((kcs.0 as usize) << 16) | p_low;
let high = p >> 32;
unsafe {
IDT.entries[i] = [low, high];

View File

@ -33,3 +33,34 @@ macro_rules! offset_of {
field_offset
});
}
/// Get the offset of a field within an object as a pointer.
///
/// ```rust
/// #[repr(C)]
/// pub struct Foo {
/// first: u8,
/// second: u32,
/// }
/// let foo = &Foo {first: 0, second: 0};
/// assert!(value_offset!(foo) == (0 as *const Foo));
/// assert!(value_offset!(foo.first) == (0 as *const u8));
/// assert!(value_offset!(foo.second) == (4 as *const u32));
/// ```
#[macro_export]
macro_rules! value_offset {
($container:ident) => ({
let container_addr = &*$container as *const _;
let offset = 0 as *const _;
let _: bool = offset == container_addr;
offset
});
($container:ident.$($field:ident).*) => ({
let container_addr = &*$container as *const _;
// SAFETY: This is safe since we never access the field
let field_addr = unsafe {&($container.$($field).*)} as *const _;
let field_offset = (field_addr as usize- container_addr as usize) as *const _;
let _: bool = field_offset == field_addr;
field_offset
});
}

View File

@ -81,10 +81,7 @@ impl VmSpace {
let page_size = (range.end - range.start) / PAGE_SIZE;
let mut inner = self.memory_set.lock();
for i in 0..page_size {
let res = inner.unmap(start_va);
if res.is_err() {
return res;
}
inner.unmap(start_va)?;
start_va += PAGE_SIZE;
}
Ok(())
@ -224,6 +221,6 @@ impl TryFrom<u64> for VmPerm {
type Error = Error;
fn try_from(value: u64) -> Result<Self> {
VmPerm::from_bits(value as u8).ok_or_else(|| Error::InvalidVmpermBits)
VmPerm::from_bits(value as u8).ok_or(Error::InvalidVmpermBits)
}
}

View File

@ -7,7 +7,10 @@
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, Generics};
use syn::{
parse_macro_input, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Fields,
Generics,
};
#[proc_macro_derive(Pod)]
pub fn derive_pod(input_token: proc_macro::TokenStream) -> proc_macro::TokenStream {
@ -15,8 +18,8 @@ pub fn derive_pod(input_token: proc_macro::TokenStream) -> proc_macro::TokenStre
expand_derive_pod(input).into()
}
const ALLOWED_REPRS: [&'static str; 13] = [
"C", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize", "u128", "i128",
const ALLOWED_REPRS: [&'static str; 11] = [
"C", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize",
];
fn expand_derive_pod(input: DeriveInput) -> TokenStream {
@ -25,7 +28,8 @@ fn expand_derive_pod(input: DeriveInput) -> TokenStream {
let generics = input.generics;
match input.data {
Data::Struct(data_struct) => impl_pod_for_struct(data_struct, generics, ident, attrs),
Data::Enum(..) | Data::Union(..) => impl_pod_for_enum_or_union(attrs, generics, ident),
Data::Union(data_union) => impl_pod_for_union(data_union, generics, ident, attrs),
Data::Enum(data_enum) => impl_pod_for_enum(data_enum, attrs, generics, ident),
}
}
@ -72,7 +76,45 @@ fn impl_pod_for_struct(
}
}
fn impl_pod_for_enum_or_union(
fn impl_pod_for_union(
data_union: DataUnion,
generics: Generics,
ident: Ident,
attrs: Vec<Attribute>,
) -> TokenStream {
if !has_valid_repr(attrs) {
panic!("{} has invalid repr to implement Pod", ident.to_string());
}
let fields = data_union.fields.named;
// deal with generics
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let pod_where_predicates = fields
.into_iter()
.map(|field| {
let field_ty = field.ty;
quote! {
#field_ty: ::pod::Pod
}
})
.collect::<Vec<_>>();
// if where_clause is none, we should add a `where` word manually.
if where_clause.is_none() {
quote! {
#[automatically_derived]
unsafe impl #impl_generics ::pod::Pod #type_generics for #ident where #(#pod_where_predicates),* {}
}
} else {
quote! {
#[automatically_derived]
unsafe impl #impl_generics ::pod::Pod #type_generics for #ident #where_clause, #(#pod_where_predicates),* {}
}
}
}
fn impl_pod_for_enum(
data_enum: DataEnum,
attrs: Vec<Attribute>,
generics: Generics,
ident: Ident,
@ -84,6 +126,13 @@ fn impl_pod_for_enum_or_union(
);
}
// check variant
for variant in data_enum.variants {
if None == variant.discriminant {
panic!("Enum can only have fields like Variant=1");
}
}
// deal with generics
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
quote! {

View File

@ -1,6 +1,6 @@
#![no_std]
use core::{fmt::Debug, mem::MaybeUninit};
use core::mem::MaybeUninit;
/// A marker trait for plain old data (POD).
///
@ -16,7 +16,7 @@ use core::{fmt::Debug, mem::MaybeUninit};
/// # Safety
///
/// Marking a non-POD type as POD may cause undefined behaviors.
pub unsafe trait Pod: Copy + Sized + Debug {
pub unsafe trait Pod: Copy + Sized {
/// Creates a new instance of Pod type that is filled with zeroes.
fn new_zeroed() -> Self {
// SAFETY. An all-zero value of `T: Pod` is always valid.
@ -35,7 +35,8 @@ pub unsafe trait Pod: Copy + Sized + Debug {
/// Creates a new instance from the given bytes.
fn from_bytes(bytes: &[u8]) -> Self {
let mut new_self = Self::new_uninit();
new_self.as_bytes_mut().copy_from_slice(bytes);
let copy_len = new_self.as_bytes().len();
new_self.as_bytes_mut().copy_from_slice(&bytes[..copy_len]);
new_self
}

View File

@ -14,6 +14,7 @@ jinux-virtio = {path="../../comps/jinux-virtio"}
typeflags = {path="../typeflags"}
typeflags-util = {path="../typeflags-util"}
jinux-rights-proc = {path="../jinux-rights-proc"}
jinux-util = {path="../jinux-util"}
# parse elf file
xmas-elf = "0.8.0"

View File

@ -11,7 +11,10 @@
#![feature(const_option)]
#![feature(extend_one)]
use crate::{prelude::*, user_apps::UserApp};
use crate::{
prelude::*,
user_apps::{get_busybox_app, UserApp},
};
use jinux_frame::{info, println};
use process::Process;
@ -61,7 +64,12 @@ pub fn init_process() {
process.pid()
);
for app in get_all_apps().into_iter().last() {
// FIXME: should be running this apps before we running shell?
println!("");
println!("[kernel] Running test programs");
println!("");
// Run test apps
for app in get_all_apps().into_iter() {
let UserApp {
app_name,
app_content,
@ -69,11 +77,20 @@ pub fn init_process() {
envp,
} = app;
info!("[jinux-std/lib.rs] spwan {:?} process", app_name);
print!("\n");
print!("BusyBox v1.35.0 built-in shell (ash)\n\n");
Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new());
}
// Run busybox ash
let UserApp {
app_name,
app_content,
argv,
envp,
} = get_busybox_app();
println!("");
println!("BusyBox v1.35.0 built-in shell (ash)\n");
Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new());
loop {
// We don't have preemptive scheduler now.
// The long running init process should yield its own execution to allow other tasks to go on.
@ -84,7 +101,6 @@ pub fn init_process() {
/// first process never return
pub fn run_first_process() -> ! {
// let elf_file_content = read_hello_world_content();
Process::spawn_kernel_process(init_process);
unreachable!()
}

View File

@ -1,13 +1,16 @@
#![allow(non_camel_case_types)]
use core::mem;
use jinux_frame::cpu::GpRegs;
use jinux_frame::{cpu::GpRegs, offset_of};
use jinux_util::{read_union_fields, union_read_ptr::UnionReadPtr};
use crate::prelude::*;
use crate::{prelude::*, process::Pid};
use super::sig_num::SigNum;
use super::{sig_num::SigNum, signals::user::Uid};
pub type sigset_t = u64;
// FIXME: this type should be put at suitable place
pub type clock_t = i64;
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
@ -18,7 +21,7 @@ pub struct sigaction_t {
pub mask: sigset_t,
}
#[derive(Debug, Clone, Copy, Pod)]
#[derive(Clone, Copy, Pod)]
#[repr(C)]
pub struct siginfo_t {
pub si_signo: i32,
@ -27,20 +30,116 @@ pub struct siginfo_t {
_padding: i32,
/// siginfo_fields should be a union type ( See occlum definition ). But union type have unsafe interfaces.
/// Here we use a simple byte array.
pub siginfo_fields: [u8; 128 - mem::size_of::<i32>() * 4],
siginfo_fields: siginfo_fields_t,
}
impl siginfo_t {
pub fn new(num: SigNum, code: i32) -> Self {
let zero_fields = [0u8; 128 - mem::size_of::<i32>() * 4];
siginfo_t {
si_signo: num.as_u8() as i32,
si_errno: 0,
si_code: code,
_padding: 0,
siginfo_fields: zero_fields,
siginfo_fields: siginfo_fields_t::zero_fields(),
}
}
pub fn set_si_addr(&mut self, si_addr: Vaddr) {
self.siginfo_fields.sigfault.addr = si_addr;
}
pub fn si_addr(&self) -> Vaddr {
// let siginfo = *self;
read_union_fields!(self.siginfo_fields.sigfault.addr)
}
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_fields_t {
bytes: [u8; 128 - mem::size_of::<i32>() * 4],
common: siginfo_common_t,
sigfault: siginfo_sigfault_t,
}
impl siginfo_fields_t {
fn zero_fields() -> Self {
Self {
bytes: [0; 128 - mem::size_of::<i32>() * 4],
}
}
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_common_t {
first: siginfo_common_first_t,
second: siginfo_common_second_t,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_common_first_t {
piduid: siginfo_piduid_t,
timer: siginfo_timer_t,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
struct siginfo_piduid_t {
pid: Pid,
uid: Uid,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
struct siginfo_timer_t {
timerid: i32,
overrun: i32,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_common_second_t {
value: sigval_t,
sigchild: siginfo_sigchild_t,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
pub union sigval_t {
sigval_int: i32,
sigval_ptr: Vaddr, //*mut c_void
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_sigchild_t {
status: i32,
utime: clock_t,
stime: clock_t,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
struct siginfo_sigfault_t {
addr: Vaddr, //*const c_void
addr_lsb: i16,
first: siginfo_sigfault_first_t,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_sigfault_first_t {
addr_bnd: siginfo_addr_bnd_t,
pkey: u32,
}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
union siginfo_addr_bnd_t {
lower: Vaddr, // *const c_void
upper: Vaddr, // *const c_void,
}
#[derive(Clone, Copy, Debug, Pod)]

View File

@ -56,6 +56,10 @@ pub fn get_all_apps() -> Vec<UserApp> {
let signal_test = UserApp::new("/signal_test", read_signal_test_content());
res.push(signal_test);
res
}
pub fn get_busybox_app() -> UserApp {
// busybox
let mut busybox = UserApp::new("/busybox", read_busybox_content());
// -l option means the busybox is running as logging shell
@ -74,9 +78,7 @@ pub fn get_all_apps() -> Vec<UserApp> {
let envp = to_vec_cstring(&envp).unwrap();
busybox.set_argv(argv);
busybox.set_envp(envp);
res.push(busybox);
res
busybox
}
fn read_hello_world_content() -> &'static [u8] {

View File

@ -18,7 +18,7 @@ use super::{Pager, Vmo, VmoFlags};
///
/// Creating a VMO as a _dynamic_ capability with full access rights:
/// ```
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
/// use jinux_std::vm::{PAGE_SIZE, VmoOptions};
///
/// let vmo = VmoOptions::new(PAGE_SIZE)
/// .alloc()
@ -28,7 +28,7 @@ use super::{Pager, Vmo, VmoFlags};
/// Creating a VMO as a _static_ capability with all access rights:
/// ```
/// use jinux_std::prelude::*;
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
/// use jinux_std::vm::{PAGE_SIZE, VmoOptions};
///
/// let vmo = VmoOptions::<Full>::new(PAGE_SIZE)
/// .alloc()
@ -118,7 +118,7 @@ impl<R: TRights> VmoOptions<R> {
/// A child VMO created from a parent VMO of _dynamic_ capability is also a
/// _dynamic_ capability.
/// ```
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
/// use jinux_std::vm::{PAGE_SIZE, VmoOptions};
///
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
/// .alloc()
@ -150,7 +150,7 @@ impl<R: TRights> VmoOptions<R> {
/// right regardless of whether the parent is writable or not.
///
/// ```
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions, VmoChildOptions};
/// use jinux_std::vm::{PAGE_SIZE, VmoOptions, VmoChildOptions};
///
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
/// .alloc()
@ -181,7 +181,7 @@ impl<R: TRights> VmoOptions<R> {
/// Note that a slice VMO child and its parent cannot not be resizable.
///
/// ```rust
/// use kxo_std::vm::{PAGE_SIZE, VmoOptions};
/// use _std::vm::{PAGE_SIZE, VmoOptions};
///
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
/// .alloc()

View File

@ -3,3 +3,4 @@
#![forbid(unsafe_code)]
pub mod frame_ptr;
pub mod union_read_ptr;

View File

@ -0,0 +1,42 @@
use core::marker::PhantomData;
use pod::Pod;
/// This ptr is designed to read union field safely.
/// Write to union field is safe operation. While reading union field is UB.
/// However, if this field is Pod, we can safely read that field.
pub struct UnionReadPtr<'a, T: Pod> {
bytes: &'a [u8],
marker: PhantomData<&'a mut T>,
}
impl<'a, T: Pod> UnionReadPtr<'a, T> {
pub fn new(object: &'a T) -> Self {
let bytes = object.as_bytes();
Self {
bytes,
marker: PhantomData,
}
}
pub fn read_at<F: Pod>(&self, offset: *const F) -> F {
let offset = offset as usize;
F::from_bytes(&self.bytes[offset..])
}
}
/// FIXME: This macro requires the first argument to be a `reference` of a Pod type.
/// This is because the value_offset macro requires
#[macro_export]
macro_rules! read_union_fields {
($container:ident) => ({
let offset = value_offset!($container);
let union_read_ptr = UnionReadPtr::new(&*$container);
union_read_ptr.read_at(offset)
});
($container:ident.$($field:ident).*) => ({
let field_offset = jinux_frame::value_offset!($container.$($field).*);
let union_read_ptr = UnionReadPtr::new(&*$container);
union_read_ptr.read_at(field_offset)
});
}