Skip to content

Commit a404015

Browse files
committed
std: sys: process: uefi: Use NULL stdin by default
According to the docs in `Command::output`: > By default, stdout and stderr are captured (and used to provide the resulting output). Stdin is not inherited from the parent and any attempt by the child process to read from the stdin stream will result in the stream immediately closing. This was being violated by UEFI which was inheriting stdin by default. While the docs don't explicitly state that the default should be NULL, the behaviour seems like reading from NULL. UEFI however, has a bit of a problem. The `EFI_SIMPLE_TEXT_INPUT_PROTOCOL` only provides support for reading 1 key press. This means that you either get an error, or it is assumed that the keypress was read successfully. So there is no way to have a successful read of length 0. Currently, I am returning UNSUPPORTED error when trying to read from NULL stdin. On linux however, you will get a read of length 0 for Null stdin. One possible way to get around this is to translate one of the UEFI errors to a read 0 (Maybe unsupported?). It is also possible to have a non-standard error code, but well, not sure if we go that route. Alternatively, if meaning of Stdio::Null is platform dependent, it should be fine to keep the current behaviour of returning an error. Signed-off-by: Ayush Singh <[email protected]>
1 parent 175dcc7 commit a404015

File tree

1 file changed

+109
-3
lines changed

1 file changed

+109
-3
lines changed

Diff for: library/std/src/sys/process/uefi.rs

+109-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use r_efi::protocols::simple_text_output;
1+
use r_efi::protocols::{simple_text_input, simple_text_output};
22

33
use crate::collections::BTreeMap;
44
pub use crate::ffi::OsString as EnvKey;
@@ -122,6 +122,22 @@ impl Command {
122122
}
123123
}
124124

125+
fn create_stdin(
126+
s: Stdio,
127+
) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::InputProtocol>>> {
128+
match s {
129+
Stdio::Null => unsafe {
130+
helpers::OwnedProtocol::create(
131+
uefi_command_internal::InputProtocol::null(),
132+
simple_text_input::PROTOCOL_GUID,
133+
)
134+
}
135+
.map(Some),
136+
Stdio::Inherit => Ok(None),
137+
Stdio::MakePipe => unsupported(),
138+
}
139+
}
140+
125141
pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
126142
let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?;
127143

@@ -149,6 +165,14 @@ impl Command {
149165
cmd.stderr_inherit()
150166
};
151167

168+
// Setup Stdin
169+
let stdin = Self::create_stdin(Stdio::Null)?;
170+
if let Some(con) = stdin {
171+
cmd.stdin_init(con)
172+
} else {
173+
cmd.stdin_inherit()
174+
};
175+
152176
let env = env_changes(&self.env);
153177

154178
// Set any new vars
@@ -334,7 +358,7 @@ impl<'a> fmt::Debug for CommandArgs<'a> {
334358

335359
#[allow(dead_code)]
336360
mod uefi_command_internal {
337-
use r_efi::protocols::{loaded_image, simple_text_output};
361+
use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output};
338362

339363
use crate::ffi::{OsStr, OsString};
340364
use crate::io::{self, const_error};
@@ -350,6 +374,7 @@ mod uefi_command_internal {
350374
handle: NonNull<crate::ffi::c_void>,
351375
stdout: Option<helpers::OwnedProtocol<PipeProtocol>>,
352376
stderr: Option<helpers::OwnedProtocol<PipeProtocol>>,
377+
stdin: Option<helpers::OwnedProtocol<InputProtocol>>,
353378
st: OwnedTable<r_efi::efi::SystemTable>,
354379
args: Option<(*mut u16, usize)>,
355380
}
@@ -384,7 +409,14 @@ mod uefi_command_internal {
384409
helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap();
385410
let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table });
386411

387-
Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None })
412+
Ok(Self {
413+
handle: child_handle,
414+
stdout: None,
415+
stderr: None,
416+
stdin: None,
417+
st,
418+
args: None,
419+
})
388420
}
389421
}
390422

@@ -445,6 +477,17 @@ mod uefi_command_internal {
445477
}
446478
}
447479

480+
fn set_stdin(
481+
&mut self,
482+
handle: r_efi::efi::Handle,
483+
protocol: *mut simple_text_input::Protocol,
484+
) {
485+
unsafe {
486+
(*self.st.as_mut_ptr()).console_in_handle = handle;
487+
(*self.st.as_mut_ptr()).con_in = protocol;
488+
}
489+
}
490+
448491
pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) {
449492
self.set_stdout(
450493
protocol.handle().as_ptr(),
@@ -471,6 +514,19 @@ mod uefi_command_internal {
471514
unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) }
472515
}
473516

517+
pub(crate) fn stdin_init(&mut self, protocol: helpers::OwnedProtocol<InputProtocol>) {
518+
self.set_stdin(
519+
protocol.handle().as_ptr(),
520+
protocol.as_ref() as *const InputProtocol as *mut simple_text_input::Protocol,
521+
);
522+
self.stdin = Some(protocol);
523+
}
524+
525+
pub(crate) fn stdin_inherit(&mut self) {
526+
let st: NonNull<r_efi::efi::SystemTable> = system_table().cast();
527+
unsafe { self.set_stdin((*st.as_ptr()).console_in_handle, (*st.as_ptr()).con_in) }
528+
}
529+
474530
pub fn stderr(&self) -> io::Result<Vec<u8>> {
475531
match &self.stderr {
476532
Some(stderr) => stderr.as_ref().utf8(),
@@ -722,6 +778,56 @@ mod uefi_command_internal {
722778
}
723779
}
724780

781+
#[repr(C)]
782+
pub(crate) struct InputProtocol {
783+
reset: simple_text_input::ProtocolReset,
784+
read_key_stroke: simple_text_input::ProtocolReadKeyStroke,
785+
wait_for_key: r_efi::efi::Event,
786+
}
787+
788+
impl InputProtocol {
789+
pub(crate) fn null() -> Self {
790+
let evt = helpers::OwnedEvent::new(
791+
r_efi::efi::EVT_NOTIFY_WAIT,
792+
r_efi::efi::TPL_CALLBACK,
793+
Some(Self::empty_notify),
794+
None,
795+
)
796+
.unwrap();
797+
798+
Self {
799+
reset: Self::null_reset,
800+
read_key_stroke: Self::null_read_key,
801+
wait_for_key: evt.into_raw(),
802+
}
803+
}
804+
805+
extern "efiapi" fn null_reset(
806+
_: *mut simple_text_input::Protocol,
807+
_: r_efi::efi::Boolean,
808+
) -> r_efi::efi::Status {
809+
r_efi::efi::Status::SUCCESS
810+
}
811+
812+
extern "efiapi" fn null_read_key(
813+
_: *mut simple_text_input::Protocol,
814+
_: *mut simple_text_input::InputKey,
815+
) -> r_efi::efi::Status {
816+
r_efi::efi::Status::UNSUPPORTED
817+
}
818+
819+
extern "efiapi" fn empty_notify(_: r_efi::efi::Event, _: *mut crate::ffi::c_void) {}
820+
}
821+
822+
impl Drop for InputProtocol {
823+
fn drop(&mut self) {
824+
// Close wait_for_key
825+
unsafe {
826+
let _ = helpers::OwnedEvent::from_raw(self.wait_for_key);
827+
}
828+
}
829+
}
830+
725831
pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> {
726832
const QUOTE: u16 = 0x0022;
727833
const SPACE: u16 = 0x0020;

0 commit comments

Comments
 (0)