Merge upstream/main (59cf8189) into submodule/base
Sync the base fork with 13 upstream commits (xhci event-processing dedup + IRQ race fix, randd permission simplification, nvmed TimeSpec fix, /dev/ptmx, fpath legacy-path cleanup, dynamically-linked init fix, and more). Brings process_one_event into the tree, satisfying the verify-fork-functions gate for base. Conflicts resolved (3 of 8 overlapping files; 5 auto-merged): - drivers/usb/xhcid/src/xhci/irq_reactor.rs: took upstream's process_one_event/EventProcessResult refactor (b2ed85ea) including thea01d3ce6race fix (process events between NoEvent and unmasking), and re-applied the Red Bear SPURIOUS_REBOOT quirk on the NoEvent warning (downgrade to debug when quirked). - randd/src/main.rs: took upstream's permission-handling simplification (e26db606); re-applied the RB fcntl improvement (F_GETFL/F_GETFD/F_SETFL/F_SETFD handling, Linux random_fops xref) and the is_cpu_feature_detected early-return cleanup. Dropped test_scheme_perms (deleted by upstream's simplification). - Makefile: kept upstream's new /dev symlink block (dev/null, ptmx, random, urandom, zero, tty, stdin/stdout/stderr). Verified: cargo check clean; 57/57 tests pass (xhcid 35, usbhubd 14, xhci trb 8); verify-fork-functions.sh --no-fetch base reports all upstream functions present.
This commit is contained in:
@@ -77,7 +77,7 @@ base:
|
||||
--manifest-path "$(SRC_DIR)/Cargo.toml" \
|
||||
$(INITFS_CARGO_ARGS) $(INITFS_DRIVERS_CARGO_ARGS)
|
||||
# Build bootstrap
|
||||
cd "$(SRC_DIR)/bootstrap" && $(CARGO) rustc $(BUILD_FLAGS) \
|
||||
cd "$(SRC_DIR)/bootstrap" && RUSTFLAGS= $(CARGO) rustc $(BUILD_FLAGS) \
|
||||
-- -Ctarget-feature=+crt-static -Clinker="$(LINKER)"
|
||||
|
||||
install-base: base $(SYSROOT)/bin/redoxfs
|
||||
@@ -117,3 +117,15 @@ install-base: base $(SYSROOT)/bin/redoxfs
|
||||
# Distribute initfs
|
||||
@mkdir -pv "$(DESTDIR)/usr/lib/boot"
|
||||
cp -v "$(BUILD_DIR)/initfs.img" "$(DESTDIR)/usr/lib/boot/initfs"
|
||||
|
||||
# Device file symlinks
|
||||
@mkdir -pv "$(DESTDIR)/dev"
|
||||
ln -sf /scheme/null $(DESTDIR)/dev/null
|
||||
ln -sf /scheme/pty/ptmx $(DESTDIR)/dev/ptmx
|
||||
ln -sf /scheme/rand $(DESTDIR)/dev/random
|
||||
ln -sf /scheme/rand $(DESTDIR)/dev/urandom
|
||||
ln -sf /scheme/zero $(DESTDIR)/dev/zero
|
||||
ln -sf libc:tty $(DESTDIR)/dev/tty
|
||||
ln -sf libc:stdin $(DESTDIR)/dev/stdin
|
||||
ln -sf libc:stdout $(DESTDIR)/dev/stdout
|
||||
ln -sf libc:stderr $(DESTDIR)/dev/stderr
|
||||
|
||||
+3
-16
@@ -238,8 +238,6 @@ pub fn main() -> ! {
|
||||
.openat_into_upper(exe_reference, O_RDONLY | O_CLOEXEC, 0)
|
||||
.expect("failed to open init");
|
||||
|
||||
drop(initfs_root_fd);
|
||||
|
||||
let FexecResult::Interp {
|
||||
path: interp_path,
|
||||
interp_override,
|
||||
@@ -265,24 +263,13 @@ pub fn main() -> ! {
|
||||
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
|
||||
let interp_cstr = CStr::from_bytes_with_nul(&interp_path).expect("interpreter not valid C str");
|
||||
let interp_path = interp_cstr.to_str().expect("interpreter not UTF-8");
|
||||
let root_fd = FdGuard::new(
|
||||
redox_rt::sys::openat_into_upper(
|
||||
extrainfo.ns_fd.unwrap(), // initns, not initfs!
|
||||
interp_path,
|
||||
O_RDONLY | O_CLOEXEC,
|
||||
0,
|
||||
)
|
||||
.expect("failed to open root fd"),
|
||||
)
|
||||
.to_upper()
|
||||
.unwrap();
|
||||
let redox_path = redox_path::RedoxPath::from_absolute(interp_path)
|
||||
.expect("interpreter path is not a Scheme-rooted path");
|
||||
let (_, reference) = redox_path
|
||||
let (_, interp_reference) = redox_path
|
||||
.as_parts()
|
||||
.expect("redox_path is not scheme root path");
|
||||
let interp_file = root_fd
|
||||
.openat_into_upper(reference.as_ref(), O_RDONLY | O_CLOEXEC, 0)
|
||||
let interp_file = initfs_root_fd
|
||||
.openat_into_upper(interp_reference.as_ref(), O_RDONLY | O_CLOEXEC, 0)
|
||||
.expect("failed to open dynamic linker");
|
||||
|
||||
fexec_impl(
|
||||
|
||||
@@ -197,7 +197,7 @@ impl SchemeSync for FbbootlogScheme {
|
||||
}
|
||||
|
||||
fn fpath(&mut self, _id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with_legacy(buf, "fbbootlog", |_| Ok(()))
|
||||
FpathWriter::with(buf, "fbbootlog", |_| Ok(()))
|
||||
}
|
||||
|
||||
fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
|
||||
@@ -144,7 +144,7 @@ impl SchemeSync for FbconScheme {
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with_legacy(buf, "fbcon", |w| {
|
||||
FpathWriter::with(buf, "fbcon", |w| {
|
||||
let handle = self.get_vt_handle_mut(id)?;
|
||||
write!(w, "{}", handle.vt_i.0).unwrap();
|
||||
Ok(())
|
||||
|
||||
@@ -568,7 +568,7 @@ impl<T: Disk> SchemeAsync for DiskSchemeInner<T> {
|
||||
}
|
||||
|
||||
async fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with_legacy(buf, &self.scheme_name, |w| {
|
||||
FpathWriter::with(buf, &self.scheme_name, |w| {
|
||||
match *self.handles.get(id)? {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
|
||||
@@ -39,19 +39,14 @@ impl Disk for NvmeDisk {
|
||||
}
|
||||
|
||||
fn time_arm(time_handle: &mut File, secs: i64) -> io::Result<()> {
|
||||
let mut time_buf = [0_u8; core::mem::size_of::<libredox::data::TimeSpec>()];
|
||||
let mut time_buf = syscall::TimeSpec::default();
|
||||
if time_handle.read(&mut time_buf)? < time_buf.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"time read too small",
|
||||
));
|
||||
}
|
||||
|
||||
match libredox::data::timespec_from_mut_bytes(&mut time_buf) {
|
||||
time => {
|
||||
time.tv_sec += secs;
|
||||
}
|
||||
}
|
||||
time_buf.tv_sec += secs;
|
||||
time_handle.write(&time_buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -104,6 +104,12 @@ pub struct IrqReactor<const N: usize> {
|
||||
|
||||
pub type NewPendingTrb = State;
|
||||
|
||||
enum EventProcessResult {
|
||||
NoEvent,
|
||||
EventRingFull,
|
||||
RegularEvent,
|
||||
}
|
||||
|
||||
impl<const N: usize> IrqReactor<N> {
|
||||
pub fn new(hci: Arc<Xhci<N>>, irq_file: Option<File>) -> Self {
|
||||
let device_enumerator_sender = hci.device_enumerator_sender.clone();
|
||||
@@ -139,45 +145,14 @@ impl<const N: usize> IrqReactor<N> {
|
||||
|
||||
let mut event_ring = hci_clone.primary_event_ring.lock().unwrap_or_else(|e| e.into_inner());
|
||||
|
||||
let event_trb = &mut event_ring.ring.trbs[event_trb_index];
|
||||
|
||||
if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 {
|
||||
continue 'trb_loop;
|
||||
}
|
||||
|
||||
trace!(
|
||||
"Found event TRB at index {} with type {} and cycle bit {}: {:?}",
|
||||
event_trb_index,
|
||||
event_trb.trb_type(),
|
||||
event_trb.cycle() as u8,
|
||||
event_trb
|
||||
);
|
||||
|
||||
if self.check_event_ring_full(event_trb.clone()) {
|
||||
info!("Had to resize event TRB, retrying...");
|
||||
continue 'trb_loop;
|
||||
}
|
||||
|
||||
trace!("Handling requests");
|
||||
self.handle_requests();
|
||||
trace!("Requests handled");
|
||||
|
||||
match event_trb.trb_type() {
|
||||
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
|
||||
trace!("Received a port status change!");
|
||||
self.handle_port_status_change(event_trb.clone())
|
||||
} //TODO Handle the other unprompted events
|
||||
_ => {
|
||||
self.acknowledge(event_trb.clone());
|
||||
loop {
|
||||
match self.process_one_event(&mut event_ring, &mut event_trb_index) {
|
||||
EventProcessResult::NoEvent | EventProcessResult::EventRingFull => {
|
||||
continue 'trb_loop
|
||||
}
|
||||
EventProcessResult::RegularEvent => {}
|
||||
}
|
||||
}
|
||||
|
||||
event_trb.reserved(false);
|
||||
|
||||
self.update_erdp(&*event_ring);
|
||||
hci_clone.event_handler_finished();
|
||||
|
||||
event_trb_index = event_ring.ring.next_index();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,69 +238,102 @@ impl<const N: usize> IrqReactor<N> {
|
||||
|
||||
loop {
|
||||
trace!("count: {}", count);
|
||||
let event_trb = &mut event_ring.ring.trbs[event_trb_index];
|
||||
|
||||
if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 {
|
||||
if count == 0 {
|
||||
// Linux 7.1 xhci-pci.c SPURIOUS_REBOOT: some
|
||||
// Intel controllers (Panther Point, Lynx Point)
|
||||
// generate spurious interrupts under load.
|
||||
// Downgrade from warn to debug when quirked.
|
||||
if self.hci.quirks.contains(crate::xhci::quirks::XhciQuirks::SPURIOUS_REBOOT) {
|
||||
debug!("xhci: spurious interrupt (SPURIOUS_REBOOT quirk active) — ignoring");
|
||||
} else {
|
||||
warn!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.");
|
||||
match self.process_one_event(&mut event_ring, &mut event_trb_index) {
|
||||
EventProcessResult::NoEvent => {
|
||||
if count == 0 {
|
||||
// Linux 7.1 xhci-pci.c SPURIOUS_REBOOT: some
|
||||
// Intel controllers (Panther Point, Lynx Point)
|
||||
// generate spurious interrupts under load.
|
||||
// Downgrade from warn to debug when quirked.
|
||||
if self.hci.quirks.contains(crate::xhci::quirks::XhciQuirks::SPURIOUS_REBOOT) {
|
||||
debug!("xhci: spurious interrupt (SPURIOUS_REBOOT quirk active) — ignoring");
|
||||
} else {
|
||||
warn!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.")
|
||||
}
|
||||
}
|
||||
self.unmask_interrupts();
|
||||
|
||||
// Process any events received between the NoEvent and unmasking interrupts
|
||||
loop {
|
||||
match self.process_one_event(&mut event_ring, &mut event_trb_index) {
|
||||
EventProcessResult::NoEvent => break,
|
||||
EventProcessResult::EventRingFull
|
||||
| EventProcessResult::RegularEvent => {}
|
||||
}
|
||||
}
|
||||
|
||||
continue 'trb_loop;
|
||||
}
|
||||
//hci_clone.event_handler_finished();
|
||||
self.unmask_interrupts();
|
||||
continue 'trb_loop;
|
||||
} else {
|
||||
count += 1
|
||||
}
|
||||
|
||||
info!(
|
||||
"Found event TRB at index {} with type {} and cycle bit {}: {:?}",
|
||||
event_trb_index,
|
||||
event_trb.trb_type(),
|
||||
event_trb.cycle() as u8,
|
||||
event_trb
|
||||
);
|
||||
|
||||
if self.check_event_ring_full(event_trb.clone()) {
|
||||
info!("Had to resize event TRB, retrying...");
|
||||
//hci_clone.event_handler_finished();
|
||||
if self.hci.interrupt_is_pending(0) {
|
||||
warn!("After incrementing the dequeue pointer, the interrupt bit is still pending.")
|
||||
} else {
|
||||
debug!("The interrupt bit is no longer pending.");
|
||||
EventProcessResult::EventRingFull => {
|
||||
// FIXME can this use the RegularEvent path?
|
||||
if self.hci.interrupt_is_pending(0) {
|
||||
warn!("After incrementing the dequeue pointer, the interrupt bit is still pending.")
|
||||
} else {
|
||||
debug!("The interrupt bit is no longer pending.");
|
||||
}
|
||||
self.unmask_interrupts();
|
||||
continue 'trb_loop;
|
||||
}
|
||||
self.unmask_interrupts();
|
||||
continue 'trb_loop;
|
||||
}
|
||||
self.handle_requests();
|
||||
|
||||
match event_trb.trb_type() {
|
||||
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
|
||||
trace!("Received a port status change!");
|
||||
self.handle_port_status_change(event_trb.clone())
|
||||
} //TODO Handle the other unprompted events
|
||||
_ => {
|
||||
trace!("Received a non-status trb");
|
||||
self.acknowledge(event_trb.clone());
|
||||
EventProcessResult::RegularEvent => {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
event_trb.reserved(false);
|
||||
|
||||
self.update_erdp(&*event_ring);
|
||||
self.hci.event_handler_finished();
|
||||
|
||||
event_trb_index = event_ring.ring.next_index();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_one_event(
|
||||
&mut self,
|
||||
event_ring: &mut EventRing,
|
||||
event_trb_index: &mut usize,
|
||||
) -> EventProcessResult {
|
||||
let event_trb = &mut event_ring.ring.trbs[*event_trb_index];
|
||||
|
||||
if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 {
|
||||
//hci_clone.event_handler_finished();
|
||||
return EventProcessResult::NoEvent;
|
||||
}
|
||||
|
||||
trace!(
|
||||
"Found event TRB at index {} with type {} and cycle bit {}: {:?}",
|
||||
event_trb_index,
|
||||
event_trb.trb_type(),
|
||||
event_trb.cycle() as u8,
|
||||
event_trb
|
||||
);
|
||||
|
||||
if self.check_event_ring_full(event_trb.clone()) {
|
||||
info!("Had to resize event TRB, retrying...");
|
||||
//hci_clone.event_handler_finished();
|
||||
return EventProcessResult::EventRingFull;
|
||||
}
|
||||
|
||||
trace!("Handling requests");
|
||||
self.handle_requests();
|
||||
trace!("Requests handled");
|
||||
|
||||
match event_trb.trb_type() {
|
||||
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
|
||||
trace!("Received a port status change!");
|
||||
self.handle_port_status_change(event_trb.clone())
|
||||
} //TODO Handle the other unprompted events
|
||||
_ => {
|
||||
trace!("Received a non-status trb");
|
||||
self.acknowledge(event_trb.clone());
|
||||
}
|
||||
}
|
||||
|
||||
event_trb.reserved(false);
|
||||
|
||||
self.update_erdp(&*event_ring);
|
||||
self.hci.event_handler_finished();
|
||||
|
||||
*event_trb_index = event_ring.ring.next_index();
|
||||
|
||||
EventProcessResult::RegularEvent
|
||||
}
|
||||
|
||||
/// Handles device attach/detach events as indicated by a PortStatusChange
|
||||
fn handle_port_status_change(&mut self, trb: Trb) {
|
||||
if let Some(root_hub_port_num) = trb.port_status_change_port_id() {
|
||||
|
||||
+1
-1
@@ -331,7 +331,7 @@ impl<'sock> SchemeSync for ChanScheme<'sock> {
|
||||
}
|
||||
}
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with_legacy(buf, "chan", |w| {
|
||||
FpathWriter::with(buf, "chan", |w| {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
if let Extra::SchemeRoot = handle.extra {
|
||||
return Ok(());
|
||||
|
||||
+11
-188
@@ -3,11 +3,6 @@ use std::arch::asm;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_core::RngCore;
|
||||
|
||||
pub const MODE_PERM: u16 = 0x0FFF;
|
||||
pub const MODE_EXEC: u16 = 0o1;
|
||||
pub const MODE_WRITE: u16 = 0o2;
|
||||
pub const MODE_READ: u16 = 0o4;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use raw_cpuid::CpuId;
|
||||
|
||||
@@ -27,8 +22,6 @@ use sha2::{Digest, Sha256};
|
||||
// We do not implement blocking reads as per linux /dev/random for the reasons outlined
|
||||
// here: https://www.2uo.de/myths-about-urandom/
|
||||
|
||||
// Default file access mode for PRNG
|
||||
const DEFAULT_PRNG_MODE: u16 = 0o644;
|
||||
// Rand crate recommends at least 256 bits of entropy to seed the RNG
|
||||
const SEED_BYTES: usize = 32;
|
||||
|
||||
@@ -89,33 +82,9 @@ fn create_rdrand_seed() -> [u8; SEED_BYTES] {
|
||||
/// Contains information about an open file
|
||||
struct OpenFileInfo {
|
||||
o_flags: usize,
|
||||
/// Flags used when opening file.
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
file_stat: Stat,
|
||||
}
|
||||
|
||||
impl OpenFileInfo {
|
||||
/// Tests if the current user has enough permissions to view the file, op is the operation,
|
||||
/// like read and write, these modes are MODE_EXEC, MODE_READ, and MODE_WRITE
|
||||
/// Copied from redoxfs
|
||||
fn permission(&self, op: u16) -> bool {
|
||||
let mut perm = self.file_stat.st_mode & 0o7;
|
||||
if self.uid == self.file_stat.st_uid {
|
||||
// If self.mode is 101100110, >> 6 would be 000000101
|
||||
// 0o7 is octal for 111, or, when expanded to 9 digits is 000000111
|
||||
perm |= (self.file_stat.st_mode >> 6) & 0o7;
|
||||
// Since we erased the GID and OTHER bits when >>6'ing, |= will keep those bits in place.
|
||||
}
|
||||
if self.gid == self.file_stat.st_gid || self.file_stat.st_gid == 0 {
|
||||
perm |= (self.file_stat.st_mode >> 3) & 0o7;
|
||||
}
|
||||
if self.uid == 0 {
|
||||
//set the `other` bits to 111
|
||||
perm |= 0o7;
|
||||
}
|
||||
perm & op == op
|
||||
}
|
||||
fn o_flag_set(&self, f: usize) -> bool {
|
||||
return (f & self.o_flags) == f;
|
||||
}
|
||||
@@ -141,7 +110,6 @@ struct RandScheme {
|
||||
// ChaCha20 is a Cryptographically Secure PRNG
|
||||
// https://docs.rs/rand/0.5.0/rand/prng/chacha/struct.ChaChaRng.html
|
||||
// Allows 2^64 streams of random numbers, which we will equate with file numbers
|
||||
prng_stat: Stat,
|
||||
handles: HandleMap<Handle>,
|
||||
}
|
||||
|
||||
@@ -150,12 +118,6 @@ impl RandScheme {
|
||||
fn new() -> RandScheme {
|
||||
RandScheme {
|
||||
prng: ChaCha20Rng::from_seed(create_rdrand_seed()),
|
||||
prng_stat: Stat {
|
||||
st_mode: MODE_CHR | DEFAULT_PRNG_MODE,
|
||||
st_gid: 0,
|
||||
st_uid: 0,
|
||||
..Default::default()
|
||||
},
|
||||
handles: HandleMap::new(),
|
||||
}
|
||||
}
|
||||
@@ -169,12 +131,11 @@ impl RandScheme {
|
||||
/// Checks to see if the op (MODE_READ, MODE_WRITE) can be performed on the open file
|
||||
/// descriptor - Will return the open file info if successful, and error if the file
|
||||
/// descriptor is invalid, or the permission is denied.
|
||||
fn can_perform_op_on_fd(&self, fd: usize, op: u16) -> Result<&OpenFileInfo> {
|
||||
let file_info = self.get_fd(fd)?;
|
||||
if !file_info.permission(op) {
|
||||
fn can_perform_op_on_fd(&self, fd: usize, f: usize) -> Result<()> {
|
||||
if !self.get_fd(fd)?.o_flag_set(f) {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
Ok(file_info)
|
||||
Ok(())
|
||||
}
|
||||
/// Reseed the CSPRNG with the supplied entropy.
|
||||
/// TODO add this to an entropy pool and give a limited estimate to the amount of entropy
|
||||
@@ -201,20 +162,10 @@ impl RandScheme {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
let open_file_info = OpenFileInfo {
|
||||
o_flags: flags,
|
||||
file_stat: self.prng_stat,
|
||||
uid: ctx.uid,
|
||||
gid: ctx.gid,
|
||||
};
|
||||
let open_file_info = OpenFileInfo { o_flags: flags };
|
||||
|
||||
if (open_file_info.o_flag_set(O_RDONLY) || open_file_info.o_flag_set(O_RDWR))
|
||||
&& !open_file_info.permission(MODE_READ)
|
||||
{
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
if (open_file_info.o_flag_set(O_WRONLY) || open_file_info.o_flag_set(O_RDWR))
|
||||
&& !open_file_info.permission(MODE_WRITE)
|
||||
&& ctx.uid != 0
|
||||
{
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
@@ -227,108 +178,6 @@ impl RandScheme {
|
||||
})
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_scheme_perms() {
|
||||
use syscall::{O_CLOEXEC, O_STAT};
|
||||
|
||||
let mut ctx = CallerCtx {
|
||||
pid: 0,
|
||||
uid: 1,
|
||||
gid: 1,
|
||||
id: unsafe { std::mem::zeroed() }, // Id doesn't have a public constructor
|
||||
};
|
||||
|
||||
let mut scheme = RandScheme::new();
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o200;
|
||||
scheme.prng_stat.st_uid = 1;
|
||||
scheme.prng_stat.st_gid = 1;
|
||||
assert!(scheme.open_inner("/", O_RDWR, &ctx).is_err());
|
||||
assert!(scheme.open_inner("/", O_RDONLY, &ctx).is_err());
|
||||
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o400;
|
||||
let mut fd = match scheme.open("", O_RDONLY, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
scheme.on_close(fd);
|
||||
|
||||
assert!(scheme.open_inner("", O_WRONLY, &ctx).is_err());
|
||||
assert!(scheme.open_inner("", O_RDWR, &ctx).is_err());
|
||||
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o600;
|
||||
fd = match scheme.open_inner("", O_RDWR, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
scheme.on_close(fd);
|
||||
|
||||
ctx.uid = 2;
|
||||
ctx.gid = 2;
|
||||
fd = match scheme.open_inner("", O_STAT, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
scheme.on_close(fd);
|
||||
fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
scheme.on_close(fd);
|
||||
|
||||
// Try another user in group (no group perms)
|
||||
ctx.uid = 2;
|
||||
ctx.gid = 1;
|
||||
fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
|
||||
scheme.on_close(fd);
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
|
||||
fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
scheme.on_close(fd);
|
||||
|
||||
// Check root can do anything
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o000;
|
||||
ctx.uid = 0;
|
||||
ctx.gid = 0;
|
||||
fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() {
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
scheme.on_close(fd);
|
||||
|
||||
// Check the rand:/urandom URL (Equivalent to rand:/)
|
||||
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
|
||||
ctx.uid = 2;
|
||||
ctx.gid = 1;
|
||||
fd = match scheme
|
||||
.open_inner("/urandom", O_STAT | O_CLOEXEC, &ctx)
|
||||
.unwrap()
|
||||
{
|
||||
OpenResult::ThisScheme { number, .. } => number,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
|
||||
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
|
||||
scheme.on_close(fd);
|
||||
}
|
||||
|
||||
impl SchemeSync for RandScheme {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
@@ -360,7 +209,7 @@ impl SchemeSync for RandScheme {
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(id, MODE_READ)?;
|
||||
self.can_perform_op_on_fd(id, O_RDONLY)?;
|
||||
|
||||
// Setting the stream will ensure that if two clients are reading concurrently, they won't get the same numbers
|
||||
self.prng.set_stream(id as u64); // Should probably find a way to re-instate the counter for this stream, but
|
||||
@@ -379,7 +228,7 @@ impl SchemeSync for RandScheme {
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(id, MODE_WRITE)?;
|
||||
self.can_perform_op_on_fd(id, O_WRONLY)?;
|
||||
|
||||
// TODO - when we support other entropy sources, just add this to an entropy pool
|
||||
// TODO - consider having trusted and untrusted entropy writing paths
|
||||
@@ -397,31 +246,6 @@ impl SchemeSync for RandScheme {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn fchmod(&mut self, id: usize, mode: u16, ctx: &CallerCtx) -> Result<()> {
|
||||
// Check fd and permissions
|
||||
let file_info = self.get_fd(id)?;
|
||||
// only root and owner can chmod
|
||||
if ctx.uid != file_info.file_stat.st_uid && ctx.uid != 0 {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
self.prng_stat.st_mode = MODE_CHR | (mode & MODE_PERM); // Apply mask
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fchown(&mut self, id: usize, uid: u32, gid: u32, ctx: &CallerCtx) -> Result<()> {
|
||||
// Check fd and permissions
|
||||
let file_info = self.get_fd(id)?;
|
||||
// only root and owner can fchown
|
||||
if ctx.uid != file_info.file_stat.st_uid && ctx.uid != 0 {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
self.prng_stat.st_uid = uid;
|
||||
self.prng_stat.st_gid = gid;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, _id: usize, cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
// /dev/random and /dev/urandom have no file status flags or locks.
|
||||
// F_GETFL/F_GETFD legitimately return 0; set commands are no-ops
|
||||
@@ -441,11 +265,10 @@ impl SchemeSync for RandScheme {
|
||||
FpathWriter::with(buf, "rand", |_| Ok(()))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, file: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
// Check fd and permissions
|
||||
self.can_perform_op_on_fd(file, MODE_READ)?;
|
||||
|
||||
*stat = self.prng_stat.clone();
|
||||
fn fstat(&mut self, _file: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
stat.st_mode = 0o644 | MODE_CHR;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -92,17 +92,6 @@ impl<'a> FpathWriter<'a> {
|
||||
Ok(w.written)
|
||||
}
|
||||
|
||||
pub fn with_legacy(
|
||||
buf: &'a mut [u8],
|
||||
scheme_name: &str,
|
||||
f: impl FnOnce(&mut Self) -> Result<()>,
|
||||
) -> Result<usize> {
|
||||
let mut w = FpathWriter { buf, written: 0 };
|
||||
write!(w, "{scheme_name}:").unwrap();
|
||||
f(&mut w)?;
|
||||
Ok(w.written)
|
||||
}
|
||||
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
let count = core::cmp::min(s.len(), self.buf.len() - self.written);
|
||||
self.buf[self.written..self.written + count].copy_from_slice(&s.as_bytes()[..count]);
|
||||
|
||||
Reference in New Issue
Block a user