1use std::num::NonZeroUsize;
10use std::time::Duration;
11
12use async_trait::async_trait;
13use reverie::Errno;
14use reverie::Error;
15use reverie::Guest;
16use reverie::MaskChangeRefused;
17use reverie::Stack;
18use reverie::syscalls;
19use reverie::syscalls::Addr;
20use reverie::syscalls::AddrMut;
21use reverie::syscalls::Displayable;
22use reverie::syscalls::FcntlCmd;
23use reverie::syscalls::MapFlags;
24use reverie::syscalls::MemoryAccess;
25use reverie::syscalls::ProtFlags;
26use reverie::syscalls::Syscall;
27use reverie::syscalls::SyscallInfo;
28use reverie::syscalls::Sysno;
29use reverie::syscalls::Timespec;
30use reverie::syscalls::WaitPidFlag;
31
32use crate::fd::FdType;
33use crate::record_or_replay::RecordOrReplay;
34use crate::resources::ExternalOpId;
35use crate::resources::Permission;
36use crate::resources::ResourceID;
37use crate::resources::Resources;
38use crate::syscalls::entry_import::Displaced;
39use crate::syscalls::entry_import::EntryImport;
40use crate::syscalls::threads::BlockedWaitSignalError;
41use crate::syscalls::threads::KERNEL_SIGSET_SIZE;
42use crate::syscalls::threads::KernelSigaction;
43use crate::syscalls::threads::KernelSignalState;
44use crate::syscalls::threads::KernelSigset;
45use crate::syscalls::threads::SignalQueue;
46use crate::syscalls::threads::WaitSignalDisposition;
47use crate::syscalls::threads::block_signals_for_disposition;
48use crate::syscalls::threads::blocked_signal_mask;
49use crate::syscalls::threads::kernel_sigset_bit;
50use crate::syscalls::threads::read_thread_wait_signal_state;
51use crate::syscalls::threads::read_wait_signal_state;
52use crate::syscalls::threads::restore_signals_after_disposition;
53use crate::syscalls::threads::wait_signal_disposition;
54use crate::tool_global::ResumeStatus;
55use crate::tool_global::host_timed_signals;
56use crate::tool_global::resource_request;
57use crate::tool_global::thread_observe_time;
58use crate::tool_global::trace_schedevent;
59use crate::tool_local::Detcore;
60use crate::tool_local::finish_partial_record_or_replay_write;
61use crate::types::DetTid;
62use crate::types::LogicalTime;
63use crate::types::OpenFileId;
64use crate::types::SchedEvent;
65use crate::types::SyscallPhase;
66
67impl<T: RecordOrReplay> Detcore<T> {
68 pub(crate) async fn refuse_unserviceable_operation<G: Guest<Self>>(
91 &self,
92 guest: &mut G,
93 sysno: reverie::syscalls::Sysno,
94 fallback: Errno,
95 ) -> Result<i64, Error> {
96 refuse_unserviceable_operation_with(
97 guest,
98 self.cfg.panic_on_unsupported_syscalls,
99 sysno,
100 fallback,
101 None,
102 )
103 .await
104 }
105
106 pub(crate) async fn refuse_unserviceable_operation_because<G: Guest<Self>>(
110 &self,
111 guest: &mut G,
112 sysno: reverie::syscalls::Sysno,
113 fallback: Errno,
114 reason: &str,
115 ) -> Result<i64, Error> {
116 refuse_unserviceable_operation_with(
117 guest,
118 self.cfg.panic_on_unsupported_syscalls,
119 sysno,
120 fallback,
121 Some(reason),
122 )
123 .await
124 }
125
126 pub(crate) async fn report_backend_refusal<G: Guest<Self>>(
142 &self,
143 guest: &mut G,
144 sysno: Sysno,
145 operation: reverie::UnsupportedOperation,
146 result: Result<i64, Error>,
147 ) -> Result<i64, Error> {
148 let errno = match &result {
149 Err(Error::Errno(errno)) => *errno,
150 _ => return result,
151 };
152 let Some(refusal) = guest.take_unsupported_refusal() else {
153 return result;
154 };
155 let dettid = guest.thread_state().dettid;
156 if refusal.operation != operation || refusal.errno != errno {
157 tracing::warn!(
158 "[dtid {dettid}] {sysno}: the backend's refusal ({:?}, {}) is not the one this \
159 call can be refused with ({operation:?}, {errno}): {}",
160 refusal.operation,
161 refusal.errno,
162 refusal.diagnostic
163 );
164 judge_backend_refusal::<T, G>(guest, Some(refusal), sysno);
167 return result;
168 }
169 report_refusal_reason(
170 self.cfg.panic_on_unsupported_syscalls,
171 &format!(
172 "[dtid {dettid}] {sysno} refused by the backend: {}",
173 refusal.diagnostic
174 ),
175 );
176 if !self.cfg.panic_on_unsupported_syscalls {
177 crate::tool_global::report_unsupported_syscall(guest, sysno).await;
178 }
179 self.refuse_unserviceable_operation_because(guest, sysno, refusal.errno, refusal.diagnostic)
180 .await
181 }
182
183 pub async fn record_or_replay_blocking<G: Guest<Self>>(
192 &self,
193 guest: &mut G,
194 call: Syscall,
195 ) -> Result<i64, Error> {
196 self.record_or_replay_blocking_with_mask(guest, call, None)
197 .await
198 }
199
200 #[cfg(target_arch = "x86_64")]
206 pub(crate) async fn record_or_replay_blocking_as<G: Guest<Self>>(
207 &self,
208 guest: &mut G,
209 call: Syscall,
210 run: Syscall,
211 ) -> Result<i64, Error> {
212 let dettid = guest.thread_state().dettid;
213 let op_id = ExternalOpId::new(dettid, guest.thread_state().stats.syscall_count);
214 self.record_or_replay_blocking_resource_then(
215 guest,
216 (call, run),
217 ResourceID::BlockingExternalIO(op_id),
218 None,
219 false,
220 |_| {},
221 )
222 .await
223 }
224
225 pub async fn record_or_replay_blocking_with_mask<G: Guest<Self>>(
232 &self,
233 guest: &mut G,
234 call: Syscall,
235 blocked_signal_mask: Option<u64>,
236 ) -> Result<i64, Error> {
237 let dettid = guest.thread_state().dettid;
238 let op_id = ExternalOpId::new(dettid, guest.thread_state().stats.syscall_count);
239 self.record_or_replay_blocking_resource(
240 guest,
241 call,
242 ResourceID::BlockingExternalIO(op_id),
243 blocked_signal_mask,
244 false,
245 )
246 .await
247 }
248
249 pub async fn record_or_replay_blocking_with_unknown_mask<G: Guest<Self>>(
253 &self,
254 guest: &mut G,
255 call: Syscall,
256 ) -> Result<i64, Error> {
257 self.record_or_replay_blocking_with_unknown_mask_then(guest, call, |_| {})
258 .await
259 }
260
261 pub async fn record_or_replay_blocking_with_unknown_mask_then<G: Guest<Self>>(
267 &self,
268 guest: &mut G,
269 call: Syscall,
270 after_grant: impl FnOnce(&mut G),
271 ) -> Result<i64, Error> {
272 let dettid = guest.thread_state().dettid;
273 let op_id = ExternalOpId::new(dettid, guest.thread_state().stats.syscall_count);
274 self.record_or_replay_blocking_resource_then(
275 guest,
276 (call, call),
277 ResourceID::BlockingExternalIO(op_id),
278 None,
279 true,
280 after_grant,
281 )
282 .await
283 }
284
285 pub async fn record_or_replay_rt_sigsuspend<G: Guest<Self>>(
290 &self,
291 guest: &mut G,
292 call: syscalls::RtSigsuspend,
293 temporary_mask: u64,
294 ) -> Result<i64, Error> {
295 let dettid = guest.thread_state().dettid;
296 let op_id = ExternalOpId::new(dettid, guest.thread_state().stats.syscall_count);
297 let result = self
298 .record_or_replay_blocking_resource(
299 guest,
300 call.into(),
301 ResourceID::BlockingRtSigsuspend(op_id),
302 Some(temporary_mask),
303 false,
304 )
305 .await;
306 if !self.cfg.replaying {
320 let ran = !matches!(result, Err(Error::Errno(Errno::ERESTARTSYS)));
321 super::signal_shadow::note_wait_ran(guest, ran);
322 }
323 match result {
324 Err(Error::Errno(Errno::ERESTARTSYS)) => Err(Errno::ERESTARTNOHAND.into()),
325 other => other,
326 }
327 }
328
329 async fn record_or_replay_blocking_resource<G: Guest<Self>>(
330 &self,
331 guest: &mut G,
332 call: Syscall,
333 blocking_resource: ResourceID,
334 blocked_signal_mask: Option<u64>,
335 blocked_signal_mask_unknown: bool,
336 ) -> Result<i64, Error> {
337 self.record_or_replay_blocking_resource_then(
338 guest,
339 (call, call),
340 blocking_resource,
341 blocked_signal_mask,
342 blocked_signal_mask_unknown,
343 |_| {},
344 )
345 .await
346 }
347
348 async fn record_or_replay_blocking_resource_then<G: Guest<Self>>(
352 &self,
353 guest: &mut G,
354 (call, run): (Syscall, Syscall),
355 blocking_resource: ResourceID,
356 blocked_signal_mask: Option<u64>,
357 blocked_signal_mask_unknown: bool,
358 after_grant: impl FnOnce(&mut G),
359 ) -> Result<i64, Error> {
360 if let Some(refused) = kernel_run_entry_refusal(guest)? {
364 return Err(refused);
365 }
366 crate::syscalls::timerfd::refuse_host_wait_for(guest, call);
369 let dettid = guest.thread_state().dettid;
370 let op_id = match &blocking_resource {
371 ResourceID::BlockingExternalIO(op_id) | ResourceID::BlockingRtSigsuspend(op_id) => {
372 *op_id
373 }
374 _ => unreachable!("blocking syscall helper requires a blocking resource"),
375 };
376 let replay_served_from_log = self.cfg.replaying
385 && (replay_serves_blocking_call_from_log(call)
386 || replay_serves_socket_call_from_log(guest, call));
387 debug_assert!(
400 !self.cfg.sequentialize_threads || !syscall_targets_internal_fd(guest, call),
401 "record_or_replay_blocking (BlockingExternalIO) reached for an internal fd \
402 (pipe or socketpair endpoint) on syscall {}; internal fds must use the \
403 InternalIOPolling path",
404 call.name()
405 );
406 let input_access = crate::reserved_input::BackgroundAccess::acquire(guest, run)?;
411 crate::reserved_input::before_native_call(guest, run)?;
412 {
413 let mut rsrcs = Resources::new(dettid);
414 rsrcs.reads_signalfd = matches!(blocking_resource, ResourceID::BlockingExternalIO(_))
417 && call_reads_signalfd(guest, call);
418 if matches!(blocking_resource, ResourceID::BlockingExternalIO(_)) {
419 rsrcs.read_fd =
420 read_family_fd(call).map(|fd| (guest.thread_state().files_id(), fd));
421 }
422 rsrcs.insert(blocking_resource, Permission::RW);
423 rsrcs.fyi(call.name());
424 rsrcs.blocked_signal_mask = blocked_signal_mask;
425 rsrcs.blocked_signal_mask_unknown = blocked_signal_mask_unknown;
426 rsrcs.replay_served_from_log = replay_served_from_log;
427 Detcore::<T>::passes_descriptors_out_outside_the_turn(guest, call);
430 resource_request(guest, rsrcs).await;
431 }
432 after_grant(guest);
433 crate::race_probe::delay(crate::race_probe::BG_PRE_CALL).await;
436 tracing::trace!(
437 "Guest proceeding to execute potentially blocking call {}...",
438 call.name()
439 );
440 let res = {
441 let mut native_guest = reverie::NativeOperationGuest::<_, Self>::optional(
445 guest,
446 input_access
447 .as_ref()
448 .map(|access| access.native_operation()),
449 )?;
450 self.record_or_replay_preserving_tool_errors(&mut native_guest, run)
451 .await
452 };
453 let native_returned = res.is_ok() || matches!(&res, Err(Error::Errno(_)));
454 if native_returned && let Some(access) = &input_access {
455 access.returned()?;
456 }
457 crate::race_probe::delay(crate::race_probe::BG_POST_CALL).await;
458 {
461 let mut rsrcs = Resources::new(dettid);
462 rsrcs.insert(ResourceID::BlockedExternalContinue(op_id), Permission::RW);
463 rsrcs.fyi(call.name());
464 match continuation_mask(call, &res) {
467 ContinuationMask::Temporary => {
468 rsrcs.blocked_signal_mask = blocked_signal_mask;
469 rsrcs.blocked_signal_mask_unknown = blocked_signal_mask_unknown;
470 }
471 ContinuationMask::Unknown => rsrcs.blocked_signal_mask_unknown = true,
472 ContinuationMask::Restored => {}
473 }
474 resource_request(guest, rsrcs).await;
475 }
476 if native_returned && let Some(access) = input_access {
477 access.readmitted()?;
479 }
480 res
481 }
482
483 pub(crate) fn nonblockable_call_runs_outside_turn<G, C>(&self, guest: &mut G, call: C) -> bool
491 where
492 G: Guest<Self>,
493 C: NonblockableSyscall + Into<Syscall> + Copy,
494 {
495 let wrapped: Syscall = call.into();
496 match ioaction_based_on_fd_status(guest, call) {
497 Err(_) => true,
498 Ok(action) => {
499 !self.cfg.sequentialize_threads
500 || (self.cfg.recordreplay_modes && !syscall_targets_internal_fd(guest, wrapped))
501 || action == IOAction::Blocking
502 }
503 }
504 }
505
506 pub async fn execute_nonblockable_fd_syscall<
513 G: Guest<Self>,
514 C: SyscallInfo + NonblockableSyscall + Into<Syscall>,
515 >(
516 &self,
517 guest: &mut G,
518 call: C,
519 ) -> Result<i64, Error> {
520 let wrapped: Syscall = call.into();
521
522 let action = match ioaction_based_on_fd_status(guest, call) {
523 Ok(action) => action,
524 Err(errno) => {
525 tracing::trace!(
531 "NonblockableSyscall: fd classification failed with {}; executing kernel-authoritatively: {}",
532 errno,
533 call.name()
534 );
535 return self.record_or_replay_blocking(guest, wrapped).await;
536 }
537 };
538
539 let internal_fd = syscall_targets_internal_fd(guest, wrapped);
551
552 if !self.cfg.sequentialize_threads
553 || (self.cfg.recordreplay_modes && !internal_fd)
554 || action == IOAction::Blocking
555 {
556 tracing::trace!(
557 "NonblockableSyscall: executing in blocking mode after all: {}",
558 call.name()
559 );
560 Ok(self.record_or_replay_blocking(guest, wrapped).await?)
562 } else if action == IOAction::NonblockizeRetry {
566 tracing::trace!(
567 "NonblockableSyscall: converting to nonblocking syscall (internal polling): {}",
568 call.name()
569 );
570 let mut rsrc = Resources::new(guest.thread_state().dettid);
571 rsrc.insert(ResourceID::InternalIOPolling, Permission::W);
572 rsrc.fyi(call.name());
573 if self.cfg.recordreplay_modes {
577 rsrc.polls_guest_state_only = internal_fd;
578 refresh_guest_poll_state(guest, wrapped, &mut rsrc);
579 } else {
580 (rsrc.polls_guest_state_only, rsrc.relies_on_guest_held_marks) =
581 single_fd_poll_readiness(guest, wrapped, internal_fd);
582 }
583 rsrc.polls_a_named_fifo =
585 internal_fd && get_fd(wrapped).is_some_and(|fd| is_named_fifo(guest, fd));
586 rsrc.private_sigchld_decides = reads_a_pipe(guest, wrapped);
588 rsrc.polls_channel = crate::pipe_wake::poll_of(&wrapped).and_then(|(fd, wants)| {
590 self.pipe_channel_end(guest, fd)
591 .map(|end| Box::new((end.channel, wants)))
592 });
593 let subtool = (self.cfg.recordreplay_modes && internal_fd).then_some(self);
598 Ok(retry_nonblocking_syscall(guest, call, rsrc, subtool).await?)
599 } else {
600 assert!(action == IOAction::PassThru);
601 tracing::trace!(
602 "NonblockableSyscall: just passing it through: {}",
603 call.name()
604 );
605 self.record_or_replay_preserving_tool_errors(guest, wrapped)
607 .await
608 }
609 }
610
611 pub async fn execute_blocking_pipe_writev<G: Guest<Self>>(
619 &self,
620 guest: &mut G,
621 call: syscalls::Writev,
622 expected_open_file: OpenFileId,
623 ) -> Result<i64, Error> {
624 const MAX_IOVECS: usize = 1024;
625 let max_rw_count = crate::page::max_rw_count();
627 const PIPE_BUF: usize = 4096;
629 const STACK_IOVECS: usize = 32;
632
633 let Some(iov_addr) = call.iov() else {
634 return self.execute_nonblockable_fd_syscall(guest, call).await;
635 };
636 if call.len() == 0 || call.len() > MAX_IOVECS {
637 return self.execute_nonblockable_fd_syscall(guest, call).await;
638 }
639
640 let iovecs: Vec<(usize, usize)> = {
641 let mut raw_iovecs = vec![
642 libc::iovec {
643 iov_base: std::ptr::null_mut(),
644 iov_len: 0,
645 };
646 call.len()
647 ];
648 guest.memory().read_values(iov_addr, &mut raw_iovecs)?;
649 raw_iovecs
650 .into_iter()
651 .map(|iovec| (iovec.iov_base as usize, iovec.iov_len))
652 .collect()
653 };
654 let entry_import =
659 EntryImport::capture(&guest.memory(), &Syscall::Writev(call)).map(Box::new);
660 let requested = iovecs.iter().try_fold(0usize, |total, (_, length)| {
661 total.checked_add(*length).ok_or(Errno::EINVAL)
662 })?;
663 if requested > isize::MAX as usize {
664 return Err(Errno::EINVAL.into());
665 }
666 let target = requested.min(max_rw_count);
667 if target == 0 {
668 return self.execute_nonblockable_fd_syscall(guest, call).await;
669 }
670
671 let atomic_pipe_write = target <= PIPE_BUF;
672
673 tracing::trace!(
674 "NonblockableSyscall: converting to nonblocking syscall (internal polling): writev"
675 );
676 let guest_held = syscall_targets_guest_held_fd(guest, call.into());
677 let mut resources = pipe_writev_resources(guest.thread_state().dettid, call, guest_held);
678 resources.polls_a_named_fifo = is_named_fifo(guest, call.fd());
679 resources.polls_guest_state_only = self.cfg.recordreplay_modes
680 || resources.polls_guest_state_only
681 || resources.polls_a_named_fifo;
682 resources.polls_channel = self
683 .pipe_channel_end(guest, call.fd())
684 .map(|end| Box::new((end.channel, crate::pipe_wake::Wants::Space)));
685 let subtool = self.cfg.recordreplay_modes.then_some(self);
686 let mut current = Syscall::Writev(call);
687 let mut written_total = 0usize;
688
689 let blocked_mask = blocked_signal_mask();
694 let mut stack = guest.stack().await;
695 let atomic_scratch_iov = if atomic_pipe_write && iovecs.len() <= STACK_IOVECS {
696 let mut raw_iovecs = [libc::iovec {
697 iov_base: std::ptr::null_mut(),
698 iov_len: 0,
699 }; STACK_IOVECS];
700 for (raw, (base, length)) in raw_iovecs.iter_mut().zip(&iovecs) {
701 raw.iov_base = *base as *mut libc::c_void;
702 raw.iov_len = *length;
703 }
704 let scratch_iov: Addr<libc::iovec> = stack.push(raw_iovecs).cast();
705 Some(scratch_iov.as_raw())
706 } else {
707 None
708 };
709 let blocked_mask_addr = stack.push(blocked_mask);
710 let old_mask_addr = stack.reserve::<KernelSigset>();
711 let turn_mask_cell = stack.reserve::<KernelSigset>();
712 let action_addr = stack.reserve::<KernelSigaction>();
713 let _mask_guard = stack.commit()?;
714 let gated = guest.config().in_guest_signal_delivery;
719 let checks_at_turn =
720 !gated && decides_interruption_at_turn(guest, guest_signal_state_task(guest));
721 let mut first_turn = if checks_at_turn {
745 let turn = read_turn_signals(guest, None, 0, NamedRealTime::ByDisposition).await?;
746 Some((ResumeStatus::Normal, turn))
747 } else {
748 None
749 };
750 let mut turn_wait = checks_at_turn
751 .then(|| KernelSignalWait::new(guest, 0, false, call.signal_interrupt_errno()));
752 let mut turn_mask: Option<KernelSigset> = None;
753 let guest_signal_mask = if checks_at_turn || gated {
754 0
755 } else {
756 block_signals_for_disposition(guest, blocked_mask_addr, old_mask_addr).await?
757 };
758
759 let result: Result<i64, Error> = loop {
760 let written_before = written_total;
761 let (status, turn) = match first_turn.take() {
764 Some((status, turn)) => (status, Some(turn)),
765 None => {
766 if let Some(wait) = turn_wait.as_mut()
767 && turn_mask.is_none()
768 {
769 match install_turn_mask(guest, wait, turn_mask_cell).await {
770 Ok(mask) => turn_mask = Some(mask),
771 Err(Error::Errno(errno)) => {
772 break if written_total > 0 {
773 Ok(written_total as i64)
774 } else {
775 Err(errno.into())
776 };
777 }
778 Err(error) => break Err(error),
779 }
780 }
781 refresh_guest_poll_state(guest, call.into(), &mut resources);
782 let status = resource_request(guest, resources.clone()).await;
783 let turn = if checks_at_turn {
784 match read_turn_signals(
788 guest,
789 turn_mask,
790 committed_signals(&status),
791 NamedRealTime::ByDisposition,
792 )
793 .await
794 {
795 Ok(turn) => Some(turn),
796 Err(error) => break Err(error),
797 }
798 } else {
799 None
800 };
801 (status, turn)
802 }
803 };
804 if turn.is_none() && !gated {
805 let disposition = match wait_signal_disposition(
806 guest,
807 status.clone(),
808 &guest_signal_mask,
809 action_addr,
810 true,
811 )
812 .await
813 {
814 Ok(disposition) => disposition,
815 Err(error) => break Err(error),
816 };
817 if matches!(status, ResumeStatus::Signaled(_))
818 && let Some(result) =
819 interrupted_write_result(&call, written_total, disposition)
820 {
821 break result;
822 }
823 }
824
825 if resources.poll_attempt > 0
826 && !guest
827 .thread_state()
828 .with_detfd(call.fd(), |detfd| {
829 detfd.open_file_id() == expected_open_file
830 })
831 .unwrap_or(false)
832 {
833 break if written_total > 0 {
834 Ok(written_total as i64)
835 } else {
836 self.refuse_unserviceable_operation(guest, Sysno::writev, Errno::EOPNOTSUPP)
837 .await
838 };
839 }
840
841 let own_first_attempt = turn_wait.is_some() && turn_mask.is_none();
848 let result = if atomic_pipe_write && !own_first_attempt {
849 self.execute_atomic_pipe_writev_attempt(guest, call, &iovecs, atomic_scratch_iov)
850 .await
851 } else {
852 let displaced = matches!(current, Syscall::Writev(_))
855 .then(|| entry_import.as_ref())
856 .flatten()
857 .map(|entry| entry.reinstate(&mut guest.memory()));
858 let result = match subtool {
859 Some(detcore) => {
860 detcore
861 .record_or_replay_preserving_tool_errors(guest, current)
862 .await
863 }
864 None => guest.inject_with_retry(current).await.map_err(Error::from),
865 };
866 if let Err(error) =
867 put_back_entry_import(guest, &entry_import, displaced, result.is_ok())
868 {
869 break Err(error);
870 }
871 result
872 };
873 match result {
874 Ok(written) if written > 0 => {
875 let written = match usize::try_from(written) {
876 Ok(written) => written,
877 Err(_) => break Err(Errno::EIO.into()),
878 };
879 written_total = match written_total.checked_add(written) {
880 Some(written_total) => written_total,
881 None => break Err(Errno::EIO.into()),
882 };
883 if written_total >= target {
884 break Ok(written_total as i64);
885 }
886 if atomic_pipe_write {
887 break Ok(written_total as i64);
888 }
889 if turn.is_some_and(|turn| turn.ends(true)) {
893 break Ok(written_total as i64);
894 }
895 if gated {
896 match gated_wait_ends(guest, None) {
897 Ok(true) => break Ok(written_total as i64),
898 Ok(false) => {}
899 Err(error) => break Err(error),
900 }
901 }
902 current = match remaining_writev_segment(
903 call.fd(),
904 &iovecs,
905 written_total,
906 target - written_total,
907 ) {
908 Ok(Some(write)) => Syscall::Write(write),
909 Ok(None) => break Ok(written_total as i64),
910 Err(_) => break Ok(written_total as i64),
911 };
912 }
913 Ok(0) => break Ok(written_total as i64),
914 Err(Error::Errno(Errno::EAGAIN))
915 if turn.is_some_and(|turn| turn.ends(written_total > 0)) =>
916 {
917 break if written_total > 0 {
918 Ok(written_total as i64)
919 } else {
920 Err(call.signal_interrupt_errno().into())
921 };
922 }
923 Err(Error::Errno(Errno::EAGAIN)) => {
924 if gated {
925 match gated_wait_ends(guest, None) {
926 Ok(true) => {
927 break if written_total > 0 {
928 Ok(written_total as i64)
929 } else {
930 Err(call.signal_interrupt_errno().into())
931 };
932 }
933 Ok(false) => {}
934 Err(error) => break Err(error),
935 }
936 }
937 if !atomic_pipe_write && matches!(current, Syscall::Writev(_)) {
938 current = match remaining_writev_segment(call.fd(), &iovecs, 0, target) {
939 Ok(Some(write)) => Syscall::Write(write),
940 Ok(None) => break Ok(0),
941 Err(error) => break Err(error.into()),
942 };
943 }
944 }
945 Err(error) => {
946 break finish_partial_record_or_replay_write(written_total as i64, error);
947 }
948 Ok(_) => break Err(Errno::EIO.into()),
949 }
950
951 resources.previous_attempt_made_progress = written_total > written_before;
952 if resources.previous_attempt_made_progress {
955 self.note_fd_readied(guest, call.fd(), crate::pipe_wake::Wants::Data)
956 .await;
957 }
958 resources.poll_attempt += 1;
959 tracing::trace!(
960 "Retry #{} for {}blocking pipe writev after EAGAIN: {}",
961 resources.poll_attempt,
962 if atomic_pipe_write { "atomic " } else { "" },
963 call.display(&guest.memory())
964 );
965 record_retry_event(guest, call).await;
966 };
967 keep_entry_view(guest, entry_import);
968
969 match turn_wait {
970 Some(mut wait) => {
971 let restored = wait.restore(guest, Some(turn_mask_cell)).await;
972 if turn_mask.is_some() {
973 crate::tool_global::set_saved_guest_sigmask(guest, None).await;
974 }
975 match result_after_restore(restored, result) {
976 Ok(result) => refuse_held_signal_loss(guest, Sysno::writev, result).await,
977 Err(error) => Err(error),
978 }
979 }
980 None if gated => result,
981 None => {
982 restore_signals_after_disposition(guest, old_mask_addr).await?;
983 result
984 }
985 }
986 }
987
988 pub async fn execute_blocking_pipe_write<G: Guest<Self>>(
1006 &self,
1007 guest: &mut G,
1008 call: syscalls::Write,
1009 expected_open_file: OpenFileId,
1010 ) -> Result<i64, Error> {
1011 let max_rw_count = crate::page::max_rw_count();
1012
1013 let target = call.len().min(max_rw_count);
1014 if target == 0 {
1015 return self.execute_nonblockable_fd_syscall(guest, call).await;
1016 }
1017
1018 tracing::trace!(
1019 "NonblockableSyscall: converting to nonblocking syscall (internal polling): write"
1020 );
1021 let mut resources = Resources::new(guest.thread_state().dettid);
1022 resources.insert(ResourceID::InternalIOPolling, Permission::W);
1023 resources.fyi(call.name());
1024 resources.set_signal_interrupt_errno(call.signal_interrupt_errno());
1030 resources.signal_state_task = guest_signal_state_task(guest);
1031 resources.private_sigchld_decides = true;
1034 resources.polls_a_named_fifo = is_named_fifo(guest, call.fd());
1035 resources.relies_on_guest_held_marks = syscall_targets_guest_held_fd(guest, call.into());
1036 resources.polls_guest_state_only = self.cfg.recordreplay_modes
1037 || resources.relies_on_guest_held_marks
1038 || resources.polls_a_named_fifo;
1039 resources.polls_channel = self
1040 .pipe_channel_end(guest, call.fd())
1041 .map(|end| Box::new((end.channel, crate::pipe_wake::Wants::Space)));
1042 let disposition_scratch = if resources.signal_state_task.is_none() {
1049 resources.decides_signal_interruption_itself = true;
1050 let mut stack = guest.stack().await;
1051 let mask_addr = stack.reserve::<KernelSigset>();
1052 let action_addr = stack.reserve::<KernelSigaction>();
1053 let guard = stack.commit()?;
1054 Some((mask_addr, action_addr, guard))
1055 } else {
1056 None
1057 };
1058 let subtool = self.cfg.recordreplay_modes.then_some(self);
1059 let mut current = call;
1060 let mut written_total = 0usize;
1061 let gated = guest.config().in_guest_signal_delivery;
1065 let checks_at_turn =
1066 !gated && decides_interruption_at_turn(guest, resources.signal_state_task);
1067 let mut turn_wait = checks_at_turn
1074 .then(|| KernelSignalWait::new(guest, 0, false, call.signal_interrupt_errno()));
1075 let mut published = PublishedWaitMask::default();
1076
1077 let result: Result<i64, Error> = loop {
1078 let written_before = written_total;
1079 let status = if resources.poll_attempt > 0 {
1085 refresh_guest_poll_state(guest, call.into(), &mut resources);
1086 Some(resource_request(guest, resources.clone()).await)
1087 } else {
1088 None
1089 };
1090 let turn = if checks_at_turn {
1099 let committed = status.as_ref().map_or(0, committed_signals);
1100 match turn_signals_with_block(
1104 guest,
1105 &mut turn_wait,
1106 if resources.poll_attempt > 0 {
1107 ProbeMaskPolicy::PendingSignals
1108 } else {
1109 ProbeMaskPolicy::Guest
1110 },
1111 committed,
1112 resources.private_sigchld_decides,
1113 &mut published,
1114 )
1115 .await
1116 {
1117 Ok(turn) => Some(turn),
1118 Err(error) => break Err(error),
1119 }
1120 } else {
1121 None
1122 };
1123 let signaled = if turn.is_some() || gated {
1124 false
1125 } else {
1126 match (&status, &disposition_scratch) {
1127 (Some(ResumeStatus::Signaled(_)), None) => true,
1128 (
1129 Some(status @ ResumeStatus::Signaled(_)),
1130 Some((mask_addr, action_addr, _)),
1131 ) => {
1132 match own_disposition_interrupts(
1133 guest,
1134 status.clone(),
1135 *mask_addr,
1136 *action_addr,
1137 )
1138 .await
1139 {
1140 Ok(interrupts) => interrupts,
1141 Err(error) => break Err(error),
1142 }
1143 }
1144 _ => false,
1145 }
1146 };
1147
1148 if resources.poll_attempt > 0
1149 && !guest
1150 .thread_state()
1151 .with_detfd(call.fd(), |detfd| {
1152 detfd.open_file_id() == expected_open_file
1153 })
1154 .unwrap_or(false)
1155 {
1156 break if written_total > 0 {
1157 Ok(written_total as i64)
1158 } else {
1159 self.refuse_unserviceable_operation(guest, Sysno::write, Errno::EOPNOTSUPP)
1160 .await
1161 };
1162 }
1163
1164 let result = match subtool {
1165 Some(detcore) => {
1166 detcore
1167 .record_or_replay_preserving_tool_errors(guest, current)
1168 .await
1169 }
1170 None => guest.inject_with_retry(current).await.map_err(Error::from),
1171 };
1172 match result {
1175 Ok(written) if written > 0 => {
1176 let Ok(written) = usize::try_from(written) else {
1177 break Err(Errno::EIO.into());
1178 };
1179 let Some(remaining) = target.checked_sub(written_total) else {
1180 break Err(Errno::EIO.into());
1181 };
1182 if written > remaining {
1183 break Err(Errno::EIO.into());
1184 }
1185 let Some(total) = written_total.checked_add(written) else {
1186 break Err(Errno::EIO.into());
1187 };
1188 written_total = total;
1189 if written_total == target || turn.map_or(signaled, |turn| turn.ends(true)) {
1192 break Ok(written_total as i64);
1193 }
1194 if gated {
1195 match gated_wait_ends(guest, None) {
1196 Ok(true) => break Ok(written_total as i64),
1197 Ok(false) => {}
1198 Err(error) => break Err(error),
1199 }
1200 }
1201 let Some(buffer) = call.buf() else {
1202 break Err(Errno::EFAULT.into());
1203 };
1204 let Some(next_buffer) = buffer
1205 .as_raw()
1206 .checked_add(written_total)
1207 .and_then(Addr::<u8>::from_raw)
1208 else {
1209 break finish_partial_record_or_replay_write(
1210 written_total as i64,
1211 Errno::EFAULT.into(),
1212 );
1213 };
1214 current = call
1215 .with_buf(Some(next_buffer))
1216 .with_len(target - written_total);
1217 }
1218 Ok(0) => break Ok(written_total as i64),
1219 Err(Error::Errno(Errno::EAGAIN))
1220 if turn.map_or(signaled, |turn| turn.ends(written_total > 0)) =>
1221 {
1222 break if written_total > 0 {
1223 Ok(written_total as i64)
1224 } else {
1225 Err(call.signal_interrupt_errno().into())
1226 };
1227 }
1228 Err(Error::Errno(Errno::EAGAIN)) => {
1229 if gated {
1230 match gated_wait_ends(guest, None) {
1231 Ok(true) => {
1232 break if written_total > 0 {
1233 Ok(written_total as i64)
1234 } else {
1235 Err(call.signal_interrupt_errno().into())
1236 };
1237 }
1238 Ok(false) => {}
1239 Err(error) => break Err(error),
1240 }
1241 }
1242 }
1243 Err(error) => {
1244 break finish_partial_record_or_replay_write(written_total as i64, error);
1245 }
1246 Ok(_) => break Err(Errno::EIO.into()),
1247 }
1248
1249 resources.previous_attempt_made_progress = written_total > written_before;
1250 if resources.previous_attempt_made_progress {
1253 self.note_fd_readied(guest, call.fd(), crate::pipe_wake::Wants::Data)
1254 .await;
1255 }
1256 resources.poll_attempt += 1;
1257 tracing::trace!(
1258 "Retry #{} for blocking pipe write: {}",
1259 resources.poll_attempt,
1260 call.display(&guest.memory())
1261 );
1262 record_retry_event(guest, call).await;
1263 };
1264
1265 match turn_wait {
1269 Some(mut wait) if wait.effective_mask().is_some() => {
1270 let restored = wait.restore(guest, None).await;
1271 publish_wait_mask(guest, None, &mut published).await;
1272 match result_after_restore(restored, result) {
1273 Ok(result) => refuse_held_signal_loss(guest, Sysno::write, result).await,
1274 Err(error) => Err(error),
1275 }
1276 }
1277 _ => result,
1278 }
1279 }
1280
1281 async fn execute_atomic_pipe_writev_attempt<G: Guest<Self>>(
1282 &self,
1283 guest: &mut G,
1284 call: syscalls::Writev,
1285 iovecs: &[(usize, usize)],
1286 stack_iov: Option<usize>,
1287 ) -> Result<i64, Error> {
1288 if let Some(stack_iov) = stack_iov {
1289 let scratch_call = call.with_iov(Addr::from_raw(stack_iov));
1290 return if self.cfg.recordreplay_modes {
1291 self.record_or_replay_preserving_tool_errors(guest, scratch_call)
1292 .await
1293 } else {
1294 guest
1295 .inject_with_retry(scratch_call)
1296 .await
1297 .map_err(Error::from)
1298 };
1299 }
1300
1301 let mapping_len = iovecs
1302 .len()
1303 .checked_mul(std::mem::size_of::<libc::iovec>())
1304 .expect("validated iovec count cannot overflow scratch length");
1305 let mapped = guest
1306 .inject_with_retry(Syscall::Mmap(
1307 syscalls::Mmap::new()
1308 .with_addr(None)
1309 .with_len(mapping_len)
1310 .with_prot(ProtFlags::PROT_READ | ProtFlags::PROT_WRITE)
1311 .with_flags(MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS)
1312 .with_fd(-1)
1313 .with_offset(0),
1314 ))
1315 .await
1316 .unwrap_or_else(|error| panic!("failed to map atomic writev scratch: {error}"));
1317 let mapped = usize::try_from(mapped)
1318 .unwrap_or_else(|_| panic!("atomic writev scratch mmap returned {mapped}"));
1319 let scratch_iov = Addr::<libc::iovec>::from_raw(mapped)
1320 .unwrap_or_else(|| panic!("atomic writev scratch mmap returned a null address"));
1321 let mapping_addr: Addr<libc::c_void> = scratch_iov.cast();
1322 let write_result = {
1323 let raw_iovecs: Vec<libc::iovec> = iovecs
1324 .iter()
1325 .map(|(base, length)| libc::iovec {
1326 iov_base: *base as *mut libc::c_void,
1327 iov_len: *length,
1328 })
1329 .collect();
1330 guest
1332 .memory()
1333 .write_values(unsafe { scratch_iov.into_mut() }, &raw_iovecs)
1334 };
1335 if let Err(write_error) = write_result {
1336 guest
1337 .inject_with_retry(Syscall::Munmap(
1338 syscalls::Munmap::new()
1339 .with_addr(Some(mapping_addr))
1340 .with_len(mapping_len),
1341 ))
1342 .await
1343 .unwrap_or_else(|cleanup_error| {
1344 panic!(
1345 "failed to populate atomic writev scratch ({write_error}); cleanup failed ({cleanup_error})"
1346 )
1347 });
1348 panic!("failed to populate atomic writev scratch: {write_error}");
1349 }
1350
1351 let scratch_call = call.with_iov(Some(scratch_iov));
1352 let result = if self.cfg.recordreplay_modes {
1353 self.record_or_replay_preserving_tool_errors(guest, scratch_call)
1354 .await
1355 } else {
1356 guest
1357 .inject_with_retry(scratch_call)
1358 .await
1359 .map_err(Error::from)
1360 };
1361 guest
1362 .inject_with_retry(Syscall::Munmap(
1363 syscalls::Munmap::new()
1364 .with_addr(Some(mapping_addr))
1365 .with_len(mapping_len),
1366 ))
1367 .await
1368 .unwrap_or_else(|error| panic!("failed to unmap atomic writev scratch: {error}"));
1369 result
1370 }
1371
1372 pub fn maybe_set_nonblocking_fd<G: Guest<Self>>(&self, guest: &G, fd: i32) {
1374 if self.cfg.sequentialize_threads && !self.cfg.debug_externalize_sockets {
1375 guest
1376 .thread_state()
1377 .with_detfd(fd, |detfd| {
1378 detfd.set_physically_nonblocking();
1379 })
1380 .unwrap();
1381 }
1382 }
1383}
1384
1385fn remaining_writev_segment(
1386 fd: i32,
1387 iovecs: &[(usize, usize)],
1388 mut consumed: usize,
1389 remaining_limit: usize,
1390) -> Result<Option<syscalls::Write>, Errno> {
1391 for (base, length) in iovecs {
1392 if consumed >= *length {
1393 consumed -= *length;
1394 continue;
1395 }
1396 let base = base.checked_add(consumed).ok_or(Errno::EFAULT)?;
1397 let buffer = Addr::<u8>::from_raw(base).ok_or(Errno::EFAULT)?;
1398 return Ok(Some(
1399 syscalls::Write::new()
1400 .with_fd(fd)
1401 .with_buf(Some(buffer))
1402 .with_len((*length - consumed).min(remaining_limit)),
1403 ));
1404 }
1405 Ok(None)
1406}
1407
1408#[derive(PartialEq, Eq, Debug)]
1410pub enum IOAction {
1411 Blocking,
1413 NonblockizeRetry,
1415 PassThru,
1417}
1418
1419pub fn ioaction_based_on_fd_status<
1424 G: Guest<Detcore<T>>,
1425 T: RecordOrReplay,
1426 C: NonblockableSyscall + Into<Syscall>,
1427>(
1428 guest: &mut G,
1429 call: C,
1430) -> Result<IOAction, Errno> {
1431 let wrapped: Syscall = call.into();
1432 let fd = get_fd(wrapped).unwrap_or_else(|| panic!("Failed to get fd for {}", call.name()));
1433 let (phys, virt, error_queue_never_waits) = guest.thread_state().with_detfd(fd, |detfd| {
1434 (
1435 detfd.physically_nonblocking(),
1436 detfd.is_nonblocking(),
1437 detfd.error_queue_never_waits(),
1438 )
1439 })?;
1440 tracing::trace!(
1441 "Checking FD {} for nonblocking: physical {} / virtual {}",
1442 fd,
1443 phys,
1444 virt
1445 );
1446 let action = ioaction_for_fd_status(fd, virt, phys, call.flags_forbid_waiting());
1447 if action == IOAction::NonblockizeRetry && error_queue_never_waits && call.reads_error_queue() {
1450 Ok(IOAction::PassThru)
1451 } else {
1452 Ok(action)
1453 }
1454}
1455
1456pub(crate) fn ioaction_based_on_fd<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1460 guest: &mut G,
1461 fd: i32,
1462) -> Result<IOAction, Errno> {
1463 let (phys, virt) = guest.thread_state().with_detfd(fd, |detfd| {
1464 (detfd.physically_nonblocking(), detfd.is_nonblocking())
1465 })?;
1466 Ok(ioaction_for_fd_status(fd, virt, phys, false))
1467}
1468
1469fn ioaction_for_fd_status(fd: i32, virt: bool, phys: bool, flags_forbid_waiting: bool) -> IOAction {
1473 if virt && !phys {
1474 panic!(
1476 "Invariant violation, fd {fd}: we cannot simulate nonblocking behavior when set to blocking mode in the kernel."
1477 );
1478 } else if !virt && !phys {
1479 IOAction::Blocking
1481 } else if virt && phys {
1482 IOAction::PassThru
1484 } else if flags_forbid_waiting {
1485 IOAction::PassThru
1488 } else {
1489 IOAction::NonblockizeRetry
1491 }
1492}
1493
1494pub fn syscall_targets_internal_fd<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1530 guest: &mut G,
1531 call: Syscall,
1532) -> bool {
1533 match call {
1536 Syscall::EpollPwait(_) => return false,
1537 #[cfg(target_arch = "x86_64")]
1538 Syscall::EpollWait(_) => return false,
1539 _ => {}
1540 }
1541 match get_fd(call) {
1542 Some(fd) => guest
1543 .thread_state()
1544 .with_detfd(fd, |detfd| {
1545 if matches!(call, Syscall::Read(_) | Syscall::Readv(_))
1548 && detfd.inherited_external_read().is_some()
1549 {
1550 return false;
1551 }
1552 matches!(detfd.ty(), FdType::Pipe) || detfd.is_internal_socket()
1553 })
1554 .unwrap_or(false),
1555 None => false,
1556 }
1557}
1558
1559fn syscall_targets_guest_held_fd<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1565 guest: &mut G,
1566 call: Syscall,
1567) -> bool {
1568 if matches!(call, Syscall::EpollWait(_) | Syscall::EpollPwait(_)) {
1569 return false;
1570 }
1571 get_fd(call).is_some_and(|fd| {
1572 guest
1573 .thread_state()
1574 .with_detfd(fd, |detfd| detfd.is_guest_held())
1575 .unwrap_or(false)
1576 })
1577}
1578
1579fn single_fd_poll_readiness<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1588 guest: &mut G,
1589 call: Syscall,
1590 internal_fd: bool,
1591) -> (bool, bool) {
1592 let socketpair = get_fd(call).is_some_and(|fd| {
1593 guest
1594 .thread_state()
1595 .with_detfd(fd, |detfd| detfd.is_socketpair_endpoint())
1596 .unwrap_or(false)
1597 });
1598 let relies_on_guest_held_marks =
1599 (!internal_fd || socketpair) && syscall_targets_guest_held_fd(guest, call);
1600 let polls_guest_state_only = (internal_fd && !socketpair) || relies_on_guest_held_marks;
1601 (polls_guest_state_only, relies_on_guest_held_marks)
1602}
1603
1604const GUEST_HELD_POLL_MAX_FDS: usize = 1024;
1607
1608pub(crate) fn poll_targets_guest_held<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1617 guest: &mut G,
1618 fds: Option<usize>,
1619 nfds: usize,
1620) -> bool {
1621 if guest.config().recordreplay_modes || nfds == 0 || nfds > GUEST_HELD_POLL_MAX_FDS {
1622 return false;
1623 }
1624 let Some(address) = fds.and_then(Addr::<u8>::from_raw) else {
1625 return false;
1626 };
1627 let mut bytes = vec![0u8; nfds * std::mem::size_of::<libc::pollfd>()];
1628 if guest.memory().read_exact(address, &mut bytes).is_err() {
1629 return false;
1630 }
1631 let polled = pollfd_descriptors(&bytes);
1632 !polled.is_empty()
1633 && polled.into_iter().all(|fd| {
1634 guest
1635 .thread_state()
1636 .with_detfd(fd, |detfd| detfd.is_guest_held())
1637 .unwrap_or(false)
1638 })
1639}
1640
1641pub(crate) fn refresh_guest_poll_state<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1650 guest: &mut G,
1651 call: Syscall,
1652 resources: &mut Resources,
1653) {
1654 if guest.config().recordreplay_modes {
1655 resources.relies_on_guest_held_marks = false;
1656 return;
1657 }
1658 let held = match call {
1659 #[cfg(target_arch = "x86_64")]
1660 Syscall::Poll(call) => poll_targets_guest_held(
1661 guest,
1662 call.fds().map(|fds| fds.as_raw()),
1663 call.nfds() as usize,
1664 ),
1665 Syscall::Ppoll(call) => poll_targets_guest_held(
1666 guest,
1667 call.fds().map(|fds| fds.as_raw()),
1668 call.nfds() as usize,
1669 ),
1670 _ if get_fd(call).is_some() => syscall_targets_guest_held_fd(guest, call),
1671 _ => return,
1672 };
1673 resources.relies_on_guest_held_marks = held;
1674 resources.polls_guest_state_only = held;
1675}
1676
1677fn pollfd_descriptors(bytes: &[u8]) -> Vec<i32> {
1680 let (entries, _) = bytes.as_chunks::<{ std::mem::size_of::<libc::pollfd>() }>();
1681 entries
1682 .iter()
1683 .map(|entry| i32::from_ne_bytes([entry[0], entry[1], entry[2], entry[3]]))
1684 .filter(|fd| *fd >= 0)
1685 .collect()
1686}
1687
1688pub(crate) struct GuestHeldPollProof {
1701 call: Syscall,
1702 targets: Option<GuestHeldPollTargets>,
1703 claimed: bool,
1706}
1707
1708#[derive(PartialEq, Eq)]
1709enum GuestHeldPollTargets {
1710 Single(i32, OpenFileId),
1711 Poll {
1712 address: usize,
1713 entries: Vec<(i32, Option<OpenFileId>)>,
1714 },
1715}
1716
1717impl GuestHeldPollProof {
1718 pub(crate) fn capture<T, G>(guest: &mut G, call: Syscall, resources: &mut Resources) -> Self
1719 where
1720 T: RecordOrReplay,
1721 G: Guest<Detcore<T>>,
1722 {
1723 let targets = resources
1724 .relies_on_guest_held_marks
1725 .then(|| Self::read_targets(guest, call))
1726 .flatten();
1727 if resources.relies_on_guest_held_marks && targets.is_none() {
1731 Self::clear(resources);
1732 }
1733 Self {
1734 claimed: resources.relies_on_guest_held_marks,
1735 call,
1736 targets,
1737 }
1738 }
1739
1740 pub(crate) fn revalidate<T, G>(&mut self, guest: &mut G, resources: &mut Resources)
1741 where
1742 T: RecordOrReplay,
1743 G: Guest<Detcore<T>>,
1744 {
1745 if self.targets.is_some() && Self::read_targets(guest, self.call) != self.targets {
1746 self.targets = None;
1747 Self::clear(resources);
1748 } else if self.targets.is_none() && (self.claimed || resources.relies_on_guest_held_marks) {
1749 Self::clear(resources);
1756 }
1757 }
1758
1759 fn clear(resources: &mut Resources) {
1760 resources.polls_guest_state_only = false;
1761 resources.relies_on_guest_held_marks = false;
1762 }
1763
1764 fn identity<T, G>(guest: &mut G, fd: i32) -> Option<OpenFileId>
1765 where
1766 T: RecordOrReplay,
1767 G: Guest<Detcore<T>>,
1768 {
1769 guest
1770 .thread_state()
1771 .with_detfd(fd, |entry| {
1772 entry.is_guest_held().then(|| entry.open_file_id())
1773 })
1774 .ok()
1775 .flatten()
1776 }
1777
1778 fn read_targets<T, G>(guest: &mut G, call: Syscall) -> Option<GuestHeldPollTargets>
1779 where
1780 T: RecordOrReplay,
1781 G: Guest<Detcore<T>>,
1782 {
1783 let (address, nfds) = match call {
1784 Syscall::Poll(call) => (call.fds()?.as_raw(), call.nfds() as usize),
1785 Syscall::Ppoll(call) => (call.fds()?.as_raw(), call.nfds() as usize),
1786 _ => {
1787 let fd = get_fd(call)?;
1788 return Some(GuestHeldPollTargets::Single(fd, Self::identity(guest, fd)?));
1789 }
1790 };
1791 if nfds == 0 || nfds > GUEST_HELD_POLL_MAX_FDS {
1792 return None;
1793 }
1794 let mut bytes = vec![0; nfds * std::mem::size_of::<libc::pollfd>()];
1795 guest
1796 .memory()
1797 .read_exact(Addr::from_raw(address)?, &mut bytes)
1798 .ok()?;
1799 let mut entries = Vec::with_capacity(nfds);
1800 for entry in bytes
1801 .as_chunks::<{ std::mem::size_of::<libc::pollfd>() }>()
1802 .0
1803 {
1804 let fd = i32::from_ne_bytes([entry[0], entry[1], entry[2], entry[3]]);
1805 entries.push((
1806 fd,
1807 if fd < 0 {
1808 None
1809 } else {
1810 Some(Self::identity(guest, fd)?)
1811 },
1812 ));
1813 }
1814 entries
1818 .iter()
1819 .any(|(fd, _)| *fd >= 0)
1820 .then_some(GuestHeldPollTargets::Poll { address, entries })
1821 }
1822}
1823
1824fn is_named_fifo<G: Guest<Detcore<T>>, T: RecordOrReplay>(guest: &mut G, fd: i32) -> bool {
1827 guest
1828 .thread_state()
1829 .with_detfd(fd, |detfd| detfd.is_named_fifo())
1830 .unwrap_or(false)
1831}
1832
1833fn reads_a_pipe<G: Guest<Detcore<T>>, T: RecordOrReplay>(guest: &mut G, call: Syscall) -> bool {
1840 matches!(call, Syscall::Read(_) | Syscall::Readv(_))
1841 && get_fd(call).is_some_and(|fd| {
1842 guest
1843 .thread_state()
1844 .with_detfd(fd, |detfd| matches!(detfd.ty(), FdType::Pipe))
1845 .unwrap_or(false)
1846 })
1847}
1848
1849fn call_reads_signalfd<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1853 guest: &mut G,
1854 call: Syscall,
1855) -> bool {
1856 let fd = match call {
1857 Syscall::Read(c) => c.fd(),
1858 Syscall::Readv(c) => c.fd(),
1859 Syscall::Preadv2(c) => c.fd(),
1860 _ => return false,
1861 };
1862 guest
1863 .thread_state()
1864 .with_detfd(fd, |detfd| detfd.ty() == crate::fd::FdType::Signalfd)
1865 .unwrap_or(false)
1866}
1867
1868fn read_family_fd(call: Syscall) -> Option<i32> {
1870 match call {
1871 Syscall::Read(c) => Some(c.fd()),
1872 Syscall::Readv(c) => Some(c.fd()),
1873 Syscall::Preadv2(c) => Some(c.fd()),
1874 _ => None,
1875 }
1876}
1877
1878fn replay_serves_socket_call_from_log<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1892 guest: &mut G,
1893 call: Syscall,
1894) -> bool {
1895 let fd = match call {
1896 Syscall::Accept(_)
1897 | Syscall::Accept4(_)
1898 | Syscall::Connect(_)
1899 | Syscall::Sendto(_)
1900 | Syscall::Sendmsg(_)
1901 | Syscall::Recvfrom(_)
1902 | Syscall::Recvmsg(_) => return true,
1903 Syscall::Read(s) => s.fd(),
1904 Syscall::Readv(s) => s.fd(),
1905 Syscall::Write(s) => s.fd(),
1906 Syscall::Writev(s) => s.fd(),
1907 _ => return false,
1908 };
1909 is_log_served_socket(guest, fd)
1910}
1911
1912fn replay_serves_blocking_call_from_log(call: Syscall) -> bool {
1918 match call {
1919 Syscall::Ppoll(_) | Syscall::Pselect6(_) => true,
1920 #[cfg(target_arch = "x86_64")]
1921 Syscall::Poll(_) | Syscall::Select(_) => true,
1922 _ => false,
1923 }
1924}
1925
1926fn is_log_served_socket<G: Guest<Detcore<T>>, T: RecordOrReplay>(guest: &mut G, fd: i32) -> bool {
1932 guest
1933 .thread_state()
1934 .with_detfd(fd, |detfd| {
1935 matches!(detfd.ty(), FdType::Socket) && !detfd.is_socketpair_endpoint()
1936 })
1937 .unwrap_or(false)
1938}
1939
1940pub(crate) fn is_accepted_connection<G: Guest<Detcore<T>>, T: RecordOrReplay>(
1945 guest: &mut G,
1946 fd: i32,
1947) -> bool {
1948 guest
1949 .thread_state()
1950 .with_detfd(fd, |detfd| detfd.is_accepted_connection())
1951 .unwrap_or(false)
1952}
1953
1954pub(crate) fn get_fd(s: Syscall) -> Option<i32> {
1960 match s {
1961 Syscall::Recvfrom(s) => Some(s.fd()),
1962 Syscall::Recvmsg(s) => Some(s.sockfd()),
1963 Syscall::Recvmmsg(s) => Some(s.fd()),
1964 Syscall::Sendto(s) => Some(s.fd()),
1965 Syscall::Sendmsg(s) => Some(s.fd()),
1966 Syscall::Sendmmsg(s) => Some(s.sockfd()),
1967 Syscall::Accept(s) => Some(s.sockfd()),
1968 Syscall::Accept4(s) => Some(s.sockfd()),
1969 Syscall::Connect(s) => Some(s.fd()),
1970 Syscall::Bind(s) => Some(s.fd()),
1971 Syscall::Listen(s) => Some(s.fd()),
1972 Syscall::Getsockname(s) => Some(s.fd()),
1973 Syscall::Getpeername(s) => Some(s.fd()),
1974 Syscall::Setsockopt(s) => Some(s.fd()),
1975 Syscall::Getsockopt(s) => Some(s.fd()),
1976
1977 Syscall::Read(s) => Some(s.fd()),
1978 Syscall::Write(s) => Some(s.fd()),
1979 Syscall::Close(s) => Some(s.fd()),
1980 Syscall::Fstat(s) => Some(s.fd()),
1981 Syscall::Lseek(s) => Some(s.fd()),
1982 Syscall::Mmap(s) => Some(s.fd()),
1983 Syscall::Ioctl(s) => Some(s.fd()),
1984 Syscall::Pread64(s) => Some(s.fd()),
1985 Syscall::Pwrite64(s) => Some(s.fd()),
1986 Syscall::Readv(s) => Some(s.fd()),
1987 Syscall::Writev(s) => Some(s.fd()),
1988
1989 Syscall::Shutdown(s) => Some(s.fd()),
1990 Syscall::Fcntl(s) => Some(s.fd()),
1991 Syscall::Flock(s) => Some(s.fd()),
1992 Syscall::Fsync(s) => Some(s.fd()),
1993 Syscall::Fdatasync(s) => Some(s.fd()),
1994 Syscall::Ftruncate(s) => Some(s.fd()),
1995 Syscall::Fchdir(s) => Some(s.fd()),
1996 Syscall::Fchmod(s) => Some(s.fd()),
1997 Syscall::Fchown(s) => Some(s.fd()),
1998 Syscall::Fstatfs(s) => Some(s.fd()),
1999 Syscall::Readahead(s) => Some(s.fd()),
2000 Syscall::Fsetxattr(s) => Some(s.fd()),
2001 Syscall::Fgetxattr(s) => Some(s.fd()),
2002 Syscall::Flistxattr(s) => Some(s.fd()),
2003 Syscall::Fremovexattr(s) => Some(s.fd()),
2004 Syscall::Fadvise64(s) => Some(s.fd()),
2005 Syscall::InotifyAddWatch(s) => Some(s.fd()),
2006 Syscall::InotifyRmWatch(s) => Some(s.fd()),
2007 #[cfg(target_arch = "x86_64")]
2008 Syscall::SyncFileRange(s) => Some(s.fd()),
2009 Syscall::Vmsplice(s) => Some(s.fd()),
2010 Syscall::Utimensat(s) => Some(s.dirfd()),
2011 #[cfg(target_arch = "x86_64")]
2012 Syscall::Signalfd(s) => Some(s.fd()),
2013 Syscall::Fallocate(s) => Some(s.fd()),
2014 Syscall::TimerfdSettime(s) => Some(s.fd()),
2015 Syscall::TimerfdGettime(s) => Some(s.fd()),
2016 Syscall::Signalfd4(s) => Some(s.fd()),
2017 Syscall::Preadv(s) => Some(s.fd()),
2018 Syscall::Pwritev(s) => Some(s.fd()),
2019 Syscall::Syncfs(s) => Some(s.fd()),
2020 Syscall::Setns(s) => Some(s.fd()),
2021 Syscall::FinitModule(s) => Some(s.fd()),
2022 Syscall::Preadv2(s) => Some(s.fd()),
2023 Syscall::Pwritev2(s) => Some(s.fd()),
2024
2025 Syscall::Openat(s) => Some(s.dirfd()),
2026 Syscall::Mkdirat(s) => Some(s.dirfd()),
2027 Syscall::Mknodat(s) => Some(s.dirfd()),
2028 Syscall::Fchownat(s) => Some(s.dirfd()),
2029 #[cfg(target_arch = "x86_64")]
2030 Syscall::Futimesat(s) => Some(s.dirfd()),
2031 #[cfg(target_arch = "x86_64")]
2032 Syscall::Newfstatat(s) => Some(s.dirfd()),
2033 #[cfg(target_arch = "aarch64")]
2034 Syscall::Fstatat(s) => Some(s.dirfd()),
2035 Syscall::Unlinkat(s) => Some(s.dirfd()),
2036 Syscall::Readlinkat(s) => Some(s.dirfd()),
2037 Syscall::Fchmodat(s) => Some(s.dirfd()),
2038 Syscall::Faccessat(s) => Some(s.dirfd()),
2039 Syscall::NameToHandleAt(s) => Some(s.dirfd()),
2040 Syscall::Execveat(s) => Some(s.dirfd()),
2041 Syscall::Statx(s) => Some(s.dirfd()),
2042 Syscall::Symlinkat(s) => Some(s.newdirfd()),
2043 Syscall::PerfEventOpen(s) => Some(s.group_fd()),
2044 Syscall::OpenByHandleAt(s) => Some(s.mount_fd()),
2045
2046 Syscall::EpollCtl(s) => Some(s.epfd()),
2047 #[cfg(target_arch = "x86_64")]
2048 Syscall::EpollWait(s) => Some(s.epfd()),
2049 Syscall::EpollPwait(s) => Some(s.epfd()),
2050
2051 #[cfg(target_arch = "x86_64")]
2053 Syscall::Dup2(_) => None,
2054 Syscall::Sendfile(_) => None,
2056 Syscall::Renameat(_) => None,
2058 Syscall::Linkat(_) => None,
2060 Syscall::FanotifyMark(_) => None,
2062 Syscall::Renameat2(_) => None,
2064 Syscall::Dup3(_) => None,
2066 Syscall::KexecLoad(_) => None,
2068
2069 #[cfg(target_arch = "x86_64")]
2071 Syscall::Poll(_) => None,
2072 Syscall::Ppoll(_) => None,
2074
2075 _ => None,
2076 }
2077}
2078
2079#[allow(clippy::double_must_use)]
2087#[async_trait]
2088pub trait NonblockableSyscall: SyscallInfo {
2089 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2094 self,
2095 guest: &mut G,
2096 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>);
2097
2098 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2101 res == Ok(0)
2102 }
2103
2104 fn flags_forbid_waiting(&self) -> bool {
2112 false
2113 }
2114
2115 fn reads_error_queue(&self) -> bool {
2121 false
2122 }
2123
2124 fn signal_interrupt_errno(&self) -> Errno {
2127 Errno::ERESTARTSYS
2128 }
2129
2130 fn kernel_restart_errno(&self) -> Errno {
2137 self.signal_interrupt_errno()
2138 }
2139
2140 fn restart_keeps_deadline(&self) -> bool {
2152 false
2153 }
2154
2155 fn signals_consumed_by_wait<M: MemoryAccess>(&self, _memory: &M) -> KernelSigset {
2158 0
2159 }
2160
2161 fn ends_at_read_shutdown(&self) -> bool {
2164 false
2165 }
2166
2167 fn normalize_nonblocking_result(
2170 &self,
2171 res: Result<i64, Errno>,
2172 _retried: bool,
2173 ) -> Result<i64, Errno> {
2174 res
2175 }
2176}
2177
2178pub trait TimeoutableSyscall: SyscallInfo {
2181 fn timeout_return_val(&self) -> Result<i64, Errno>;
2183}
2184
2185fn interrupted_write_result<C: NonblockableSyscall>(
2186 call: &C,
2187 written_total: usize,
2188 disposition: Option<WaitSignalDisposition>,
2189) -> Option<Result<i64, Error>> {
2190 match disposition {
2191 None => None,
2192 Some(_) if written_total > 0 => Some(Ok(written_total as i64)),
2193 Some(_) => Some(Err(call.signal_interrupt_errno().into())),
2194 }
2195}
2196
2197fn pipe_writev_resources(
2198 dettid: DetTid,
2199 call: reverie::syscalls::Writev,
2200 guest_held: bool,
2201) -> Resources {
2202 let mut resources = Resources::new(dettid);
2203 resources.insert(ResourceID::InternalIOPolling, Permission::W);
2204 resources.fyi(call.name());
2205 resources.set_signal_interrupt_errno(call.signal_interrupt_errno());
2206 resources.decides_signal_interruption_itself = true;
2210 resources.polls_guest_state_only = guest_held;
2211 resources.relies_on_guest_held_marks = guest_held;
2212 resources
2213}
2214
2215#[cfg(target_arch = "x86_64")]
2216#[async_trait]
2217impl NonblockableSyscall for reverie::syscalls::Poll {
2218 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2219 self,
2220 _guest: &mut G,
2221 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2222 (self.with_timeout(0), None)
2223 }
2224
2225 fn signal_interrupt_errno(&self) -> Errno {
2226 Errno::EINTR
2227 }
2228
2229 fn kernel_restart_errno(&self) -> Errno {
2239 if self.restart_keeps_deadline() {
2240 Errno::ERESTART_RESTARTBLOCK
2241 } else {
2242 Errno::ERESTARTNOHAND
2243 }
2244 }
2245
2246 fn restart_keeps_deadline(&self) -> bool {
2248 self.timeout() > 0
2249 }
2250}
2251
2252#[cfg(target_arch = "x86_64")]
2253impl TimeoutableSyscall for reverie::syscalls::Poll {
2254 fn timeout_return_val(&self) -> Result<i64, Errno> {
2255 Ok(0)
2256 }
2257}
2258
2259#[async_trait]
2260impl NonblockableSyscall for reverie::syscalls::Ppoll {
2261 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2262 self,
2263 guest: &mut G,
2264 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2265 let (tp, guard) = zero_timespec(guest).await;
2266 let tp = unsafe { tp.into_mut() };
2268 (self.with_timeout(Some(tp)), Some(guard))
2269 }
2270
2271 fn signal_interrupt_errno(&self) -> Errno {
2272 Errno::EINTR
2273 }
2274
2275 fn kernel_restart_errno(&self) -> Errno {
2278 Errno::ERESTARTNOHAND
2279 }
2280}
2281
2282impl TimeoutableSyscall for reverie::syscalls::Ppoll {
2283 fn timeout_return_val(&self) -> Result<i64, Errno> {
2284 Ok(0)
2285 }
2286}
2287
2288#[cfg(target_arch = "x86_64")]
2289#[async_trait]
2290impl NonblockableSyscall for reverie::syscalls::EpollWait {
2291 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2292 self,
2293 _guest: &mut G,
2294 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2295 (self.with_timeout(0), None)
2296 }
2297
2298 fn signal_interrupt_errno(&self) -> Errno {
2299 Errno::EINTR
2300 }
2301}
2302
2303#[cfg(target_arch = "x86_64")]
2304impl TimeoutableSyscall for reverie::syscalls::EpollWait {
2305 fn timeout_return_val(&self) -> Result<i64, Errno> {
2306 Ok(0)
2307 }
2308}
2309
2310#[async_trait]
2321impl NonblockableSyscall for reverie::syscalls::EpollPwait {
2322 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2323 self,
2324 _guest: &mut G,
2325 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2326 (self.with_timeout(0), None)
2327 }
2328
2329 fn signal_interrupt_errno(&self) -> Errno {
2330 Errno::EINTR
2331 }
2332}
2333
2334impl TimeoutableSyscall for reverie::syscalls::EpollPwait {
2335 fn timeout_return_val(&self) -> Result<i64, Errno> {
2336 Ok(0)
2337 }
2338}
2339
2340pub(crate) async fn take_outcome<T, G>(
2347 guest: &mut G,
2348 signal: i32,
2349 after: Option<(u64, u64)>,
2350) -> crate::scheduler::posix_timer_coalesce::TakeOutcome
2351where
2352 T: RecordOrReplay,
2353 G: Guest<Detcore<T>>,
2354{
2355 use crate::scheduler::discarded_at_send::DiscardedSignalAnswer;
2356 use crate::scheduler::discarded_at_send::DiscardedSignalControl;
2357 use crate::scheduler::posix_timer_coalesce::TakeOutcome;
2358 let at = guest.thread_state().thread_logical_time.as_nanos();
2359 match crate::tool_global::discarded_signal(
2360 guest,
2361 DiscardedSignalControl::Take { signal, at, after },
2362 )
2363 .await
2364 {
2365 DiscardedSignalAnswer::Taken(outcome) => outcome,
2366 _ => TakeOutcome {
2367 skip: false,
2368 rewrite: None,
2369 },
2370 }
2371}
2372
2373async fn zero_timespec<'stack, T: RecordOrReplay, G: Guest<Detcore<T>>>(
2374 guest: &mut G,
2375) -> (Addr<'stack, Timespec>, <G::Stack as Stack>::StackGuard) {
2376 let mut stack = guest.stack().await;
2377 let tp_val = Timespec {
2378 tv_sec: 0,
2379 tv_nsec: 0,
2380 };
2381 let tp = stack.push(tp_val);
2382 let guard = stack.commit().expect("stack.commit to succeed");
2383 (tp, guard)
2384}
2385
2386#[async_trait]
2387impl NonblockableSyscall for reverie::syscalls::Wait4 {
2388 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2389 self,
2390 _guest: &mut G,
2391 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2392 let call2 = self.with_options(self.options() | WaitPidFlag::WNOHANG);
2393 (call2, None)
2394 }
2395
2396 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2403 res == Ok(0)
2404 }
2405}
2406
2407#[async_trait]
2408impl NonblockableSyscall for reverie::syscalls::Futex {
2410 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2411 self,
2412 guest: &mut G,
2413 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2414 let (tp, guard) = zero_timespec(guest).await;
2415 (self.with_timeout(Some(tp)), Some(guard))
2416 }
2417
2418 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2419 res == Err(Errno::ETIMEDOUT)
2422 }
2423
2424 fn kernel_restart_errno(&self) -> Errno {
2443 if matches!(
2444 self.futex_op() & libc::FUTEX_CMD_MASK,
2445 libc::FUTEX_LOCK_PI | libc::FUTEX_LOCK_PI2
2446 ) {
2447 Errno::ERESTARTNOINTR
2448 } else if self.restart_keeps_deadline() {
2449 Errno::ERESTART_RESTARTBLOCK
2450 } else if self.timeout().is_some() {
2451 Errno::ERESTARTNOHAND
2452 } else {
2453 Errno::ERESTARTSYS
2454 }
2455 }
2456
2457 fn restart_keeps_deadline(&self) -> bool {
2460 matches!(
2461 self.futex_op() & libc::FUTEX_CMD_MASK,
2462 libc::FUTEX_WAIT | libc::FUTEX_WAIT_BITSET
2463 ) && self.timeout().is_some()
2464 }
2465}
2466
2467impl TimeoutableSyscall for reverie::syscalls::Futex {
2468 fn timeout_return_val(&self) -> Result<i64, Errno> {
2469 Err(Errno::ETIMEDOUT)
2470 }
2471}
2472
2473#[async_trait]
2474impl NonblockableSyscall for reverie::syscalls::RtSigtimedwait {
2475 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2476 self,
2477 guest: &mut G,
2478 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2479 let (tp, guard) = zero_timespec(guest).await;
2482 (self.with_timeout(Some(tp)), Some(guard))
2483 }
2484
2485 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2486 res == Err(Errno::EAGAIN)
2487 }
2488
2489 fn signal_interrupt_errno(&self) -> Errno {
2490 Errno::EINTR
2491 }
2492
2493 fn signals_consumed_by_wait<M: MemoryAccess>(&self, memory: &M) -> KernelSigset {
2499 self.set()
2500 .and_then(|set| memory.read_value(set.cast::<KernelSigset>()).ok())
2501 .unwrap_or(0)
2502 }
2503}
2504
2505impl TimeoutableSyscall for reverie::syscalls::RtSigtimedwait {
2506 fn timeout_return_val(&self) -> Result<i64, Errno> {
2507 Err(Errno::EAGAIN)
2508 }
2509}
2510
2511#[async_trait]
2514impl NonblockableSyscall for reverie::syscalls::Read {
2515 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2516 self,
2517 guest: &mut G,
2518 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2519 network_comm_syscall(self, guest)
2520 }
2521
2522 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2523 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2526 }
2527
2528 fn ends_at_read_shutdown(&self) -> bool {
2529 true
2530 }
2531}
2532
2533#[async_trait]
2536impl NonblockableSyscall for reverie::syscalls::Write {
2537 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2538 self,
2539 guest: &mut G,
2540 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2541 network_comm_syscall(self, guest)
2542 }
2543
2544 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2545 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2548 }
2549}
2550
2551#[async_trait]
2555impl NonblockableSyscall for reverie::syscalls::Readv {
2556 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2557 self,
2558 guest: &mut G,
2559 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2560 network_comm_syscall(self, guest)
2561 }
2562
2563 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2564 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2565 }
2566
2567 fn ends_at_read_shutdown(&self) -> bool {
2568 true
2569 }
2570}
2571
2572#[async_trait]
2576impl NonblockableSyscall for reverie::syscalls::Preadv2 {
2577 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2578 self,
2579 guest: &mut G,
2580 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2581 network_comm_syscall(self, guest)
2582 }
2583
2584 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2585 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2586 }
2587
2588 fn flags_forbid_waiting(&self) -> bool {
2589 self.flags() & libc::RWF_NOWAIT != 0
2590 }
2591}
2592
2593#[async_trait]
2597impl NonblockableSyscall for reverie::syscalls::Writev {
2598 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2599 self,
2600 guest: &mut G,
2601 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2602 network_comm_syscall(self, guest)
2603 }
2604
2605 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2606 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2607 }
2608}
2609
2610fn has_msg_dontwait(flags: u32) -> bool {
2613 flags & libc::MSG_DONTWAIT as u32 != 0
2614}
2615
2616fn network_comm_syscall<T: RecordOrReplay, G: Guest<Detcore<T>>, C: SyscallInfo + Into<Syscall>>(
2620 call: C,
2621 guest: &mut G,
2622) -> (C, Option<<G::Stack as Stack>::StackGuard>) {
2623 let fd = get_fd(call.into()).unwrap_or_else(|| {
2625 panic!(
2626 "network_comm_syscall called on invalid syscall / unknown fd: {}",
2627 call.name()
2628 );
2629 });
2630 guest
2631 .thread_state()
2632 .with_detfd(fd, |detfd| {
2633 assert!(
2634 detfd.physically_nonblocking(),
2635 "expecting sockets/pipes to be physically nonblocking"
2636 );
2637 })
2638 .unwrap();
2639 (call, None)
2640}
2641
2642#[async_trait]
2643impl NonblockableSyscall for reverie::syscalls::Accept4 {
2644 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2645 self,
2646 guest: &mut G,
2647 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2648 network_comm_syscall(self, guest)
2649 }
2650
2651 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2652 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2653 }
2654}
2655
2656impl TimeoutableSyscall for reverie::syscalls::Accept4 {
2657 fn timeout_return_val(&self) -> Result<i64, Errno> {
2658 Ok(0)
2659 }
2660}
2661
2662#[async_trait]
2663impl NonblockableSyscall for reverie::syscalls::Recvfrom {
2664 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2665 self,
2666 guest: &mut G,
2667 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2668 network_comm_syscall(self, guest)
2669 }
2670
2671 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2672 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2673 }
2674
2675 fn ends_at_read_shutdown(&self) -> bool {
2676 true
2677 }
2678
2679 fn flags_forbid_waiting(&self) -> bool {
2680 has_msg_dontwait(self.flags() as u32)
2681 }
2682
2683 fn reads_error_queue(&self) -> bool {
2684 self.flags() & libc::MSG_ERRQUEUE != 0
2685 }
2686}
2687
2688#[async_trait]
2689impl NonblockableSyscall for reverie::syscalls::Recvmsg {
2690 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2691 self,
2692 guest: &mut G,
2693 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2694 network_comm_syscall(self, guest)
2695 }
2696
2697 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2698 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2699 }
2700
2701 fn ends_at_read_shutdown(&self) -> bool {
2702 true
2703 }
2704
2705 fn flags_forbid_waiting(&self) -> bool {
2706 has_msg_dontwait(self.flags() as u32)
2707 }
2708
2709 fn reads_error_queue(&self) -> bool {
2710 self.flags() & libc::MSG_ERRQUEUE != 0
2711 }
2712}
2713
2714#[async_trait]
2715impl NonblockableSyscall for reverie::syscalls::Recvmmsg {
2716 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2719 self,
2720 guest: &mut G,
2721 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2722 network_comm_syscall(self, guest)
2723 }
2724
2725 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2726 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2727 }
2728
2729 fn ends_at_read_shutdown(&self) -> bool {
2730 true
2731 }
2732
2733 fn flags_forbid_waiting(&self) -> bool {
2737 has_msg_dontwait(self.flags())
2738 }
2739
2740 fn reads_error_queue(&self) -> bool {
2741 self.flags() & libc::MSG_ERRQUEUE as u32 != 0
2742 }
2743}
2744
2745#[async_trait]
2746impl NonblockableSyscall for reverie::syscalls::Sendto {
2747 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2748 self,
2749 guest: &mut G,
2750 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2751 network_comm_syscall(self, guest)
2752 }
2753
2754 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2755 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2756 }
2757
2758 fn flags_forbid_waiting(&self) -> bool {
2759 has_msg_dontwait(self.flags())
2760 }
2761}
2762
2763#[async_trait]
2764impl NonblockableSyscall for reverie::syscalls::Sendmmsg {
2765 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2766 self,
2767 guest: &mut G,
2768 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2769 network_comm_syscall(self, guest)
2770 }
2771
2772 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2773 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2774 }
2775
2776 fn flags_forbid_waiting(&self) -> bool {
2777 has_msg_dontwait(self.flags() as u32)
2778 }
2779}
2780
2781#[async_trait]
2782impl NonblockableSyscall for reverie::syscalls::Sendmsg {
2783 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2784 self,
2785 guest: &mut G,
2786 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2787 network_comm_syscall(self, guest)
2788 }
2789
2790 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2791 res == Err(Errno::EAGAIN) || res == Err(Errno::EWOULDBLOCK)
2792 }
2793
2794 fn flags_forbid_waiting(&self) -> bool {
2795 has_msg_dontwait(self.flags() as u32)
2796 }
2797}
2798
2799#[async_trait]
2800impl NonblockableSyscall for reverie::syscalls::Connect {
2801 async fn into_nonblocking<T: RecordOrReplay, G: Guest<Detcore<T>>>(
2802 self,
2803 guest: &mut G,
2804 ) -> (Self, Option<<G::Stack as Stack>::StackGuard>) {
2805 network_comm_syscall(self, guest)
2806 }
2807
2808 fn syscall_would_have_blocked(&self, res: Result<i64, Errno>) -> bool {
2809 res == Err(Errno::EAGAIN)
2810 || res == Err(Errno::EWOULDBLOCK)
2811 || res == Err(Errno::EINPROGRESS)
2812 || res == Err(Errno::EALREADY)
2813 }
2814
2815 fn normalize_nonblocking_result(
2816 &self,
2817 res: Result<i64, Errno>,
2818 retried: bool,
2819 ) -> Result<i64, Errno> {
2820 match (retried, res) {
2821 (true, Err(Errno::EISCONN)) => Ok(0),
2822 (_, res) => res,
2823 }
2824 }
2825}
2826
2827pub async fn retry_nonblocking_syscall<T, G, C>(
2839 guest: &mut G,
2840 call: C,
2841 rsrc: Resources,
2842 subtool: Option<&Detcore<T>>,
2843) -> Result<i64, Error>
2844where
2845 C: NonblockableSyscall + Into<Syscall>,
2846 T: RecordOrReplay,
2847 G: Guest<Detcore<T>>,
2848{
2849 retry_nonblocking_syscall_helper(guest, call, rsrc, None, subtool).await
2851}
2852
2853#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2882pub(crate) struct RestartBlock {
2883 pub(crate) rip: u64,
2888 pub(crate) deadline: Option<LogicalTime>,
2891 pub(crate) call: RestartCall,
2893}
2894
2895#[derive(Clone, Copy)]
2903pub(crate) struct PollProbe {
2904 #[cfg(not(target_arch = "x86_64"))]
2905 timeout: AddrMut<'static, Timespec>,
2906}
2907
2908impl PollProbe {
2909 pub(crate) fn reserve<S: Stack>(stack: &mut S) -> Self {
2911 #[cfg(target_arch = "x86_64")]
2912 return {
2913 let _ = stack;
2914 Self {}
2915 };
2916 #[cfg(not(target_arch = "x86_64"))]
2917 return Self {
2918 timeout: stack.reserve(),
2919 };
2920 }
2921
2922 #[cfg(target_arch = "x86_64")]
2925 pub(crate) fn call<M: MemoryAccess>(
2926 self,
2927 _memory: M,
2928 fds: AddrMut<'_, libc::pollfd>,
2929 nfds: libc::nfds_t,
2930 timeout_millis: libc::c_int,
2931 ) -> Result<syscalls::Poll, Errno> {
2932 Ok(syscalls::Poll::new()
2933 .with_fds(Some(fds.cast()))
2934 .with_nfds(nfds)
2935 .with_timeout(timeout_millis))
2936 }
2937
2938 #[cfg(not(target_arch = "x86_64"))]
2942 pub(crate) fn call<M: MemoryAccess>(
2943 self,
2944 mut memory: M,
2945 fds: AddrMut<'_, libc::pollfd>,
2946 nfds: libc::nfds_t,
2947 timeout_millis: libc::c_int,
2948 ) -> Result<syscalls::Ppoll, Errno> {
2949 let timeout = if timeout_millis < 0 {
2950 None
2951 } else {
2952 let timeout = Timespec {
2953 tv_sec: libc::time_t::from(timeout_millis / 1000),
2954 tv_nsec: libc::c_long::from(timeout_millis % 1000) * 1_000_000,
2955 };
2956 memory.write_value(self.timeout, &timeout)?;
2957 Some(self.timeout)
2958 };
2959 Ok(syscalls::Ppoll::new()
2960 .with_fds(Some(fds.cast()))
2961 .with_nfds(nfds)
2962 .with_timeout(timeout)
2963 .with_sigmask(None)
2964 .with_sigsetsize(KERNEL_SIGSET_SIZE))
2965 }
2966}
2967
2968#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2970pub(crate) enum RestartCall {
2971 #[cfg(target_arch = "x86_64")]
2974 Poll(syscalls::Poll),
2975 #[cfg(target_arch = "x86_64")]
2981 KernelPoll(syscalls::Poll, KernelPollDeadline),
2982 Futex(syscalls::Futex),
2983 Nanosleep(reverie::syscalls::family::NanosleepFamily),
2986 InKernel(Sysno),
2992}
2993
2994#[cfg(target_arch = "x86_64")]
3006#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3007pub(crate) struct KernelPollDeadline(pub(crate) Option<std::time::Instant>);
3008
3009fn resumed_kernel_wait_result(result: Result<i64, Errno>) -> Result<i64, Errno> {
3011 match result {
3012 Err(Errno::ERESTARTSYS) => Err(Errno::ERESTART_RESTARTBLOCK),
3013 other => other,
3014 }
3015}
3016
3017impl RestartBlock {
3018 pub(crate) fn resumed_at(self, rip: u64) -> Option<Self> {
3022 (self.rip == rip).then_some(self)
3023 }
3024}
3025
3026pub(crate) async fn inject_keeping_kernel_restart<T, G, S>(
3034 guest: &mut G,
3035 call: S,
3036) -> Result<i64, Error>
3037where
3038 T: RecordOrReplay,
3039 G: Guest<Detcore<T>>,
3040 S: SyscallInfo,
3041{
3042 let sysno = call.number();
3043 let result = guest.inject(call).await.map_err(Error::from);
3044 keep_restart_block(guest, &result, RestartCall::InKernel(sysno), None).await;
3045 result
3046}
3047
3048pub(crate) async fn resume_kernel_wait<T, G>(
3057 guest: &mut G,
3058 restart: Syscall,
3059 sysno: Sysno,
3060) -> Result<i64, Error>
3061where
3062 T: RecordOrReplay,
3063 G: Guest<Detcore<T>>,
3064{
3065 let result = resumed_kernel_wait_result(guest.inject(restart).await).map_err(Error::from);
3066 keep_restart_block(guest, &result, RestartCall::InKernel(sysno), None).await;
3067 result
3068}
3069
3070pub(crate) async fn keep_restart_block<T, G>(
3073 guest: &mut G,
3074 result: &Result<i64, Error>,
3075 call: RestartCall,
3076 deadline: Option<LogicalTime>,
3077) where
3078 T: RecordOrReplay,
3079 G: Guest<Detcore<T>>,
3080{
3081 if matches!(result, Err(Error::Errno(errno)) if *errno == Errno::ERESTART_RESTARTBLOCK) {
3082 let rip = crate::arch::instruction_pointer(&guest.regs().await);
3083 let block = RestartBlock {
3084 rip,
3085 deadline,
3086 call,
3087 };
3088 tracing::trace!(
3089 "[tid {}] the wait keeps {:?} for its restart",
3090 guest.tid(),
3091 block
3092 );
3093 guest.thread_state_mut().restart_block = Some(block);
3094 }
3095}
3096
3097fn held_signal_restart_errno(restart_errno: Errno, signal: i32) -> Errno {
3112 if restart_errno == Errno::ERESTART_RESTARTBLOCK && signal != libc::SIGSTOP {
3113 Errno::ERESTARTNOHAND
3114 } else {
3115 restart_errno
3116 }
3117}
3118
3119pub(crate) fn poll_deadline_for<T, G>(
3124 guest: &G,
3125 deadline: Option<LogicalTime>,
3126) -> Option<LogicalTime>
3127where
3128 T: RecordOrReplay,
3129 G: Guest<Detcore<T>>,
3130{
3131 if guest.config().recordreplay_modes {
3132 None
3133 } else {
3134 deadline
3135 }
3136}
3137
3138pub async fn retry_nonblocking_syscall_with_timeout<T, G, C>(
3142 guest: &mut G,
3143 call: C,
3144 rsrc: Resources,
3145 maybe_timeout: Option<LogicalTime>,
3147) -> Result<i64, Error>
3148where
3149 C: NonblockableSyscall + TimeoutableSyscall + Into<Syscall>,
3150 T: RecordOrReplay,
3151 G: Guest<Detcore<T>>,
3152{
3153 let maybe_tup = maybe_timeout.map(|t| (t, call.timeout_return_val()));
3154 if guest
3158 .config()
3159 .backend
3160 .supports_blocked_wait_signal_interruption
3161 && !guest.config().in_guest_signal_delivery
3162 {
3163 return retry_blocking_wait_with_kernel_signal_state(guest, call, rsrc, maybe_tup).await;
3164 }
3165 retry_nonblocking_syscall_helper(guest, call, rsrc, maybe_tup, None).await
3169}
3170
3171pub(crate) fn guest_signal_state_task<T, G>(guest: &G) -> Option<(i32, i32)>
3176where
3177 T: RecordOrReplay,
3178 G: Guest<Detcore<T>>,
3179{
3180 crate::procfs_inode::guest_tids_name_host_tasks(&guest.config().backend, None)
3181 .then(|| (guest.pid().as_raw(), guest.tid().as_raw()))
3182}
3183
3184async fn own_disposition_interrupts<T, G>(
3190 guest: &mut G,
3191 status: ResumeStatus,
3192 mask_addr: AddrMut<'_, KernelSigset>,
3193 action_addr: AddrMut<'_, KernelSigaction>,
3194) -> Result<bool, Error>
3195where
3196 T: RecordOrReplay,
3197 G: Guest<Detcore<T>>,
3198{
3199 let query = syscalls::RtSigprocmask::new()
3200 .with_how(libc::SIG_BLOCK)
3201 .with_set(None)
3202 .with_oldset(Some(mask_addr.cast()))
3203 .with_sigsetsize(KERNEL_SIGSET_SIZE);
3204 guest.inject_with_retry(query).await?;
3205 let mask: KernelSigset = guest.memory().read_value(mask_addr)?;
3206 Ok(
3207 wait_signal_disposition(guest, status, &mask, action_addr, true)
3208 .await?
3209 .is_some(),
3210 )
3211}
3212
3213async fn progress_signal_interrupts<T, G>(
3221 guest: &mut G,
3222 status: ResumeStatus,
3223) -> Result<bool, Error>
3224where
3225 T: RecordOrReplay,
3226 G: Guest<Detcore<T>>,
3227{
3228 let mut stack = guest.stack().await;
3229 let mask_addr = stack.reserve::<KernelSigset>();
3230 let action_addr = stack.reserve::<KernelSigaction>();
3231 let _guard = stack.commit()?;
3232 own_disposition_interrupts(guest, status, mask_addr, action_addr).await
3233}
3234
3235fn inotify_description_of<T, G, C>(guest: &G, call: C) -> Option<crate::fd::DetFd>
3243where
3244 T: RecordOrReplay,
3245 G: Guest<Detcore<T>>,
3246 C: NonblockableSyscall + Into<Syscall>,
3247{
3248 let fd = get_fd(call.into())?;
3249 guest
3250 .thread_state()
3251 .with_detfd(fd, |detfd| {
3252 (detfd.ty() == FdType::Inotify).then(|| detfd.clone())
3253 })
3254 .ok()
3255 .flatten()
3256}
3257
3258fn interrupted_wait_errno<C>(began_on: Option<&crate::fd::DetFd>, call: C) -> Errno
3273where
3274 C: NonblockableSyscall,
3275{
3276 if began_on.is_some_and(crate::fd::DetFd::is_nonblocking) {
3277 Errno::EAGAIN
3278 } else {
3279 call.signal_interrupt_errno()
3280 }
3281}
3282
3283#[derive(Clone, Debug, Default)]
3286pub(crate) struct CallParts {
3287 pub(crate) progress: bool,
3291 pub(crate) carried: Option<CarriedSignal>,
3297}
3298
3299#[derive(Clone, Debug)]
3302pub(crate) enum CarriedSignal {
3303 Decided,
3306 Wake(ResumeStatus),
3312}
3313
3314fn carried_signal(
3320 turn: Option<TurnSignals>,
3321 resumed: &ResumeStatus,
3322 gated: bool,
3323) -> Option<CarriedSignal> {
3324 match turn {
3325 Some(turn) => turn.ends(true).then_some(CarriedSignal::Decided),
3326 None if gated => None,
3327 None => matches!(resumed, ResumeStatus::Signaled(_))
3328 .then(|| CarriedSignal::Wake(resumed.clone())),
3329 }
3330}
3331
3332async fn carried_signal_ends<T, G>(
3339 guest: &mut G,
3340 decides_itself: bool,
3341 gated: bool,
3342) -> Result<bool, Error>
3343where
3344 T: RecordOrReplay,
3345 G: Guest<Detcore<T>>,
3346{
3347 let carried = guest
3348 .thread_state_mut()
3349 .call_parts
3350 .as_mut()
3351 .and_then(|parts| parts.carried.take());
3352 match carried {
3353 None => Ok(false),
3354 Some(CarriedSignal::Decided) => Ok(true),
3355 Some(CarriedSignal::Wake(status)) if decides_itself => {
3356 progress_signal_interrupts(guest, status).await
3357 }
3358 Some(CarriedSignal::Wake(_)) => Ok(!gated),
3359 }
3360}
3361
3362async fn retry_nonblocking_syscall_helper<T, G, C>(
3364 guest: &mut G,
3365 call0: C,
3366 mut rsrc: Resources,
3367 maybe_timeout: Option<(LogicalTime, Result<i64, Errno>)>,
3368 subtool: Option<&Detcore<T>>,
3369) -> Result<i64, Error>
3370where
3371 C: NonblockableSyscall + Into<Syscall>,
3372 T: RecordOrReplay,
3373 G: Guest<Detcore<T>>,
3374{
3375 refresh_guest_poll_state(guest, call0.into(), &mut rsrc);
3379 let mut guest_held = GuestHeldPollProof::capture(guest, call0.into(), &mut rsrc);
3380 let consumed = call0.signals_consumed_by_wait(&guest.memory());
3382 let entry_import = EntryImport::capture(&guest.memory(), &call0.into()).map(Box::new);
3389 let mut completed_observation = None;
3390 let (mut call, mut maybe_stackguard) = call0.into_nonblocking(guest).await;
3393 rsrc.poll_deadline = poll_deadline_for(guest, maybe_timeout.map(|(deadline, _)| deadline));
3394 if rsrc.signal_interrupt_errno().is_none()
3404 && rsrc.resources.len() == 1
3405 && rsrc.resources.contains_key(&ResourceID::InternalIOPolling)
3406 && call.signal_interrupt_errno() == Errno::ERESTARTSYS
3407 {
3408 rsrc.set_signal_interrupt_errno(Errno::ERESTARTSYS);
3409 rsrc.signal_state_task = guest_signal_state_task(guest);
3414 }
3415 let gated = guest.config().in_guest_signal_delivery;
3420 let checks_at_turn = !gated
3421 && rsrc.signal_interrupt_errno() == Some(Errno::ERESTARTSYS.into_raw())
3422 && decides_interruption_at_turn(guest, rsrc.signal_state_task);
3423 let progress = guest
3435 .thread_state()
3436 .call_parts
3437 .as_ref()
3438 .is_some_and(|parts| parts.progress);
3439 let decides_itself = !checks_at_turn && !gated;
3440 let decides_progress_itself = progress && decides_itself;
3441 let began_on = inotify_description_of(guest, call0);
3444 let mut turn_wait = checks_at_turn
3454 .then(|| KernelSignalWait::new(guest, 0, false, call.signal_interrupt_errno()));
3455 let mut published = PublishedWaitMask::default();
3456
3457 let result: Result<i64, Error> = loop {
3458 refresh_guest_poll_state(guest, call.into(), &mut rsrc);
3463 guest_held.revalidate(guest, &mut rsrc);
3466 let resumed = match call.into() {
3467 Syscall::Read(read) if maybe_timeout.is_none() && maybe_stackguard.is_none() => {
3468 crate::tool_global::polled_read_request(guest, read, rsrc.clone()).await
3469 }
3470 _ => resource_request(guest, rsrc.clone()).await,
3471 };
3472 guest_held.revalidate(guest, &mut rsrc);
3473 let turn = if checks_at_turn && !matches!(resumed, ResumeStatus::Signaled(None)) {
3486 match turn_signals_with_block(
3487 guest,
3488 &mut turn_wait,
3489 ProbeMaskPolicy::PrivateProbe,
3490 committed_signals(&resumed),
3491 rsrc.private_sigchld_decides,
3492 &mut published,
3493 )
3494 .await
3495 {
3496 Ok(turn) => Some(turn),
3497 Err(error) => break Err(error),
3498 }
3499 } else {
3500 None
3501 };
3502 let reconvert =
3509 maybe_stackguard.is_some() && crate::syscalls::timerfd::may_publish(guest, call.into());
3510 if reconvert {
3511 maybe_stackguard = None;
3512 }
3513 rsrc.poll_deadline = crate::syscalls::timerfd::publish_before_probe(
3514 guest,
3515 call.into(),
3516 maybe_timeout.map(|(deadline, _)| deadline),
3517 )
3518 .await;
3519 if reconvert {
3520 (call, maybe_stackguard) = call0.into_nonblocking(guest).await;
3521 }
3522 let mode_wake_decides_itself = decides_itself
3539 && began_on
3540 .as_ref()
3541 .is_some_and(crate::fd::DetFd::is_nonblocking);
3542 let signaled = match turn {
3543 Some(turn) => turn.ends(progress),
3544 None if gated => false,
3545 None if (decides_progress_itself || mode_wake_decides_itself)
3549 && maybe_stackguard.is_none()
3550 && matches!(resumed, ResumeStatus::Signaled(_)) =>
3551 {
3552 match progress_signal_interrupts(guest, resumed.clone()).await {
3553 Ok(interrupts) => interrupts,
3554 Err(error) => break Err(error),
3555 }
3556 }
3557 None => matches!(resumed, ResumeStatus::Signaled(_)),
3558 };
3559 let pending = pending_real_time_among(guest, consumed).await;
3562 act_on_pending_real_time(guest, pending, consumed, 0).await;
3563 let mut displaced = entry_import
3567 .as_ref()
3568 .map(|entry| entry.reinstate(&mut guest.memory()));
3569 if Detcore::<T>::passes_descriptors_out_now(guest, call.into()) {
3573 if let Err(error) = put_back_entry_import(guest, &entry_import, displaced.take(), false)
3577 {
3578 break Err(error);
3579 }
3580 let dettid = guest.thread_state().dettid;
3581 let _ = resource_request(guest, Resources::new(dettid)).await;
3582 displaced = entry_import
3583 .as_ref()
3584 .map(|entry| entry.reinstate(&mut guest.memory()));
3585 }
3586 if guest.thread_state().receive_geometry.is_some() {
3590 let geometry =
3591 crate::syscalls::scm_rights::ReceiveGeometry::before(&guest.memory(), &call.into());
3592 guest.thread_state_mut().receive_geometry = Some(geometry);
3593 }
3594 if let Some(inspection) = guest.thread_state().send_inspection.clone()
3602 && let Some(reason) =
3603 Box::pin(Detcore::<T>::changed_placeholder_send(guest, &inspection)).await
3604 {
3605 tracing::error!("Detcore refuses {}: {reason}", inspection.sysno);
3606 if let Err(error) = put_back_entry_import(guest, &entry_import, displaced, false) {
3607 break Err(error);
3608 }
3609 let panic_on_unsupported_syscalls = guest.config().panic_on_unsupported_syscalls;
3610 break refuse_unserviceable_operation_with(
3611 guest,
3612 panic_on_unsupported_syscalls,
3613 inspection.sysno,
3614 Errno::EOPNOTSUPP,
3615 Some(&reason),
3616 )
3617 .await;
3618 }
3619 if guest.thread_state().inotify_queue.is_some() {
3629 Box::pin(crate::syscalls::inotify::count_before_attempt(
3630 guest,
3631 call.into(),
3632 ))
3633 .await;
3634 }
3635 let attempt_observation =
3638 if guest.config().detlog_io_buffers && crate::io_buffers::observed() {
3639 EntryImport::capture_observation(&guest.memory(), &call.into())
3640 } else {
3641 None
3642 };
3643 let res = match subtool {
3647 Some(detcore) => {
3648 detcore
3649 .record_or_replay_preserving_tool_errors(guest, call)
3650 .await
3651 }
3652 None => {
3653 match crate::reserved_input::before_native_call(guest, call.into()) {
3657 Ok(()) => guest.inject_with_retry(call).await.map_err(Error::from),
3658 Err(error) => Err(error),
3661 }
3662 }
3663 };
3664 if let Err(error) = put_back_entry_import(guest, &entry_import, displaced, res.is_ok()) {
3665 break Err(error);
3666 }
3667 let syscall_result = match res {
3675 Ok(value) => Ok(value),
3676 Err(Error::Errno(error)) => Err(error),
3677 Err(error) => break Err(error),
3678 };
3679 if subtool.is_none()
3685 && call.syscall_would_have_blocked(syscall_result)
3686 && let Some(result) = finish_after_read_shutdown(guest, call).await
3687 {
3688 break result;
3689 }
3690 let carried_ends =
3695 if progress && !signaled && call.syscall_would_have_blocked(syscall_result) {
3696 match carried_signal_ends(
3697 guest,
3698 decides_progress_itself && maybe_stackguard.is_none(),
3699 gated,
3700 )
3701 .await
3702 {
3703 Ok(ends) => ends,
3704 Err(error) => break Err(error),
3705 }
3706 } else {
3707 false
3708 };
3709 let signaled = signaled || carried_ends;
3710 if signaled && call.syscall_would_have_blocked(syscall_result) {
3711 let errno = interrupted_wait_errno(began_on.as_ref(), call);
3712 tracing::trace!(
3713 "retry_nonblocking_syscall: interrupted by signal while {} would block: {:?}",
3714 call.display(&guest.memory()),
3715 errno
3716 );
3717 break Err(errno.into());
3718 }
3719 if gated && call.syscall_would_have_blocked(syscall_result) {
3723 match gated_wait_ends(guest, None) {
3724 Ok(true) => break Err(interrupted_wait_errno(began_on.as_ref(), call).into()),
3725 Ok(false) => {}
3726 Err(error) => break Err(error),
3727 }
3728 }
3729 if call.syscall_would_have_blocked(syscall_result) {
3730 if let Some(wait) = turn_wait.as_mut() {
3731 if let Err(error) = wait.release_probe_mask_in_place(guest).await {
3739 break Err(error);
3740 }
3741 publish_wait_mask(guest, None, &mut published).await;
3742 }
3743 rsrc.poll_attempt += 1;
3744 if let Some((timeout, timeout_result)) = maybe_timeout {
3745 let new_time = thread_observe_time(guest).await;
3746 if new_time >= timeout {
3747 tracing::trace!(
3748 "Timing out syscall after #{} retries: {}",
3749 rsrc.poll_attempt - 1,
3750 call.display(&guest.memory())
3751 );
3752 break timeout_result.map_err(|e| e.into());
3753 } else {
3754 tracing::trace!(
3755 "Retry #{} for syscall due to result {:?}, {} from timeout: {}",
3756 rsrc.poll_attempt,
3757 syscall_result,
3758 timeout - new_time,
3759 call.display(&guest.memory())
3760 );
3761 record_retry_event(guest, call).await;
3762 }
3763 } else {
3764 tracing::trace!(
3765 "Retry #{} for syscall due to result {:?}: {}",
3766 rsrc.poll_attempt,
3767 syscall_result,
3768 call.display(&guest.memory())
3769 );
3770 record_retry_event(guest, call).await;
3771 }
3772 } else {
3773 if let Some(carried) = carried_signal(turn, &resumed, gated)
3777 && let Some(parts) = guest.thread_state_mut().call_parts.as_mut()
3778 {
3779 parts.carried = Some(carried);
3780 }
3781 let normalized =
3782 call.normalize_nonblocking_result(syscall_result, rsrc.poll_attempt > 0);
3783 if normalized != syscall_result {
3784 guest.thread_state_mut().nonblocking_result_normalized = true;
3785 }
3786 let res = normalized.map_err(|e| e.into());
3787 if res.is_ok() {
3788 completed_observation = attempt_observation;
3789 }
3790 tracing::trace!(
3791 "retry_nonblocking_syscall: syscall completed after {} retries: {} = {:?}",
3792 rsrc.poll_attempt,
3793 call.display(&guest.memory()),
3794 res
3795 );
3796 break res;
3797 }
3798 };
3799 let entry_import =
3800 EntryImport::with_observation(entry_import, call0.into(), completed_observation);
3801 keep_entry_view(guest, entry_import);
3802
3803 match turn_wait {
3809 Some(mut wait) if wait.effective_mask().is_some() => {
3810 let restored = wait.restore(guest, None).await;
3811 publish_wait_mask(guest, None, &mut published).await;
3812 match result_after_restore(restored, result) {
3813 Ok(result) => refuse_held_signal_loss(guest, call0.number(), result).await,
3814 Err(error) => Err(error),
3815 }
3816 }
3817 _ => result,
3818 }
3819}
3820
3821async fn finish_after_read_shutdown<T, G, C>(guest: &mut G, call: C) -> Option<Result<i64, Error>>
3841where
3842 C: NonblockableSyscall + Into<Syscall>,
3843 T: RecordOrReplay,
3844 G: Guest<Detcore<T>>,
3845{
3846 let fd = get_fd(call.into()).filter(|_| call.ends_at_read_shutdown())?;
3847 let shut_down_by_guest = guest.thread_state().with_detfd(fd, |detfd| {
3848 matches!(detfd.ty(), FdType::Socket) && detfd.read_shutdown_requested()
3849 });
3850 if shut_down_by_guest != Ok(true) || !read_side_shut_down(guest, fd).await {
3851 return None;
3852 }
3853 let fcntl = |cmd| syscalls::Fcntl::new().with_fd(fd).with_cmd(cmd);
3854 let flags = match guest.inject_with_retry(fcntl(FcntlCmd::F_GETFL)).await {
3856 Ok(flags) => flags as libc::c_int,
3857 Err(errno) => {
3858 tracing::warn!("socket {fd}: F_GETFL before a read-shutdown receive failed: {errno}");
3859 return None;
3860 }
3861 };
3862 let blocking = fcntl(FcntlCmd::F_SETFL(flags & !libc::O_NONBLOCK));
3863 if let Err(errno) = guest.inject_with_retry(blocking).await {
3864 tracing::warn!(
3865 "socket {fd}: clearing O_NONBLOCK for a read-shutdown receive failed: {errno}"
3866 );
3867 return None;
3868 }
3869 let result = guest.inject_with_retry(call).await.map_err(Error::from);
3870 if let Err(errno) = guest
3873 .inject_with_retry(fcntl(FcntlCmd::F_SETFL(flags)))
3874 .await
3875 {
3876 panic!("socket {fd}: restoring O_NONBLOCK after a read-shutdown receive failed: {errno}");
3877 }
3878 tracing::trace!(
3879 "retry_nonblocking_syscall: {} ran blocking after a read shutdown: {:?}",
3880 call.display(&guest.memory()),
3881 result
3882 );
3883 Some(result)
3884}
3885
3886async fn read_side_shut_down<T, G>(guest: &mut G, fd: i32) -> bool
3890where
3891 T: RecordOrReplay,
3892 G: Guest<Detcore<T>>,
3893{
3894 let mut stack = guest.stack().await;
3895 let pollfd: AddrMut<libc::pollfd> = stack.reserve();
3896 let Ok(_guard) = stack.commit() else {
3897 return false;
3898 };
3899 let probe = syscalls::Poll::new()
3900 .with_fds(Some(pollfd.cast()))
3901 .with_nfds(1)
3902 .with_timeout(0);
3903 let request = libc::pollfd {
3904 fd,
3905 events: libc::POLLRDHUP,
3906 revents: 0,
3907 };
3908 guest.memory().write_value(pollfd, &request).is_ok()
3909 && guest.inject_with_retry(probe).await == Ok(1)
3910 && guest
3911 .memory()
3912 .read_value(pollfd)
3913 .is_ok_and(|polled| polled.revents & libc::POLLRDHUP != 0)
3914}
3915
3916fn put_back_entry_import<T, G>(
3922 guest: &mut G,
3923 entry_import: &Option<Box<EntryImport>>,
3924 displaced: Option<Displaced>,
3925 completed: bool,
3926) -> Result<(), Error>
3927where
3928 T: RecordOrReplay,
3929 G: Guest<Detcore<T>>,
3930{
3931 let (Some(entry), Some(displaced)) = (entry_import, displaced) else {
3932 return Ok(());
3933 };
3934 if displaced.is_empty() {
3935 return Ok(());
3936 }
3937 entry
3938 .put_back(&mut guest.memory(), displaced, completed)
3939 .map_err(|errno| {
3940 tracing::error!(
3941 "Detcore could not put back the guest's rewrite of a vectored call's memory: {errno}"
3942 );
3943 Error::from(errno)
3944 })
3945}
3946
3947fn keep_entry_view<T, G>(guest: &mut G, entry_import: Option<Box<EntryImport>>)
3952where
3953 T: RecordOrReplay,
3954 G: Guest<Detcore<T>>,
3955{
3956 let view =
3957 entry_import.filter(|entry| entry.has_observation() || entry.differs(&guest.memory()));
3958 guest.thread_state_mut().entry_import = view;
3959}
3960
3961fn committed_signals(resumed: &ResumeStatus) -> KernelSigset {
3990 match resumed {
3991 ResumeStatus::Signaled(Some(signals)) => signals.iter().fold(0, |set, signal| {
3992 set | kernel_sigset_bit(signal.0 as libc::c_int)
3993 }),
3994 _ => 0,
3995 }
3996}
3997
3998#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4005struct TurnSignals {
4006 named: KernelSigset,
4015 pending: KernelSigset,
4021}
4022
4023impl TurnSignals {
4024 fn ends(&self, progress: bool) -> bool {
4031 self.pending != 0 || (progress && self.named != 0)
4032 }
4033}
4034
4035async fn read_turn_signals<T, G>(
4042 guest: &mut G,
4043 mask: Option<KernelSigset>,
4044 committed: KernelSigset,
4045 real_time: NamedRealTime,
4046) -> Result<TurnSignals, Error>
4047where
4048 T: RecordOrReplay,
4049 G: Guest<Detcore<T>>,
4050{
4051 Ok(
4052 read_turn_signals_and_state(guest, mask, committed, real_time)
4053 .await?
4054 .0,
4055 )
4056}
4057
4058async fn read_turn_signals_and_state<T, G>(
4061 guest: &mut G,
4062 mask: Option<KernelSigset>,
4063 committed: KernelSigset,
4064 real_time: NamedRealTime,
4065) -> Result<(TurnSignals, KernelSignalState), Error>
4066where
4067 T: RecordOrReplay,
4068 G: Guest<Detcore<T>>,
4069{
4070 let state = read_wait_signal_state(guest.pid(), guest.tid())?;
4071 let mask = mask.unwrap_or(state.blocked);
4072 let host_timed = host_timed_signals(guest).await;
4073 Ok((
4074 turn_signals_from_state(guest, &state, mask, host_timed, committed, real_time, 0).await,
4075 state,
4076 ))
4077}
4078
4079async fn turn_signals_from_state<T, G>(
4084 guest: &mut G,
4085 state: &KernelSignalState,
4086 mask: KernelSigset,
4087 host_timed: KernelSigset,
4088 committed: KernelSigset,
4089 real_time: NamedRealTime,
4090 private: KernelSigset,
4091) -> TurnSignals
4092where
4093 T: RecordOrReplay,
4094 G: Guest<Detcore<T>>,
4095{
4096 let before =
4097 turn_signals_before_discard(state, mask, host_timed, committed, real_time, private);
4098 let pending =
4099 crate::syscalls::threads::without_discarded(guest, state, before.pending, committed).await;
4100 TurnSignals { pending, ..before }
4101}
4102
4103#[derive(Clone, Copy)]
4107enum ProbeMaskPolicy {
4108 Guest,
4109 PendingSignals,
4110 PrivateProbe,
4111}
4112
4113async fn turn_signals_with_block<T, G>(
4133 guest: &mut G,
4134 wait: &mut Option<KernelSignalWait>,
4135 mask_policy: ProbeMaskPolicy,
4136 committed: KernelSigset,
4137 private_decides: bool,
4138 published: &mut PublishedWaitMask,
4139) -> Result<TurnSignals, Error>
4140where
4141 T: RecordOrReplay,
4142 G: Guest<Detcore<T>>,
4143{
4144 let mask = wait.as_ref().and_then(KernelSignalWait::effective_mask);
4145 let state = read_wait_signal_state(guest.pid(), guest.tid())?;
4146 let (host_timed, recorded) = crate::tool_global::host_timed_and_private_sigchld(guest).await;
4147 let mut private = 0;
4148 if let Some(wait) = wait.as_mut() {
4149 let needs_protection = match mask_policy {
4153 ProbeMaskPolicy::Guest => false,
4154 ProbeMaskPolicy::PendingSignals => {
4155 state.pending & !state.blocked & blocked_signal_mask() != 0
4156 }
4157 ProbeMaskPolicy::PrivateProbe => true,
4158 };
4159 if needs_protection && wait.needs_block() && wait.block_in_place(guest, host_timed).await? {
4160 publish_wait_mask(guest, wait.effective_mask(), published).await;
4161 }
4162 if private_decides
4163 && wait.saved_mask.is_some()
4164 && state.thread_pending & kernel_sigset_bit(libc::SIGCHLD) != 0
4165 {
4166 private = recorded;
4167 }
4168 }
4169 let mask = mask.unwrap_or(state.blocked);
4170 Ok(turn_signals_from_state(
4171 guest,
4172 &state,
4173 mask,
4174 host_timed,
4175 committed,
4176 NamedRealTime::Woken,
4177 private,
4178 )
4179 .await)
4180}
4181
4182fn turn_signals_before_discard(
4190 state: &KernelSignalState,
4191 mask: KernelSigset,
4192 host_timed: KernelSigset,
4193 committed: KernelSigset,
4194 real_time: NamedRealTime,
4195 private: KernelSigset,
4196) -> TurnSignals {
4197 use crate::scheduler::discarded_at_send::STANDARD_SIGNALS;
4198 let held =
4202 crate::syscalls::threads::futex_wait_held_after_wake(host_timed, committed) & !private;
4203 let pending = state.pending_interrupting(mask, false) & !held;
4204 let counted = match real_time {
4205 NamedRealTime::Woken => !0,
4206 NamedRealTime::ByDisposition => {
4207 STANDARD_SIGNALS | (state.interrupting(mask) & !STANDARD_SIGNALS)
4208 }
4209 };
4210 let named = committed & !mask & counted;
4211 TurnSignals { named, pending }
4212}
4213
4214#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4217enum NamedRealTime {
4218 Woken,
4222 ByDisposition,
4225}
4226
4227fn decides_interruption_at_turn<T, G>(guest: &G, task: Option<(i32, i32)>) -> bool
4238where
4239 T: RecordOrReplay,
4240 G: Guest<Detcore<T>>,
4241{
4242 task.is_some() && crate::scheduler::discarded_at_send::records(guest.config())
4243}
4244
4245async fn install_turn_mask<T, G>(
4260 guest: &mut G,
4261 wait: &mut KernelSignalWait,
4262 cell: AddrMut<'_, KernelSigset>,
4263) -> Result<KernelSigset, Error>
4264where
4265 T: RecordOrReplay,
4266 G: Guest<Detcore<T>>,
4267{
4268 let state = read_wait_signal_state(guest.pid(), guest.tid())?;
4275 let kept = crate::syscalls::threads::without_discarded(guest, &state, state.pending, 0).await;
4276 wait.hold_until_return(host_timed_signals(guest).await | (state.pending & !kept));
4277 wait.block(guest, Some(cell)).await?;
4278 let Some(mask) = wait.effective_mask() else {
4279 return Err(Errno::EFAULT.into());
4280 };
4281 crate::tool_global::set_saved_guest_sigmask(guest, Some(mask)).await;
4282 Ok(mask)
4283}
4284
4285pub(crate) fn gated_own_mask<T, G>(guest: &G, fallback: KernelSigset) -> KernelSigset
4294where
4295 T: RecordOrReplay,
4296 G: Guest<Detcore<T>>,
4297{
4298 let own = Resources::new(guest.thread_state().dettid);
4299 crate::tool_global::publish_request_guest_mask(guest, &own).unwrap_or(fallback)
4300}
4301
4302pub(crate) fn gated_wait_end<T, G>(
4311 guest: &G,
4312 fallback: Option<KernelSigset>,
4313) -> Result<Option<crate::wait_signal_end::WaitSignalEnd>, Error>
4314where
4315 T: RecordOrReplay,
4316 G: Guest<Detcore<T>>,
4317{
4318 gated_wait_end_with_virtual(guest, fallback, 0)
4319}
4320
4321pub(crate) fn gated_wait_end_with_virtual<T, G>(
4326 guest: &G,
4327 fallback: Option<KernelSigset>,
4328 virtual_caught: KernelSigset,
4329) -> Result<Option<crate::wait_signal_end::WaitSignalEnd>, Error>
4330where
4331 T: RecordOrReplay,
4332 G: Guest<Detcore<T>>,
4333{
4334 if !guest.config().in_guest_signal_delivery {
4335 return Ok(None);
4336 }
4337 let state = crate::syscalls::threads::read_thread_wait_signal_state(guest.pid(), guest.tid())?;
4338 let mask = gated_own_mask(guest, fallback.unwrap_or(state.blocked));
4339 Ok(crate::wait_signal_end::wait_end_with_virtual(
4340 &state,
4341 mask,
4342 guest.config().in_guest_reserved_signals,
4343 virtual_caught,
4344 ))
4345}
4346
4347pub(crate) fn gated_wait_ends<T, G>(
4351 guest: &mut G,
4352 fallback: Option<KernelSigset>,
4353) -> Result<bool, Error>
4354where
4355 T: RecordOrReplay,
4356 G: Guest<Detcore<T>>,
4357{
4358 let end = gated_wait_end(guest, fallback)?;
4359 if let Some(end) = end {
4360 tracing::info!(
4361 "[dtid {}] under the in-guest delivery gate, {:?} ends the wait",
4362 guest.thread_state().dettid,
4363 end
4364 );
4365 guest.thread_state_mut().wait_signal_end = Some(end);
4366 }
4367 Ok(end.is_some())
4368}
4369
4370pub(crate) fn kernel_run_entry_refusal<T, G>(guest: &mut G) -> Result<Option<Error>, Error>
4391where
4392 T: RecordOrReplay,
4393 G: Guest<Detcore<T>>,
4394{
4395 match gated_wait_end(guest, None)? {
4396 Some(
4397 end @ (crate::wait_signal_end::WaitSignalEnd::Fatal(_)
4398 | crate::wait_signal_end::WaitSignalEnd::BehindCaught(_)),
4399 ) => {
4400 tracing::info!(
4401 "[dtid {}] under the in-guest delivery gate, {:?} is deliverable at a kernel-run \
4402 call's entry: the call is not run",
4403 guest.thread_state().dettid,
4404 end
4405 );
4406 guest.thread_state_mut().wait_signal_end = Some(end);
4407 Ok(Some(Errno::ERESTARTNOINTR.into()))
4408 }
4409 _ => Ok(None),
4410 }
4411}
4412
4413const MAX_IGNORED_REMOVALS: usize = 1 << 16;
4418
4419pub(crate) async fn remove_ignored_signals<T, G>(guest: &mut G, set: u64) -> Result<(), Error>
4433where
4434 T: RecordOrReplay,
4435 G: Guest<Detcore<T>>,
4436{
4437 remove_pending_ignored(guest, set, IgnoredRemoval::NotHeldByLinux).await
4438}
4439
4440pub(crate) async fn remove_signals_discarded_under_a_temporary_mask<T, G>(
4447 guest: &mut G,
4448 set: u64,
4449) -> Result<(), Error>
4450where
4451 T: RecordOrReplay,
4452 G: Guest<Detcore<T>>,
4453{
4454 remove_pending_ignored(guest, set, IgnoredRemoval::LetInByTemporaryMask).await
4455}
4456
4457pub(crate) async fn remove_signals_discarded_under_a_temporary_mask_in<T, G>(
4462 guest: &mut G,
4463 set: u64,
4464 set_cell: usize,
4465 timeout: AddrMut<'_, Timespec>,
4466) -> Result<(), Error>
4467where
4468 T: RecordOrReplay,
4469 G: Guest<Detcore<T>>,
4470{
4471 if set == 0 {
4472 return Ok(());
4473 }
4474 let cell = AddrMut::<u64>::from_raw(set_cell).ok_or_else(|| {
4475 Error::Tool(anyhow::anyhow!(
4476 "removing ignored signals {set:#x}: no cell for the set"
4477 ))
4478 })?;
4479 guest.memory().write_value(cell, &set)?;
4480 guest.memory().write_value(
4481 timeout,
4482 &Timespec {
4483 tv_sec: 0,
4484 tv_nsec: 0,
4485 },
4486 )?;
4487 remove_pending_ignored_in(
4488 guest,
4489 set,
4490 cell.cast::<libc::sigset_t>(),
4491 timeout.into(),
4492 IgnoredRemoval::LetInByTemporaryMask,
4493 )
4494 .await
4495}
4496
4497#[derive(Clone, Copy)]
4500enum IgnoredRemoval {
4501 NotHeldByLinux,
4503 LetInByTemporaryMask,
4505}
4506
4507async fn remove_pending_ignored<T, G>(
4510 guest: &mut G,
4511 set: u64,
4512 why: IgnoredRemoval,
4513) -> Result<(), Error>
4514where
4515 T: RecordOrReplay,
4516 G: Guest<Detcore<T>>,
4517{
4518 if set == 0 {
4519 return Ok(());
4520 }
4521 let mut stack = guest.stack().await;
4522 let set_cell = stack.push(set);
4523 let timeout = stack.push(Timespec {
4524 tv_sec: 0,
4525 tv_nsec: 0,
4526 });
4527 let _stack_guard = stack.commit().map_err(|errno| {
4530 Error::Tool(anyhow::anyhow!(
4531 "removing ignored signals {set:#x}: no scratch stack for the trusted \
4532 rt_sigtimedwait ({errno})"
4533 ))
4534 })?;
4535 let set_cell = unsafe { set_cell.cast::<libc::sigset_t>().into_mut() };
4538 remove_pending_ignored_in(guest, set, set_cell, timeout, why).await
4539}
4540
4541async fn remove_pending_ignored_in<T, G>(
4545 guest: &mut G,
4546 set: u64,
4547 set_cell: AddrMut<'_, libc::sigset_t>,
4548 timeout: Addr<'_, Timespec>,
4549 why: IgnoredRemoval,
4550) -> Result<(), Error>
4551where
4552 T: RecordOrReplay,
4553 G: Guest<Detcore<T>>,
4554{
4555 for _ in 0..MAX_IGNORED_REMOVALS {
4556 let taken = guest
4557 .inject(
4558 syscalls::RtSigtimedwait::new()
4559 .with_set(Some(set_cell))
4560 .with_info(None)
4561 .with_timeout(Some(timeout))
4562 .with_sigsetsize(KERNEL_SIGSET_SIZE),
4563 )
4564 .await;
4565 match (taken, why) {
4566 (Ok(signal), IgnoredRemoval::NotHeldByLinux) => tracing::info!(
4567 "[dtid {}] under the in-guest delivery gate, removed ignored signal {} that Linux \
4568 would not hold (D37)",
4569 guest.thread_state().dettid,
4570 signal
4571 ),
4572 (Ok(signal), IgnoredRemoval::LetInByTemporaryMask) => tracing::info!(
4573 "[dtid {}] removed ignored signal {} that a temporary mask lets in, which Linux \
4574 discards as the call returns",
4575 guest.thread_state().dettid,
4576 signal
4577 ),
4578 (Err(Errno::EAGAIN), _) => return Ok(()),
4579 (Err(errno), _) => {
4582 return Err(Error::Tool(anyhow::anyhow!(
4583 "removing ignored signals {set:#x}: the trusted rt_sigtimedwait failed with \
4584 {errno}"
4585 )));
4586 }
4587 }
4588 }
4589 Err(Error::Tool(anyhow::anyhow!(
4590 "removing ignored signals {set:#x} dequeued {MAX_IGNORED_REMOVALS} instances without \
4591 emptying the queues"
4592 )))
4593}
4594
4595#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4598pub(crate) enum OneQueue {
4599 Private,
4601 Shared,
4603}
4604
4605pub(crate) async fn remove_ignored_from_queue<T, G>(
4626 guest: &mut G,
4627 signal: i32,
4628 queue: OneQueue,
4629) -> Result<(), Error>
4630where
4631 T: RecordOrReplay,
4632 G: Guest<Detcore<T>>,
4633{
4634 let bit = crate::syscalls::kernel_sigset_bit(signal);
4635 let failed = |what: &str| {
4636 Error::Tool(anyhow::anyhow!(
4637 "removing ignored signal {signal} from the {queue:?} queue: {what}"
4638 ))
4639 };
4640 let state = read_thread_wait_signal_state(guest.pid(), guest.tid())?;
4641 let in_queue = match queue {
4642 OneQueue::Private => state.thread_pending,
4643 OneQueue::Shared => state.shared_pending,
4644 };
4645 if in_queue & bit == 0 {
4646 return Ok(());
4647 }
4648 let mut stack = guest.stack().await;
4649 let set_cell = stack.push(bit);
4650 let timeout = stack.push(Timespec {
4651 tv_sec: 0,
4652 tv_nsec: 0,
4653 });
4654 let info = stack.reserve::<[u8; 128]>();
4657 let _stack_guard = stack
4658 .commit()
4659 .map_err(|errno| failed(&format!("no scratch stack ({errno})")))?;
4660 let set_cell = unsafe { set_cell.cast::<libc::sigset_t>().into_mut() };
4662 let dequeue = move |with_info: bool| {
4663 syscalls::RtSigtimedwait::new()
4664 .with_set(Some(set_cell))
4665 .with_info(with_info.then(|| info.cast()))
4666 .with_timeout(Some(timeout))
4667 .with_sigsetsize(KERNEL_SIGSET_SIZE)
4668 };
4669 let mut kept: Vec<[u8; 128]> = Vec::new();
4672 let mut removals = 0;
4673 while read_thread_wait_signal_state(guest.pid(), guest.tid())?.thread_pending & bit != 0 {
4674 removals += 1;
4675 if removals > MAX_IGNORED_REMOVALS {
4676 return Err(failed("too many instances"));
4677 }
4678 match guest.inject(dequeue(queue == OneQueue::Shared)).await {
4679 Ok(_) => {
4680 if queue == OneQueue::Shared {
4681 kept.push(guest.memory().read_value(info)?);
4682 }
4683 }
4684 Err(errno) => return Err(failed(&format!("rt_sigtimedwait failed with {errno}"))),
4685 }
4686 }
4687 if queue == OneQueue::Private {
4688 tracing::info!(
4689 "[dtid {}] under the in-guest delivery gate, removed ignored signal {} from the \
4690 private queue, which Linux discarded at send (D37)",
4691 guest.thread_state().dettid,
4692 signal
4693 );
4694 return Ok(());
4695 }
4696 loop {
4697 removals += 1;
4698 if removals > MAX_IGNORED_REMOVALS {
4699 return Err(failed("too many instances"));
4700 }
4701 match guest.inject(dequeue(false)).await {
4702 Ok(_) => {}
4703 Err(Errno::EAGAIN) => break,
4704 Err(errno) => return Err(failed(&format!("rt_sigtimedwait failed with {errno}"))),
4705 }
4706 }
4707 for siginfo in kept {
4708 guest.memory().write_value(info, &siginfo)?;
4709 let requeue = syscalls::RtTgsigqueueinfo::new()
4710 .with_tgid(guest.pid().as_raw())
4711 .with_tid(guest.tid().as_raw())
4712 .with_sig(signal)
4713 .with_siginfo(Some(info.cast()));
4714 if let Err(errno) = guest.inject(requeue).await {
4715 return Err(failed(&format!(
4716 "queueing a kept private instance again failed with {errno}"
4717 )));
4718 }
4719 }
4720 tracing::info!(
4721 "[dtid {}] under the in-guest delivery gate, removed ignored signal {} from the shared \
4722 queue, which Linux discarded at send, keeping its private instances (D37)",
4723 guest.thread_state().dettid,
4724 signal
4725 );
4726 Ok(())
4727}
4728
4729#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4732pub(crate) enum TemporaryMaskCheck {
4733 Waits,
4736 Fatal,
4741 EpollInterrupted,
4745 Refused,
4752}
4753
4754pub(crate) async fn temporary_mask_check<T, G>(
4781 guest: &mut G,
4782 m_temp: KernelSigset,
4783 entry: Option<KernelSigset>,
4784 epoll: bool,
4785) -> Result<TemporaryMaskCheck, Error>
4786where
4787 T: RecordOrReplay,
4788 G: Guest<Detcore<T>>,
4789{
4790 use crate::wait_signal_end::WaitSignalEnd;
4791 let state = read_thread_wait_signal_state(guest.pid(), guest.tid())?;
4792 let reserved = guest.config().in_guest_reserved_signals;
4793 match crate::wait_signal_end::wait_end(&state, m_temp, reserved) {
4794 None => {}
4795 Some(end @ WaitSignalEnd::Fatal(_)) => {
4796 tracing::info!(
4797 "[dtid {}] under the in-guest delivery gate, {:?} ends the wait under its \
4798 temporary mask {:#x}",
4799 guest.thread_state().dettid,
4800 end,
4801 m_temp
4802 );
4803 guest.thread_state_mut().wait_signal_end = Some(end);
4804 return Ok(TemporaryMaskCheck::Fatal);
4805 }
4806 Some(end) => {
4807 let signal = match end {
4808 WaitSignalEnd::Caught(signal) => signal,
4809 _ => {
4810 (state.pending & !m_temp & state.caught & !reserved).trailing_zeros() as i32 + 1
4811 }
4812 };
4813 let reason = crate::refused_signal_controls::temporary_mask_caught_end_refusal(
4814 guest.thread_state().dettid.as_raw(),
4815 signal,
4816 );
4817 tracing::error!("{}", reason);
4818 crate::tool_global::stopping_refusal(guest, reason).await;
4819 return Ok(TemporaryMaskCheck::Refused);
4820 }
4821 }
4822 let own = gated_own_mask(guest, state.blocked);
4823 let now = crate::ignored_signals::pending_ignored(&state, reserved);
4824 let (removed, interrupted) = match entry {
4825 Some(entry) => {
4826 let at_entry = KernelSignalState {
4827 pending: state.pending & entry,
4828 ..state
4829 };
4830 let since = KernelSignalState {
4831 pending: state.pending & !entry,
4832 ..state
4833 };
4834 let interrupted = epoll
4835 && crate::ignored_signals::epoll_interrupting(&at_entry, own, m_temp, reserved)
4836 != 0;
4837 (
4838 crate::ignored_signals::removable(&at_entry, own, m_temp, reserved)
4839 | crate::ignored_signals::removable(&since, m_temp, m_temp, reserved),
4840 interrupted,
4841 )
4842 }
4843 None => (now & !m_temp, false),
4844 };
4845 remove_ignored_signals(guest, removed).await?;
4846 Ok(if interrupted {
4847 TemporaryMaskCheck::EpollInterrupted
4848 } else {
4849 TemporaryMaskCheck::Waits
4850 })
4851}
4852
4853pub(crate) async fn caught_action_flags<T, G>(guest: &mut G, signal: i32) -> Result<u64, Error>
4858where
4859 T: RecordOrReplay,
4860 G: Guest<Detcore<T>>,
4861{
4862 Ok(caught_action(guest, signal).await?.flags)
4863}
4864
4865pub(crate) async fn caught_action<T, G>(
4872 guest: &mut G,
4873 signal: i32,
4874) -> Result<crate::wait_signal_end::CaughtAction, Error>
4875where
4876 T: RecordOrReplay,
4877 G: Guest<Detcore<T>>,
4878{
4879 let mut stack = guest.stack().await;
4880 let action = stack.reserve::<KernelSigaction>();
4881 let _stack_guard = stack.commit()?;
4882 guest
4883 .inject_with_retry(
4884 syscalls::RtSigaction::new()
4885 .with_signum(signal)
4886 .with_action(None)
4887 .with_old_action(Some(action.cast()))
4888 .with_sigsetsize(KERNEL_SIGSET_SIZE),
4889 )
4890 .await?;
4891 let action: KernelSigaction = guest.memory().read_value(action)?;
4892 Ok(crate::wait_signal_end::CaughtAction {
4893 mask: action.mask,
4894 flags: action.flags,
4895 })
4896}
4897
4898pub(crate) fn publishes_guest_masks(cfg: &crate::Config) -> bool {
4907 crate::scheduler::discarded_at_send::records(cfg) || cfg.in_guest_signal_delivery
4908}
4909
4910#[derive(Debug, Default)]
4915pub(crate) struct PublishedWaitMask(Option<KernelSigset>);
4916
4917pub(crate) async fn publish_wait_mask<T, G>(
4926 guest: &mut G,
4927 mask: Option<KernelSigset>,
4928 published: &mut PublishedWaitMask,
4929) where
4930 T: RecordOrReplay,
4931 G: Guest<Detcore<T>>,
4932{
4933 if mask == published.0 || !publishes_guest_masks(guest.config()) {
4934 return;
4935 }
4936 crate::tool_global::set_saved_guest_sigmask(guest, mask).await;
4937 published.0 = mask;
4938}
4939
4940async fn retry_blocking_wait_with_kernel_signal_state<T, G, C>(
4941 guest: &mut G,
4942 call0: C,
4943 rsrc: Resources,
4944 maybe_timeout: Option<(LogicalTime, Result<i64, Errno>)>,
4945) -> Result<i64, Error>
4946where
4947 C: NonblockableSyscall + Into<Syscall>,
4948 T: RecordOrReplay,
4949 G: Guest<Detcore<T>>,
4950{
4951 retry_blocking_wait_under_mask(guest, call0, rsrc, maybe_timeout, None)
4952 .await
4953 .0
4954}
4955
4956pub(crate) async fn retry_blocking_wait_under_mask<T, G, C>(
4966 guest: &mut G,
4967 call0: C,
4968 mut rsrc: Resources,
4969 maybe_timeout: Option<(LogicalTime, Result<i64, Errno>)>,
4970 wait_mask: Option<KernelSigset>,
4971) -> (Result<i64, Error>, bool)
4972where
4973 C: NonblockableSyscall + Into<Syscall>,
4974 T: RecordOrReplay,
4975 G: Guest<Detcore<T>>,
4976{
4977 let mut guest_held = GuestHeldPollProof::capture(guest, call0.into(), &mut rsrc);
4978 let mut signals = KernelSignalWait::new(
4979 guest,
4980 call0.signals_consumed_by_wait(&guest.memory()),
4981 call0.restart_keeps_deadline(),
4982 call0.kernel_restart_errno(),
4983 )
4984 .with_deadline(maybe_timeout.is_some())
4985 .with_wait_mask(wait_mask);
4986 let mut deliver_under_wait_mask = false;
4988 let mut published = PublishedWaitMask::default();
4989 publish_wait_mask(guest, signals.effective_mask(), &mut published).await;
4990 rsrc.poll_deadline = poll_deadline_for(guest, maybe_timeout.map(|(deadline, _)| deadline));
4991 let (mut call, mut guard) = call0.into_nonblocking(guest).await;
4992 let mut first_turn = true;
4993 let mut interrupt_after_probe;
4997
4998 let result = loop {
4999 interrupt_after_probe = false;
5000 let resumed = resource_request(guest, rsrc.clone()).await;
5003 guest_held.revalidate(guest, &mut rsrc);
5004 let (host_timed, recorded) =
5012 crate::tool_global::host_timed_and_private_sigchld(guest).await;
5013 signals.hold_until_return(host_timed);
5014 let committed = committed_signals(&resumed) & kernel_sigset_bit(libc::SIGCHLD);
5020 let private = match signals
5021 .admit_under_block(guest, committed, recorded, host_timed, &mut published)
5022 .await
5023 {
5024 Ok(private) => private,
5025 Err(error) => break Err(error),
5026 };
5027 signals.admit(committed, private);
5028 let reconvert =
5032 guard.is_some() && crate::syscalls::timerfd::may_publish(guest, call.into());
5033 if reconvert {
5034 guard = None;
5035 }
5036 rsrc.poll_deadline = crate::syscalls::timerfd::publish_before_probe(
5037 guest,
5038 call.into(),
5039 maybe_timeout.map(|(deadline, _)| deadline),
5040 )
5041 .await;
5042 if reconvert {
5043 (call, guard) = call0.into_nonblocking(guest).await;
5044 }
5045 let pending = pending_real_time_among(guest, REAL_TIME_SIGNALS).await;
5048 if pending != 0 {
5049 match signals.observable_now() {
5050 Ok(observable) => act_on_pending_real_time(guest, pending, observable, 0).await,
5051 Err(error) => break Err(error),
5052 }
5053 }
5054 let first = std::mem::take(&mut first_turn);
5055 let injected = if first {
5057 signals.inject_first_probe(guest, call).await
5061 } else {
5062 let state = match signals.interrupted_with_state() {
5063 Ok((false, state)) => state,
5064 Ok((true, state)) if signals.interrupts_only_by_committed(&state) => {
5072 interrupt_after_probe = true;
5073 deliver_under_wait_mask = signals.interrupts_only_under_wait_mask(&state);
5074 state
5075 }
5076 Ok((true, state)) => {
5077 deliver_under_wait_mask = signals.interrupts_only_under_wait_mask(&state);
5078 let errno = call0.kernel_restart_errno();
5079 tracing::trace!(
5080 "retry_nonblocking_syscall: pending signals interrupt {}: {:?}",
5081 call.display(&guest.memory()),
5082 errno
5083 );
5084 break Err(errno.into());
5085 }
5086 Err(error) => break Err(error),
5087 };
5088 signals.inject_absorbing_after(guest, call, state).await
5089 };
5090 let syscall_result = match injected {
5091 Ok(result) => result,
5092 Err(error) => {
5093 tracing::trace!(
5094 "retry_nonblocking_syscall: a signal stop ends the wait in {}: {:?}",
5095 call.display(&guest.memory()),
5096 error
5097 );
5098 break Err(error);
5099 }
5100 };
5101 if !call.syscall_would_have_blocked(syscall_result) {
5102 let normalized =
5103 call.normalize_nonblocking_result(syscall_result, rsrc.poll_attempt > 0);
5104 if normalized != syscall_result {
5105 guest.thread_state_mut().nonblocking_result_normalized = true;
5106 }
5107 let res = normalized.map_err(|e| e.into());
5108 tracing::trace!(
5109 "retry_nonblocking_syscall: syscall completed after {} retries: {} = {:?}",
5110 rsrc.poll_attempt,
5111 call.display(&guest.memory()),
5112 res
5113 );
5114 break res;
5115 }
5116 if interrupt_after_probe {
5117 let errno = call0.kernel_restart_errno();
5118 tracing::trace!(
5119 "retry_nonblocking_syscall: a committed signal interrupts {}: {:?}",
5120 call.display(&guest.memory()),
5121 errno
5122 );
5123 break Err(errno.into());
5124 }
5125 if first {
5126 match signals.interrupted_with_state() {
5128 Ok((false, _)) => {}
5129 Ok((true, state)) => {
5130 deliver_under_wait_mask = signals.interrupts_only_under_wait_mask(&state);
5131 let errno = call0.kernel_restart_errno();
5132 tracing::trace!(
5133 "retry_nonblocking_syscall: pending signals interrupt {}: {:?}",
5134 call.display(&guest.memory()),
5135 errno
5136 );
5137 break Err(errno.into());
5138 }
5139 Err(error) => break Err(error),
5140 }
5141 }
5142 if signals.needs_block() {
5143 guard = None;
5146 if let Err(error) = signals.block(guest, None).await {
5147 break Err(error);
5148 }
5149 publish_wait_mask(guest, signals.effective_mask(), &mut published).await;
5150 (call, guard) = call0.into_nonblocking(guest).await;
5151 }
5152 rsrc.poll_attempt += 1;
5153 if let Some((timeout, timeout_result)) = maybe_timeout {
5154 let new_time = thread_observe_time(guest).await;
5155 if new_time >= timeout {
5156 tracing::trace!(
5157 "Timing out syscall after #{} retries: {}",
5158 rsrc.poll_attempt - 1,
5159 call.display(&guest.memory())
5160 );
5161 break timeout_result.map_err(|e| e.into());
5162 }
5163 tracing::trace!(
5164 "Retry #{} for syscall due to result {:?}, {} from timeout: {}",
5165 rsrc.poll_attempt,
5166 syscall_result,
5167 timeout - new_time,
5168 call.display(&guest.memory())
5169 );
5170 } else {
5171 tracing::trace!(
5172 "Retry #{} for syscall due to result {:?}: {}",
5173 rsrc.poll_attempt,
5174 syscall_result,
5175 call.display(&guest.memory())
5176 );
5177 }
5178 record_retry_event(guest, call).await;
5179 };
5180
5181 drop(guard);
5182 let restored = signals.restore(guest, None).await;
5183 publish_wait_mask(guest, None, &mut published).await;
5184 let result = match result_after_restore(restored, result) {
5185 Ok(result) => result,
5186 Err(error) => return (Err(error), false),
5187 };
5188 let deliver = deliver_under_wait_mask && !signals.holds_signal() && result.is_err();
5191 (
5192 refuse_held_signal_loss(guest, call0.number(), result).await,
5193 deliver,
5194 )
5195}
5196
5197pub(crate) fn tool_decided_injection_failure(
5209 refusal: Option<reverie::UnsupportedRefusal>,
5210 dettid: DetTid,
5211 sysno: Sysno,
5212) -> Option<String> {
5213 refusal
5214 .filter(|refusal| refusal.operation == reverie::UnsupportedOperation::ToolDecided)
5215 .map(|refusal| {
5216 format!(
5217 "HERMIT_INTERNAL_FAILURE class=tool-decided-injection dtid={dettid} \
5218 syscall={sysno}: the backend refused a call Detcore injected, as one the Tool \
5219 decides on every backend ({}); Detcore must answer it itself",
5220 refusal.diagnostic
5221 )
5222 })
5223}
5224
5225pub(crate) fn fail_on_tool_decided_injection<T, G>(guest: &mut G, sysno: Sysno)
5261where
5262 T: RecordOrReplay,
5263 G: Guest<Detcore<T>>,
5264{
5265 let refusal = guest.take_unsupported_refusal();
5266 judge_backend_refusal::<T, G>(guest, refusal, sysno);
5267}
5268
5269pub(crate) fn judge_backend_refusal<T, G>(
5273 guest: &mut G,
5274 refusal: Option<reverie::UnsupportedRefusal>,
5275 sysno: Sysno,
5276) where
5277 T: RecordOrReplay,
5278 G: Guest<Detcore<T>>,
5279{
5280 let dettid = guest.thread_state().dettid;
5281 if refusal.is_some() {
5282 crate::syscall_histogram::mark_refused::<T, G>(guest);
5283 }
5284 if let Some(refusal) =
5285 refusal.filter(|refusal| refusal.operation == reverie::UnsupportedOperation::Signalfd)
5286 {
5287 tracing::warn!(
5288 "[dtid {dettid}] {sysno} refused by the backend: {}",
5289 refusal.diagnostic
5290 );
5291 }
5292 if let Some(failure) = tool_decided_injection_failure(refusal, dettid, sysno) {
5293 tracing::error!("{failure}");
5294 panic!("{failure}");
5295 }
5296}
5297
5298pub(crate) fn report_refusal_reason(fail_closed: bool, message: &str) {
5307 tracing::error!("{message}");
5308 if fail_closed {
5309 let _ = std::io::Write::write_fmt(
5310 &mut crate::util::RetryingStderr,
5311 format_args!("hermit: {message}\n"),
5312 );
5313 }
5314}
5315
5316pub(crate) async fn refuse_unserviceable_operation_with<T, G>(
5321 guest: &mut G,
5322 panic_on_unsupported_syscalls: bool,
5323 sysno: Sysno,
5324 fallback: Errno,
5325 reason: Option<&str>,
5326) -> Result<i64, Error>
5327where
5328 T: RecordOrReplay,
5329 G: Guest<Detcore<T>>,
5330{
5331 crate::syscall_histogram::mark_refused::<T, G>(guest);
5332 if !panic_on_unsupported_syscalls {
5333 return Err(fallback.into());
5334 }
5335 if guest.config().shutdown_on_unsupported_syscall {
5336 let reason = match reason {
5341 Some(reason) => format!("{sysno}: {reason}"),
5342 None => format!(
5343 "{sysno} cannot be serviced on this backend (with \
5344 --allow-unsupported-syscalls the guest would see {fallback})"
5345 ),
5346 };
5347 crate::tool_global::report_policy_refusal(guest, reason).await;
5348 crate::tool_global::unrecoverable_shutdown(
5349 guest,
5350 detcore_model::HERMIT_POLICY_REFUSAL_EXIT,
5351 )
5352 .await;
5353 }
5354 if guest.config().exit_on_unsupported_syscall {
5355 return Err(Error::Tool(anyhow::Error::new(
5356 crate::UnsupportedSyscallError(sysno),
5357 )));
5358 }
5359 panic!("unserviceable operation on syscall: {sysno:?}");
5360}
5361
5362#[must_use]
5381pub(crate) struct IgnoredSignalShield {
5382 guest_mask: Option<u64>,
5384}
5385
5386impl IgnoredSignalShield {
5387 pub(crate) fn none() -> Self {
5390 Self { guest_mask: None }
5391 }
5392
5393 pub(crate) async fn raise<T, G>(guest: &mut G) -> Result<Self, Error>
5401 where
5402 T: RecordOrReplay,
5403 G: Guest<Detcore<T>>,
5404 {
5405 let state = match crate::syscalls::read_thread_signal_state(guest.pid(), guest.tid()) {
5406 Ok(state) => state,
5407 Err(errno) => {
5408 tracing::debug!("[tid {}] no ignored-signal shield: {errno}", guest.tid());
5409 return Ok(Self::none());
5410 }
5411 };
5412 let Some(mask) = ignored_signal_shield_mask(&state) else {
5413 return Ok(Self::none());
5414 };
5415 match guest.set_blocked_signals(mask).await {
5416 Ok(guest_mask) => Ok(Self {
5417 guest_mask: Some(guest_mask),
5418 }),
5419 Err(refused) if shield_refusal_leaves_the_mask_known(&refused) => {
5420 tracing::debug!("[tid {}] no ignored-signal shield: {refused}", guest.tid());
5421 Ok(Self::none())
5422 }
5423 Err(refused) => Err(Error::Tool(anyhow::anyhow!(
5424 "[tid {}] could not block the signals the guest ignores ({mask:#x}) for a wait, \
5425 and its signal mask is unknown: {refused}",
5426 guest.tid()
5427 ))),
5428 }
5429 }
5430
5431 pub(crate) async fn lower<T, G>(mut self, guest: &mut G) -> Result<(), Error>
5434 where
5435 T: RecordOrReplay,
5436 G: Guest<Detcore<T>>,
5437 {
5438 let Some(guest_mask) = self.guest_mask.take() else {
5439 return Ok(());
5440 };
5441 match guest.set_blocked_signals(guest_mask).await {
5442 Ok(_) | Err(MaskChangeRefused::Gone) => Ok(()),
5443 Err(refused) => Err(Error::Tool(anyhow::anyhow!(
5444 "[tid {}] could not put back the guest's signal mask {guest_mask:#x} after a \
5445 wait that blocked the signals it ignores: {refused}",
5446 guest.tid()
5447 ))),
5448 }
5449 }
5450}
5451
5452fn shield_drop_message(guest_mask: u64) -> String {
5462 format!(
5463 "an ignored-signal shield ended with its handler, leaving the guest's mask \
5464 {guest_mask:#x} unrestored; the handler ended before the mask could be put \
5465 back (the thread may have died, or the run may be ending)"
5466 )
5467}
5468
5469impl Drop for IgnoredSignalShield {
5470 fn drop(&mut self) {
5471 if let Some(guest_mask) = self.guest_mask {
5472 tracing::debug!("{}", shield_drop_message(guest_mask));
5473 }
5474 }
5475}
5476
5477fn shield_refusal_leaves_the_mask_known(refused: &MaskChangeRefused) -> bool {
5481 !matches!(
5482 refused,
5483 MaskChangeRefused::Failed {
5484 mask_unknown: true,
5485 ..
5486 }
5487 )
5488}
5489
5490fn ignored_signal_shield_mask(state: &KernelSignalState) -> Option<KernelSigset> {
5494 let ignored = state.discarded_at_generation();
5495 (ignored != 0).then_some(state.blocked | ignored)
5496}
5497
5498pub(crate) async fn refuse_held_signal_loss<T, G>(
5512 guest: &mut G,
5513 sysno: Sysno,
5514 result: Result<i64, Error>,
5515) -> Result<i64, Error>
5516where
5517 T: RecordOrReplay,
5518 G: Guest<Detcore<T>>,
5519{
5520 use std::io::Write as _;
5521 let refusal = match &result {
5522 Err(Error::Tool(error)) => match error.downcast_ref::<HeldSignalRefusal>() {
5523 Some(refusal) => HeldSignalRefusal {
5524 wait: Some(sysno),
5525 ..*refusal
5526 },
5527 None => return result,
5528 },
5529 _ => return result,
5530 };
5531 let panic_on_unsupported_syscalls = guest.config().panic_on_unsupported_syscalls;
5532 tracing::error!("[tid {}] {}", guest.tid(), refusal);
5533 if panic_on_unsupported_syscalls {
5534 let _ = writeln!(crate::util::RetryingStderr, "{refusal}");
5537 }
5538 refuse_unserviceable_operation_with(
5539 guest,
5540 panic_on_unsupported_syscalls,
5541 sysno,
5542 Errno::ERESTARTNOINTR,
5543 None,
5544 )
5545 .await
5546}
5547
5548pub(crate) const REAL_TIME_SIGNALS: KernelSigset = !0 << 31;
5595
5596pub(crate) fn unblocked_by(
5601 state: &KernelSignalState,
5602 mask: KernelSigset,
5603) -> (KernelSigset, KernelSigset) {
5604 unblocked_by_ignored(state.ignored, mask)
5605}
5606
5607pub(crate) fn unblocked_by_ignored(
5610 ignored: KernelSigset,
5611 mask: KernelSigset,
5612) -> (KernelSigset, KernelSigset) {
5613 let unblocked = !mask;
5614 (unblocked & !ignored, unblocked & ignored)
5615}
5616
5617pub(crate) async fn pending_real_time_among<T, G>(guest: &mut G, candidates: KernelSigset) -> u64
5625where
5626 T: RecordOrReplay,
5627 G: Guest<Detcore<T>>,
5628{
5629 let candidates = candidates & REAL_TIME_SIGNALS;
5630 if candidates == 0 || !crate::scheduler::real_time_expiration_kept_pending() {
5631 return 0;
5632 }
5633 let Some(detpid) = guest.thread_state().detpid else {
5634 return 0;
5635 };
5636 crate::tool_global::pending_real_time_expirations(guest, detpid).await & candidates
5637}
5638
5639pub(crate) async fn act_on_pending_real_time<T, G>(
5644 guest: &mut G,
5645 pending: KernelSigset,
5646 observable: KernelSigset,
5647 discarded: KernelSigset,
5648) where
5649 T: RecordOrReplay,
5650 G: Guest<Detcore<T>>,
5651{
5652 let observed = pending & observable;
5653 if observed != 0 {
5654 crate::scheduler::refuse_real_time_expiration(observed.trailing_zeros() as i32 + 1);
5655 }
5656 if pending & discarded != 0
5657 && let Some(detpid) = guest.thread_state().detpid
5658 {
5659 crate::tool_global::discard_pending_real_time_expirations(
5660 guest,
5661 detpid,
5662 pending & discarded,
5663 )
5664 .await;
5665 }
5666}
5667
5668pub(crate) async fn observe_under_mask<T, G>(guest: &mut G, mask: KernelSigset) -> Result<(), Error>
5673where
5674 T: RecordOrReplay,
5675 G: Guest<Detcore<T>>,
5676{
5677 let pending = pending_real_time_among(guest, !mask).await;
5678 if pending != 0 {
5679 let ignored = match crate::syscalls::signal_shadow::copied_dispositions(guest) {
5680 Some(copied) => copied.ignored,
5681 None => read_thread_wait_signal_state(guest.pid(), guest.tid())?.ignored,
5682 };
5683 let (observable, discarded) = unblocked_by_ignored(ignored, mask);
5684 act_on_pending_real_time(guest, pending, observable, discarded).await;
5685 }
5686 Ok(())
5687}
5688
5689pub(crate) struct KernelSignalWait {
5690 pid: reverie::Pid,
5691 tid: reverie::Pid,
5692 consumed: KernelSigset,
5695 defers_default_stops: bool,
5699 restart_errno: Errno,
5704 held_until_return: KernelSigset,
5718 deadline: bool,
5726 saved_mask: Option<KernelSigset>,
5728 unblockable: bool,
5730 blocked_in_place: bool,
5734 held: Option<HeldSignal>,
5737 held_unidentified: bool,
5741 host_timed: KernelSigset,
5745 committed: KernelSigset,
5748 private: KernelSigset,
5754 wait_mask: Option<KernelSigset>,
5759}
5760
5761#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5764struct HeldSignal {
5765 signal: i32,
5766 queue: SignalQueue,
5767 kind: HeldKind,
5768}
5769
5770#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
5773enum HeldKind {
5774 Harmless,
5777 Precious,
5782}
5783
5784#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5787pub(crate) enum HeldSignalLoss {
5788 Replaced,
5791 UnidentifiedStop,
5795 StopBound,
5798 Consumed,
5803 Restoration,
5807}
5808
5809#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5823pub(crate) struct HeldSignalRefusal {
5824 pub(crate) wait: Option<Sysno>,
5827 pub(crate) held: Option<i32>,
5830 pub(crate) replacing: Option<i32>,
5833 pub(crate) loss: HeldSignalLoss,
5835}
5836
5837fn signal_label(signal: Option<i32>) -> String {
5839 match signal {
5840 Some(signal) => match nix::sys::signal::Signal::try_from(signal) {
5841 Ok(named) => named.as_str().to_string(),
5842 Err(_) => format!("signal {signal}"),
5843 },
5844 None => "a signal that could not be identified".to_string(),
5845 }
5846}
5847
5848impl std::fmt::Display for HeldSignalRefusal {
5849 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5850 let wait = self
5851 .wait
5852 .map_or_else(|| "a blocking".to_string(), |sysno| sysno.to_string());
5853 let held = match self.held {
5854 Some(_) => format!("the held signal {}", signal_label(self.held)),
5855 None => "a held signal that could not be identified".to_string(),
5856 };
5857 let replacing = signal_label(self.replacing);
5858 match self.loss {
5859 HeldSignalLoss::Replaced => write!(
5860 f,
5861 "unsupported: the {wait} wait cannot keep {held}: the pending {replacing} \
5862 would stop the next injection and replace it"
5863 )?,
5864 HeldSignalLoss::UnidentifiedStop => write!(
5865 f,
5866 "unsupported: the {wait} wait cannot keep {held}: a signal stop that /proc \
5867 cannot identify replaced it"
5868 )?,
5869 HeldSignalLoss::StopBound => write!(
5870 f,
5871 "unsupported: the {wait} wait cannot keep {held}: the wait absorbed the \
5872 maximum number of signal stops"
5873 )?,
5874 HeldSignalLoss::Consumed => {
5875 write!(
5876 f,
5877 "unsupported: the {wait} wait cannot return {replacing}, which it consumes: \
5878 the signal stopped an injection, so the backend would deliver it instead"
5879 )?;
5880 if self.held.is_some() {
5881 write!(f, ", in place of {held}")?;
5882 }
5883 }
5884 HeldSignalLoss::Restoration => write!(
5885 f,
5886 "unsupported: the {wait} wait cannot keep {held}: {replacing} stopped the \
5887 injection that puts the guest's signal mask back and replaced it"
5888 )?,
5889 }
5890 write!(
5891 f,
5892 " (scope reduction of https://github.com/rrnewton/hermit/pull/3361; rerun with \
5893 --allow-unsupported-syscalls to restart the call instead)"
5894 )
5895 }
5896}
5897
5898impl std::error::Error for HeldSignalRefusal {}
5899
5900#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5902enum StopVerdict {
5903 Absorb(HeldKind),
5905 End(Errno),
5907}
5908
5909const KERNEL_SIGRTMIN: i32 = 32;
5912
5913impl KernelSignalWait {
5914 pub(crate) fn new<T, G>(
5915 guest: &G,
5916 consumed: KernelSigset,
5917 defers_default_stops: bool,
5918 restart_errno: Errno,
5919 ) -> Self
5920 where
5921 T: RecordOrReplay,
5922 G: Guest<Detcore<T>>,
5923 {
5924 Self {
5925 pid: guest.pid(),
5926 tid: guest.tid(),
5927 consumed,
5928 defers_default_stops,
5929 restart_errno,
5930 held_until_return: kernel_sigset_bit(libc::SIGCHLD),
5931 deadline: false,
5932 saved_mask: None,
5933 unblockable: false,
5934 blocked_in_place: false,
5935 held: None,
5936 held_unidentified: false,
5937 host_timed: 0,
5938 committed: 0,
5939 private: 0,
5940 wait_mask: None,
5941 }
5942 }
5943
5944 pub(crate) fn with_deadline(self, deadline: bool) -> Self {
5948 Self { deadline, ..self }
5949 }
5950
5951 pub(crate) fn for_select<T, G>(guest: &G) -> Self
5969 where
5970 T: RecordOrReplay,
5971 G: Guest<Detcore<T>>,
5972 {
5973 Self {
5974 held_until_return: 0,
5975 ..Self::new(guest, 0, false, Errno::ERESTARTNOHAND)
5976 }
5977 }
5978
5979 pub(crate) fn with_wait_mask(self, mask: Option<KernelSigset>) -> Self {
5985 Self {
5986 wait_mask: mask,
5987 ..self
5988 }
5989 }
5990
5991 pub(crate) fn effective_mask(&self) -> Option<KernelSigset> {
5996 self.wait_mask.or(self.saved_mask)
5997 }
5998
5999 pub(crate) fn holds_signal(&self) -> bool {
6003 self.held.is_some() || self.held_unidentified
6004 }
6005
6006 pub(crate) fn interrupts_only_under_wait_mask(&self, state: &KernelSignalState) -> bool {
6016 let own_mask = self.saved_mask.unwrap_or(state.blocked);
6017 let held_until_return = self.held_until_return & !self.committed;
6018 let could_interrupt = self.could_interrupt(state);
6019 let interrupting = (state.pending & !held_until_return
6020 | state.thread_pending & self.private)
6021 & could_interrupt;
6022 self.wait_mask.is_some() && interrupting & own_mask != 0
6023 }
6024
6025 pub(crate) fn hold_until_return(&mut self, signals: KernelSigset) {
6033 self.held_until_return |= signals;
6034 self.host_timed |= signals;
6035 }
6036
6037 pub(crate) fn admit(&mut self, committed: KernelSigset, private: KernelSigset) {
6052 self.committed = committed & !self.host_timed;
6053 self.private = private & !self.host_timed;
6054 }
6055
6056 pub(crate) async fn admit_under_block<T, G>(
6069 &mut self,
6070 guest: &mut G,
6071 committed: KernelSigset,
6072 recorded: KernelSigset,
6073 host_timed: KernelSigset,
6074 published: &mut PublishedWaitMask,
6075 ) -> Result<KernelSigset, Error>
6076 where
6077 T: RecordOrReplay,
6078 G: Guest<Detcore<T>>,
6079 {
6080 if (committed | recorded) & !host_timed == 0 {
6081 return Ok(0);
6082 }
6083 if self.needs_block() {
6084 if self.block_in_place(guest, host_timed).await? {
6085 publish_wait_mask(guest, self.effective_mask(), published).await;
6086 } else {
6087 self.unblockable = false;
6089 }
6090 }
6091 if recorded == 0 || self.saved_mask.is_none() {
6092 return Ok(0);
6093 }
6094 let state = read_wait_signal_state(self.pid, self.tid)?;
6095 Ok(recorded & state.thread_pending)
6096 }
6097
6098 pub(crate) fn interrupts_only_by_committed(&self, state: &KernelSignalState) -> bool {
6102 let held = self.held.map_or(0, |held| kernel_sigset_bit(held.signal));
6103 let interrupting = (state.pending | held) & self.could_interrupt(state);
6104 (self.committed | self.private) != 0 && interrupting & !self.held_until_return == 0
6105 }
6106
6107 pub(crate) fn needs_block(&self) -> bool {
6110 self.saved_mask.is_none() && !self.unblockable
6111 }
6112
6113 pub(crate) fn observable_now(&self) -> Result<KernelSigset, Error> {
6119 let state = read_wait_signal_state(self.pid, self.tid)?;
6120 Ok(self.consumed | self.could_interrupt(&state))
6121 }
6122
6123 fn could_interrupt(&self, state: &KernelSignalState) -> KernelSigset {
6124 let guest_mask = self.wait_mask.or(self.saved_mask).unwrap_or(state.blocked);
6125 state.interrupting_wait(guest_mask, self.defers_default_stops) & !self.consumed
6126 }
6127
6128 #[cfg(test)]
6130 pub(crate) fn interrupted(&self) -> Result<bool, Error> {
6131 Ok(self.interrupted_with_state()?.0)
6132 }
6133
6134 pub(crate) fn interrupted_with_state(&self) -> Result<(bool, KernelSignalState), Error> {
6152 let state = read_wait_signal_state(self.pid, self.tid)?;
6153 let could_interrupt = self.could_interrupt(&state);
6154 let held = self.held.map_or(0, |held| kernel_sigset_bit(held.signal));
6155 let held_until_return = self.held_until_return & !self.committed;
6156 let interrupting = ((state.pending | held) & !held_until_return
6159 | state.thread_pending & self.private)
6160 & could_interrupt;
6161 if interrupting != 0 {
6162 tracing::trace!(
6163 "[tid {}] pending signals {:#x} interrupt a blocking wait",
6164 self.tid,
6165 interrupting
6166 );
6167 }
6168 Ok((interrupting != 0, state))
6169 }
6170
6171 pub(crate) async fn inject_absorbing<T, G, S>(
6250 &mut self,
6251 guest: &mut G,
6252 call: S,
6253 ) -> Result<Result<i64, Errno>, Error>
6254 where
6255 T: RecordOrReplay,
6256 G: Guest<Detcore<T>>,
6257 S: SyscallInfo,
6258 {
6259 self.inject_absorbing_from(guest, call, None, false).await
6260 }
6261
6262 pub(crate) async fn inject_first_probe<T, G, S>(
6288 &mut self,
6289 guest: &mut G,
6290 call: S,
6291 ) -> Result<Result<i64, Errno>, Error>
6292 where
6293 T: RecordOrReplay,
6294 G: Guest<Detcore<T>>,
6295 S: SyscallInfo,
6296 {
6297 self.inject_absorbing_from(guest, call, None, true).await
6298 }
6299
6300 pub(crate) async fn inject_absorbing_after<T, G, S>(
6328 &mut self,
6329 guest: &mut G,
6330 call: S,
6331 state: KernelSignalState,
6332 ) -> Result<Result<i64, Errno>, Error>
6333 where
6334 T: RecordOrReplay,
6335 G: Guest<Detcore<T>>,
6336 S: SyscallInfo,
6337 {
6338 let first = self.saved_mask.is_some().then_some(state);
6339 self.inject_absorbing_from(guest, call, first, false).await
6340 }
6341
6342 async fn inject_absorbing_from<T, G, S>(
6347 &mut self,
6348 guest: &mut G,
6349 call: S,
6350 mut first: Option<KernelSignalState>,
6351 first_probe: bool,
6352 ) -> Result<Result<i64, Errno>, Error>
6353 where
6354 T: RecordOrReplay,
6355 G: Guest<Detcore<T>>,
6356 S: SyscallInfo,
6357 {
6358 const MAX_ABSORBED_STOPS: usize = 64;
6363 let mut absorbed = 0;
6364 loop {
6365 let before = match first.take() {
6366 Some(state) => state,
6367 None => read_wait_signal_state(self.pid, self.tid)?,
6368 };
6369 let ends_wait = self.could_interrupt(&before) & !self.held_until_return;
6370 if !first_probe && before.pending & ends_wait != 0 {
6371 return Err(self.restart_errno.into());
6372 }
6373 let next = before.next_dequeued(before.blocked);
6374 if let Some((signal, queue)) = next
6375 && self.would_replace_precious(signal, queue)
6376 {
6377 if let Some(held) = self.held
6378 && self.held_ends_wait(&before, held.signal)
6379 {
6380 tracing::trace!(
6381 "[tid {}] signal {} would replace the held signal {}, which ends the wait",
6382 self.tid,
6383 signal,
6384 held.signal
6385 );
6386 return Err(held_signal_restart_errno(self.restart_errno, held.signal).into());
6387 }
6388 tracing::trace!(
6392 "[tid {}] signal {} would replace the held signal {:?}; the wait is refused",
6393 self.tid,
6394 signal,
6395 self.held
6396 );
6397 return Err(self.refusal(Some(signal), HeldSignalLoss::Replaced));
6398 }
6399 let result = guest.inject(call).await;
6400 let Err(errno) = result else {
6401 return Ok(result);
6402 };
6403 if !probe_was_interrupted_by_signal(errno) {
6404 return Ok(result);
6405 }
6406 let after = read_wait_signal_state(self.pid, self.tid)?;
6407 if self.wait_mask.is_some()
6418 && errno == Errno::ERESTARTNOHAND
6419 && after.blocked != before.blocked
6420 {
6421 return Ok(result);
6422 }
6423 let identified =
6424 next.filter(|&(signal, queue)| stop_took_only(&before, &after, signal, queue));
6425 let Some((signal, queue)) = identified else {
6426 if self.holds_precious() {
6438 tracing::trace!(
6439 "[tid {}] a signal stop that cannot be identified replaced the held signal {:?}; the wait is refused",
6440 self.tid,
6441 self.held
6442 );
6443 return Err(self.refusal(None, HeldSignalLoss::UnidentifiedStop));
6444 }
6445 if !self.deadline && absorbed >= MAX_ABSORBED_STOPS {
6446 self.held = None;
6448 self.held_unidentified = true;
6449 return Err(self.refusal(None, HeldSignalLoss::StopBound));
6450 }
6451 absorbed += 1;
6452 tracing::trace!(
6453 "[tid {}] a signal stop that cannot be identified is held; the injection runs again",
6454 self.tid
6455 );
6456 self.held = None;
6457 self.held_unidentified = true;
6458 continue;
6459 };
6460 let bit = kernel_sigset_bit(signal);
6461 let verdict = match self.stop_verdict(&before, signal) {
6462 StopVerdict::End(_)
6465 if first_probe
6466 && self.consumed & bit == 0
6467 && self.could_interrupt(&before) & bit != 0 =>
6468 {
6469 StopVerdict::Absorb(HeldKind::Precious)
6470 }
6471 verdict => verdict,
6472 };
6473 match verdict {
6474 StopVerdict::Absorb(kind) => {
6475 let kind = self.held.map_or(kind, |held| held.kind.max(kind));
6476 let unidentified = self.held_unidentified;
6477 self.held = Some(HeldSignal {
6478 signal,
6479 queue,
6480 kind,
6481 });
6482 self.held_unidentified = false;
6483 if !self.deadline && absorbed >= MAX_ABSORBED_STOPS {
6484 if kind == HeldKind::Precious || unidentified {
6485 return Err(self.refusal(None, HeldSignalLoss::StopBound));
6486 }
6487 return Err(Errno::ERESTARTNOINTR.into());
6488 }
6489 absorbed += 1;
6490 tracing::trace!(
6491 "[tid {}] signal {} stopped an injection and is held ({:?})",
6492 self.tid,
6493 signal,
6494 kind
6495 );
6496 }
6497 StopVerdict::End(_)
6502 if !self.deadline
6503 && self.consumed & bit != 0
6504 && (self.held_until_return & bit != 0 || self.holds_precious()) =>
6505 {
6506 tracing::trace!(
6507 "[tid {}] consumed signal {} stopped an injection; the wait is refused",
6508 self.tid,
6509 signal
6510 );
6511 return Err(self.refusal(Some(signal), HeldSignalLoss::Consumed));
6512 }
6513 StopVerdict::End(errno) => {
6515 return Err(held_signal_restart_errno(errno, signal).into());
6516 }
6517 }
6518 }
6519 }
6520
6521 fn stop_verdict(&self, before: &KernelSignalState, signal: i32) -> StopVerdict {
6524 let bit = kernel_sigset_bit(signal);
6525 let interrupts = if self.consumed & bit != 0 {
6526 if !self.deadline {
6527 return StopVerdict::End(Errno::ERESTARTNOINTR);
6528 }
6529 let guest_mask = self.wait_mask.or(self.saved_mask).unwrap_or(before.blocked);
6533 before.interrupting_wait(guest_mask, self.defers_default_stops) & bit != 0
6534 } else {
6535 self.could_interrupt(before) & bit != 0
6536 };
6537 if interrupts {
6538 return if self.held_until_return & bit != 0 {
6539 StopVerdict::Absorb(HeldKind::Precious)
6540 } else {
6541 StopVerdict::End(self.restart_errno)
6542 };
6543 }
6544 if signal == libc::SIGCHLD
6545 || (self.defers_default_stops && before.default_job_control_stops() & bit != 0)
6546 {
6547 return StopVerdict::Absorb(HeldKind::Precious);
6548 }
6549 let default_ignored = [libc::SIGCONT, libc::SIGURG, libc::SIGWINCH]
6550 .into_iter()
6551 .fold(0, |set, signal| set | kernel_sigset_bit(signal));
6552 if (before.ignored | (default_ignored & !before.caught)) & bit != 0 {
6553 return StopVerdict::Absorb(HeldKind::Harmless);
6554 }
6555 if self.deadline || self.wait_mask.is_some_and(|mask| mask & bit != 0) {
6562 StopVerdict::Absorb(HeldKind::Precious)
6563 } else {
6564 StopVerdict::End(Errno::ERESTARTNOINTR)
6565 }
6566 }
6567
6568 fn would_replace_precious(&self, signal: i32, queue: SignalQueue) -> bool {
6574 self.held_unidentified
6575 || self.held.is_some_and(|held| {
6576 held.kind == HeldKind::Precious
6577 && !(signal == held.signal && queue == held.queue && signal < KERNEL_SIGRTMIN)
6578 })
6579 }
6580
6581 fn holds_precious(&self) -> bool {
6584 self.held_unidentified
6585 || self
6586 .held
6587 .is_some_and(|held| held.kind == HeldKind::Precious)
6588 }
6589
6590 fn refusal(&self, replacing: Option<i32>, loss: HeldSignalLoss) -> Error {
6594 Error::Tool(anyhow::Error::new(HeldSignalRefusal {
6595 wait: None,
6596 held: self.held.map(|held| held.signal),
6597 replacing,
6598 loss,
6599 }))
6600 }
6601
6602 fn held_ends_wait(&self, before: &KernelSignalState, signal: i32) -> bool {
6606 self.could_interrupt(before) & !self.held_until_return & kernel_sigset_bit(signal) != 0
6607 }
6608
6609 pub(crate) async fn block<'a, T, G>(
6627 &mut self,
6628 guest: &mut G,
6629 scratch: Option<AddrMut<'a, KernelSigset>>,
6630 ) -> Result<(), Error>
6631 where
6632 T: RecordOrReplay,
6633 G: Guest<Detcore<T>>,
6634 {
6635 let guest_mask = read_wait_signal_state(self.pid, self.tid)?.blocked;
6636 let mask = blocked_signal_mask() | guest_mask;
6637 let result = match scratch {
6638 Some(cell) => match guest.memory().write_value(cell, &mask) {
6639 Ok(()) => {
6640 self.inject_absorbing(guest, set_signal_mask_call(cell.into()))
6641 .await
6642 }
6643 Err(errno) => Ok(Err(errno)),
6644 },
6645 None => {
6646 let mut stack = guest.stack().await;
6647 let cell = stack.push(mask);
6648 match stack.commit() {
6649 Ok(_guard) => {
6650 self.inject_absorbing(guest, set_signal_mask_call(cell))
6651 .await
6652 }
6653 Err(_) => Ok(Err(Errno::EFAULT)),
6654 }
6655 }
6656 };
6657 match result {
6658 Ok(Ok(_)) => {
6659 self.saved_mask = Some(guest_mask);
6660 Ok(())
6661 }
6662 Ok(Err(errno)) => {
6663 tracing::debug!(
6664 "[tid {}] cannot block signals for a wait ({}); its probes run under the \
6665 guest's mask",
6666 self.tid,
6667 errno
6668 );
6669 self.unblockable = true;
6670 Ok(())
6671 }
6672 Err(error) => {
6673 self.saved_mask = Some(guest_mask);
6674 Err(error)
6675 }
6676 }
6677 }
6678
6679 pub(crate) async fn block_in_place<T, G>(
6700 &mut self,
6701 guest: &mut G,
6702 host_timed: KernelSigset,
6703 ) -> Result<bool, Error>
6704 where
6705 T: RecordOrReplay,
6706 G: Guest<Detcore<T>>,
6707 {
6708 self.hold_until_return(host_timed);
6713 let guest_mask = read_wait_signal_state(self.pid, self.tid)?.blocked;
6714 match guest
6715 .set_blocked_signals(blocked_signal_mask() | guest_mask)
6716 .await
6717 {
6718 Ok(old) => {
6719 self.saved_mask = Some(old);
6720 self.blocked_in_place = true;
6721 Ok(true)
6722 }
6723 Err(MaskChangeRefused::Failed {
6724 errno,
6725 mask_unknown: true,
6726 }) => Err(self.mask_not_restored(1, Some(errno))),
6727 Err(refused) => {
6728 tracing::debug!(
6729 "[tid {}] cannot block signals in place for a wait ({}); its attempts run \
6730 under the guest's mask",
6731 self.tid,
6732 refused
6733 );
6734 self.unblockable = true;
6736 Ok(false)
6737 }
6738 }
6739 }
6740
6741 fn mask_not_restored(&self, attempts: usize, last_error: Option<Errno>) -> Error {
6743 Error::Tool(anyhow::Error::new(
6744 BlockedWaitSignalError::MaskNotRestored {
6745 pid: self.pid,
6746 tid: self.tid,
6747 attempts,
6748 last_error,
6749 },
6750 ))
6751 }
6752
6753 async fn release_probe_mask_in_place<T, G>(&mut self, guest: &mut G) -> Result<(), Error>
6764 where
6765 T: RecordOrReplay,
6766 G: Guest<Detcore<T>>,
6767 {
6768 let Some(guest_mask) = self.saved_mask else {
6769 return Ok(());
6772 };
6773 if !self.blocked_in_place {
6774 return Err(self.mask_not_restored(0, None));
6775 }
6776 match guest.set_blocked_signals(guest_mask).await {
6777 Ok(_) => {
6778 self.saved_mask = None;
6779 self.blocked_in_place = false;
6780 Ok(())
6781 }
6782 Err(MaskChangeRefused::Gone) => {
6783 self.saved_mask = None;
6784 self.blocked_in_place = false;
6785 Err(Errno::ERESTARTNOINTR.into())
6789 }
6790 Err(MaskChangeRefused::Failed {
6791 errno,
6792 mask_unknown: true,
6793 }) => {
6794 self.saved_mask = None;
6795 self.blocked_in_place = false;
6796 Err(self.mask_not_restored(1, Some(errno)))
6797 }
6798 Err(refused) => {
6799 let errno = match refused {
6800 MaskChangeRefused::Failed { errno, .. } => Some(errno),
6801 _ => None,
6802 };
6803 Err(self.mask_not_restored(1, errno))
6804 }
6805 }
6806 }
6807
6808 pub(crate) async fn restore<'a, T, G>(
6826 &mut self,
6827 guest: &mut G,
6828 scratch: Option<AddrMut<'a, KernelSigset>>,
6829 ) -> Result<(), Error>
6830 where
6831 T: RecordOrReplay,
6832 G: Guest<Detcore<T>>,
6833 {
6834 let Some(guest_mask) = self.saved_mask else {
6835 return Ok(());
6836 };
6837 if std::mem::take(&mut self.blocked_in_place) {
6845 match guest.set_blocked_signals(guest_mask).await {
6846 Ok(_) | Err(MaskChangeRefused::Gone) => {
6847 self.saved_mask = None;
6848 return Ok(());
6849 }
6850 Err(MaskChangeRefused::Failed {
6851 errno,
6852 mask_unknown: true,
6853 }) => {
6854 self.saved_mask = None;
6855 return Err(self.mask_not_restored(1, Some(errno)));
6856 }
6857 Err(refused) => {
6858 tracing::debug!(
6859 "[tid {}] cannot restore the mask in place ({}); injecting it",
6860 self.tid,
6861 refused
6862 );
6863 }
6864 }
6865 }
6866 let restored = self.restore_mask(guest, guest_mask, scratch).await;
6869 self.saved_mask = None;
6870 restored
6871 }
6872
6873 async fn restore_mask<'a, T, G>(
6875 &mut self,
6876 guest: &mut G,
6877 guest_mask: KernelSigset,
6878 scratch: Option<AddrMut<'a, KernelSigset>>,
6879 ) -> Result<(), Error>
6880 where
6881 T: RecordOrReplay,
6882 G: Guest<Detcore<T>>,
6883 {
6884 const ATTEMPTS: usize = 16;
6888 let mut attempts = 0;
6889 let mut last_error = None;
6890 let mut stopped = None;
6893 let mut loss = None;
6896 while attempts < ATTEMPTS {
6897 let state = read_wait_signal_state(self.pid, self.tid)?;
6898 if let Some(before) = stopped.take() {
6899 let replaced = self.absorb_restoration_stop(&before, &state);
6900 loss = loss.or(replaced);
6901 }
6902 if state.blocked == guest_mask {
6903 return loss.map_or(Ok(()), Err);
6904 }
6905 attempts += 1;
6906 match inject_signal_mask(guest, guest_mask, scratch).await {
6907 Ok(()) => return loss.map_or(Ok(()), Err),
6908 Err(errno) if probe_was_interrupted_by_signal(errno) => {
6911 last_error = Some(errno);
6912 stopped = Some(state);
6913 }
6914 Err(errno) => {
6916 last_error = Some(errno);
6917 break;
6918 }
6919 }
6920 }
6921 let state = read_wait_signal_state(self.pid, self.tid)?;
6922 if let Some(before) = stopped.take() {
6923 let replaced = self.absorb_restoration_stop(&before, &state);
6924 loss = loss.or(replaced);
6925 }
6926 if state.blocked == guest_mask {
6927 return loss.map_or(Ok(()), Err);
6928 }
6929 Err(Error::Tool(anyhow::Error::new(
6930 BlockedWaitSignalError::MaskNotRestored {
6931 pid: self.pid,
6932 tid: self.tid,
6933 attempts,
6934 last_error,
6935 },
6936 )))
6937 }
6938
6939 fn absorb_restoration_stop(
6954 &mut self,
6955 before: &KernelSignalState,
6956 after: &KernelSignalState,
6957 ) -> Option<Error> {
6958 let identified = before
6959 .next_dequeued(before.blocked)
6960 .filter(|&(signal, queue)| stop_took_only(before, after, signal, queue));
6961 let Some((signal, queue)) = identified else {
6962 let loss = self
6963 .holds_precious()
6964 .then(|| self.refusal(None, HeldSignalLoss::Restoration));
6965 tracing::trace!(
6966 "[tid {}] a signal stop that cannot be identified stopped the mask restoration; it replaced the held signal {:?}",
6967 self.tid,
6968 self.held
6969 );
6970 self.held = None;
6971 self.held_unidentified = true;
6972 return loss;
6973 };
6974 let loss = self
6975 .would_replace_precious(signal, queue)
6976 .then(|| self.refusal(Some(signal), HeldSignalLoss::Restoration));
6977 let kind = match self.stop_verdict(before, signal) {
6978 StopVerdict::Absorb(kind) => kind,
6979 StopVerdict::End(_) => HeldKind::Precious,
6980 };
6981 let kind = self.held.map_or(kind, |held| held.kind.max(kind));
6982 tracing::trace!(
6983 "[tid {}] signal {} stopped the mask restoration and is held ({:?}) in place of {:?}",
6984 self.tid,
6985 signal,
6986 kind,
6987 self.held
6988 );
6989 self.held = Some(HeldSignal {
6990 signal,
6991 queue,
6992 kind,
6993 });
6994 self.held_unidentified = false;
6995 loss
6996 }
6997}
6998
6999pub(crate) fn result_after_restore(
7008 restored: Result<(), Error>,
7009 result: Result<i64, Error>,
7010) -> Result<Result<i64, Error>, Error> {
7011 let is_refusal =
7012 |error: &Error| matches!(error, Error::Tool(error) if error.is::<HeldSignalRefusal>());
7013 let is_mask_failure = |error: &Error| {
7014 matches!(error, Error::Tool(error) if matches!(
7015 error.downcast_ref::<BlockedWaitSignalError>(),
7016 Some(BlockedWaitSignalError::MaskNotRestored { .. })
7017 ))
7018 };
7019 match restored {
7020 Ok(()) => Ok(result),
7021 Err(refusal) if is_refusal(&refusal) => match &result {
7022 Err(error) if is_refusal(error) || is_mask_failure(error) => Ok(result),
7023 _ => Ok(Err(refusal)),
7024 },
7025 Err(error) => Err(error),
7026 }
7027}
7028
7029async fn inject_signal_mask<'a, T, G>(
7032 guest: &mut G,
7033 mask: KernelSigset,
7034 scratch: Option<AddrMut<'a, KernelSigset>>,
7035) -> Result<(), Errno>
7036where
7037 T: RecordOrReplay,
7038 G: Guest<Detcore<T>>,
7039{
7040 match scratch {
7041 Some(cell) => {
7042 guest.memory().write_value(cell, &mask)?;
7043 guest
7044 .inject(set_signal_mask_call(cell.into()))
7045 .await
7046 .map(drop)
7047 }
7048 None => {
7049 let mut stack = guest.stack().await;
7050 let cell = stack.push(mask);
7051 let _guard = stack.commit().map_err(|_| Errno::EFAULT)?;
7052 guest.inject(set_signal_mask_call(cell)).await.map(drop)
7053 }
7054 }
7055}
7056
7057fn set_signal_mask_call(cell: Addr<'_, KernelSigset>) -> syscalls::RtSigprocmask {
7059 syscalls::RtSigprocmask::new()
7060 .with_how(libc::SIG_SETMASK)
7061 .with_set(Some(cell.cast()))
7062 .with_oldset(None)
7063 .with_sigsetsize(KERNEL_SIGSET_SIZE)
7064}
7065
7066fn stop_took_only(
7071 before: &KernelSignalState,
7072 after: &KernelSignalState,
7073 signal: i32,
7074 queue: SignalQueue,
7075) -> bool {
7076 let bit = kernel_sigset_bit(signal);
7077 let other = match queue {
7078 SignalQueue::Thread => SignalQueue::Shared,
7079 SignalQueue::Shared => SignalQueue::Thread,
7080 };
7081 before.blocked == after.blocked
7082 && before.ignored == after.ignored
7083 && before.caught == after.caught
7084 && after.queued(queue) & bit == 0
7085 && before.queued(queue) & !bit & !after.queued(queue) == 0
7086 && before.queued(other) & !after.queued(other) == 0
7087}
7088
7089pub(crate) fn probe_was_interrupted_by_signal(errno: Errno) -> bool {
7093 matches!(
7094 errno,
7095 Errno::EINTR
7096 | Errno::ERESTARTSYS
7097 | Errno::ERESTARTNOINTR
7098 | Errno::ERESTARTNOHAND
7099 | Errno::ERESTART_RESTARTBLOCK
7100 )
7101}
7102
7103pub(crate) async fn record_retry_event<G, C, T>(guest: &mut G, call: C)
7104where
7105 C: SyscallInfo,
7106 T: RecordOrReplay,
7107 G: Guest<Detcore<T>>,
7108{
7109 let dettid = guest.thread_state().dettid;
7110 let cfg = &guest.config();
7111 if cfg.sequentialize_threads && cfg.observes_schedevents() {
7112 trace_schedevent(
7113 guest,
7114 with_guest_time(
7115 guest,
7116 SchedEvent::syscall(dettid, call.number(), SyscallPhase::Polling),
7117 ),
7118 true,
7119 )
7120 .await;
7121 }
7122}
7123
7124pub fn with_guest_time<G, T>(guest: &G, event: SchedEvent) -> SchedEvent
7126where
7127 G: Guest<Detcore<T>>,
7128 T: RecordOrReplay,
7129{
7130 let dettime = &guest.thread_state().thread_logical_time;
7131 event.with_dettime(dettime)
7132}
7133
7134pub async fn with_guest_rip<G, T>(guest: &mut G, mut event: SchedEvent) -> SchedEvent
7136where
7137 G: Guest<Detcore<T>>,
7138 T: RecordOrReplay,
7139{
7140 assert!(event.end_rip.is_none());
7141
7142 let regs = guest.regs().await;
7143 let end_rip =
7144 NonZeroUsize::new(crate::arch::instruction_pointer(®s).try_into().unwrap()).unwrap();
7145 event.end_rip = Some(end_rip);
7146 event
7147}
7148
7149pub async fn millis_duration_to_absolute_timeout<G: Guest<Detcore<T>>, T: RecordOrReplay>(
7152 guest: &mut G,
7153 timeout_millis: i32,
7154) -> Option<LogicalTime> {
7155 match positive_millis_as_nanos(timeout_millis) {
7156 Some(timeout_nanos) => nanos_duration_to_absolute_timeout(guest, timeout_nanos).await,
7157 None => None,
7158 }
7159}
7160
7161fn positive_millis_as_nanos(timeout_millis: i32) -> Option<u128> {
7171 (timeout_millis > 0).then(|| (timeout_millis as u128) * 1_000_000)
7172}
7173
7174pub async fn nanos_duration_to_absolute_timeout<G: Guest<Detcore<T>>, T: RecordOrReplay>(
7177 guest: &mut G,
7178 timeout_nanos: u128,
7179) -> Option<LogicalTime> {
7180 if timeout_nanos > 0 {
7181 let ns_delta = Duration::from_nanos(timeout_nanos as u64);
7182 let base_time = thread_observe_time(guest).await;
7183 let target_time = base_time + ns_delta;
7184 Some(target_time)
7185 } else {
7186 None
7187 }
7188}
7189
7190pub fn poll_nfds(nfds: libc::nfds_t) -> u32 {
7195 nfds as u32
7196}
7197
7198#[cfg(test)]
7199mod tests {
7200 use super::*;
7201
7202 #[test]
7206 fn a_shield_dropped_with_its_handler_ends_quietly() {
7207 drop(IgnoredSignalShield {
7208 guest_mask: Some(0),
7209 });
7210 drop(IgnoredSignalShield::none());
7211 }
7212
7213 #[test]
7217 fn a_dropped_shield_log_does_not_claim_the_thread_is_gone() {
7218 let message = shield_drop_message(0x2a);
7219 assert!(message.contains("ended with its handler"), "{message}");
7220 assert!(message.contains("unrestored"), "{message}");
7221 assert!(!message.contains("is gone"), "{message}");
7222 assert!(!message.contains("are gone"), "{message}");
7223 }
7224
7225 #[test]
7226 fn an_ignored_signal_shield_blocks_only_what_the_guest_ignores() {
7227 use crate::syscalls::threads::kernel_sigset_bit as bit;
7228 let state = |blocked, ignored, caught| KernelSignalState {
7229 blocked,
7230 ignored,
7231 caught,
7232 ..Default::default()
7233 };
7234 let default_ignored =
7235 bit(libc::SIGCHLD) | bit(libc::SIGCONT) | bit(libc::SIGURG) | bit(libc::SIGWINCH);
7236 assert_eq!(
7239 ignored_signal_shield_mask(&state(bit(libc::SIGUSR2), 0, 0)),
7240 Some(bit(libc::SIGUSR2) | default_ignored)
7241 );
7242 assert_eq!(
7244 ignored_signal_shield_mask(&state(0, bit(libc::SIGUSR1), bit(libc::SIGCHLD))),
7245 Some(bit(libc::SIGUSR1) | (default_ignored & !bit(libc::SIGCHLD)))
7246 );
7247 assert_eq!(
7249 ignored_signal_shield_mask(&state(
7250 bit(libc::SIGCONT) | bit(libc::SIGURG),
7251 0,
7252 bit(libc::SIGCHLD) | bit(libc::SIGWINCH)
7253 )),
7254 None
7255 );
7256 for refused in [
7259 MaskChangeRefused::Unsupported,
7260 MaskChangeRefused::Gone,
7261 MaskChangeRefused::RestorePending {
7262 saved: 0,
7263 effective: 1,
7264 },
7265 MaskChangeRefused::Failed {
7266 errno: Errno::ESRCH,
7267 mask_unknown: false,
7268 },
7269 ] {
7270 assert!(shield_refusal_leaves_the_mask_known(&refused), "{refused}");
7271 }
7272 assert!(!shield_refusal_leaves_the_mask_known(
7273 &MaskChangeRefused::Failed {
7274 errno: Errno::ESRCH,
7275 mask_unknown: true,
7276 }
7277 ));
7278 }
7279
7280 #[test]
7284 fn pollfd_descriptors_are_the_non_negative_entries() {
7285 let entries = [
7286 libc::pollfd {
7287 fd: 7,
7288 events: libc::POLLIN,
7289 revents: 0,
7290 },
7291 libc::pollfd {
7292 fd: -1,
7293 events: libc::POLLIN,
7294 revents: 0,
7295 },
7296 libc::pollfd {
7297 fd: 0,
7298 events: libc::POLLOUT,
7299 revents: libc::POLLHUP,
7300 },
7301 ];
7302 let bytes: Vec<u8> = entries
7303 .iter()
7304 .flat_map(|entry| {
7305 let mut bytes = entry.fd.to_ne_bytes().to_vec();
7306 bytes.extend(entry.events.to_ne_bytes());
7307 bytes.extend(entry.revents.to_ne_bytes());
7308 bytes
7309 })
7310 .collect();
7311 assert_eq!(pollfd_descriptors(&bytes), vec![7, 0]);
7312 assert!(pollfd_descriptors(&[]).is_empty());
7313 }
7314
7315 #[test]
7324 fn millis_to_nanos_conversion() {
7325 assert_eq!(positive_millis_as_nanos(-1), None);
7327 assert_eq!(positive_millis_as_nanos(i32::MIN), None);
7328 assert_eq!(positive_millis_as_nanos(0), None);
7329
7330 assert_eq!(positive_millis_as_nanos(1), Some(1_000_000));
7332 assert_eq!(positive_millis_as_nanos(1_000), Some(1_000_000_000));
7333 assert_eq!(
7334 positive_millis_as_nanos(i32::MAX),
7335 Some(i32::MAX as u128 * 1_000_000)
7336 );
7337
7338 for millis in [1, 2, 7, 250, 1_000, 86_400_000] {
7341 assert_eq!(
7342 positive_millis_as_nanos(millis),
7343 Some(millis as u128 * 1_000_000),
7344 "1 ms must convert to 1_000_000 ns, not 1_000"
7345 );
7346 }
7347 }
7348
7349 #[test]
7350 fn connect_nonblocking_results() {
7351 let call = reverie::syscalls::Connect::new();
7352 assert!(call.syscall_would_have_blocked(Err(Errno::EINPROGRESS)));
7353 assert!(call.syscall_would_have_blocked(Err(Errno::EALREADY)));
7354 assert_eq!(
7355 call.normalize_nonblocking_result(Err(Errno::EISCONN), true),
7356 Ok(0)
7357 );
7358 assert_eq!(
7359 call.normalize_nonblocking_result(Err(Errno::EISCONN), false),
7360 Err(Errno::EISCONN)
7361 );
7362 }
7363
7364 #[test]
7369 fn only_msg_dontwait_keeps_a_socket_call_from_waiting() {
7370 use reverie::syscalls::*;
7371 let dontwait = libc::MSG_DONTWAIT;
7372 let others = libc::MSG_PEEK | libc::MSG_WAITALL | libc::MSG_NOSIGNAL | libc::MSG_TRUNC;
7373 let cases: [(&str, bool, bool); 6] = [
7374 (
7375 "recvfrom",
7376 Recvfrom::new()
7377 .with_flags(others | dontwait)
7378 .flags_forbid_waiting(),
7379 Recvfrom::new().with_flags(others).flags_forbid_waiting(),
7380 ),
7381 (
7382 "recvmsg",
7383 Recvmsg::new().with_flags(dontwait).flags_forbid_waiting(),
7384 Recvmsg::new().with_flags(others).flags_forbid_waiting(),
7385 ),
7386 (
7387 "recvmmsg",
7388 Recvmmsg::new()
7389 .with_flags((libc::MSG_WAITFORONE | dontwait) as u32)
7390 .flags_forbid_waiting(),
7391 Recvmmsg::new()
7392 .with_flags(libc::MSG_WAITFORONE as u32)
7393 .flags_forbid_waiting(),
7394 ),
7395 (
7396 "sendto",
7397 Sendto::new()
7398 .with_flags(dontwait as u32)
7399 .flags_forbid_waiting(),
7400 Sendto::new()
7401 .with_flags(others as u32)
7402 .flags_forbid_waiting(),
7403 ),
7404 (
7405 "sendmsg",
7406 Sendmsg::new().with_flags(dontwait).flags_forbid_waiting(),
7407 Sendmsg::new().with_flags(others).flags_forbid_waiting(),
7408 ),
7409 (
7410 "sendmmsg",
7411 Sendmmsg::new().with_flags(dontwait).flags_forbid_waiting(),
7412 Sendmmsg::new().with_flags(others).flags_forbid_waiting(),
7413 ),
7414 ];
7415 for (name, with_dontwait, without) in cases {
7416 assert!(with_dontwait, "{name} with MSG_DONTWAIT may wait");
7417 assert!(!without, "{name} without MSG_DONTWAIT never waits");
7418 }
7419 assert!(!Read::new().flags_forbid_waiting());
7420 assert!(
7421 Preadv2::new()
7422 .with_flags(libc::RWF_NOWAIT)
7423 .flags_forbid_waiting(),
7424 "preadv2 with RWF_NOWAIT may wait"
7425 );
7426 assert!(
7427 !Preadv2::new()
7428 .with_flags(libc::RWF_HIPRI)
7429 .flags_forbid_waiting(),
7430 "preadv2 without RWF_NOWAIT never waits"
7431 );
7432 assert!(!Accept4::new().flags_forbid_waiting());
7433 assert!(!Connect::new().flags_forbid_waiting());
7434 }
7435
7436 #[test]
7442 fn ioaction_decision_runs_a_nowait_call_once_on_a_blocking_descriptor() {
7443 use super::IOAction;
7444 use super::ioaction_for_fd_status;
7445 assert_eq!(
7447 ioaction_for_fd_status(3, false, true, true),
7448 IOAction::PassThru
7449 );
7450 assert_eq!(
7452 ioaction_for_fd_status(3, false, true, false),
7453 IOAction::NonblockizeRetry
7454 );
7455 assert_eq!(
7457 ioaction_for_fd_status(3, true, true, false),
7458 IOAction::PassThru
7459 );
7460 assert_eq!(
7461 ioaction_for_fd_status(3, false, false, true),
7462 IOAction::Blocking
7463 );
7464 }
7465
7466 #[test]
7474 fn msg_errqueue_marks_only_a_receive_as_an_error_queue_read() {
7475 use reverie::syscalls::*;
7476 let errqueue = libc::MSG_ERRQUEUE;
7477 assert!(Recvfrom::new().with_flags(errqueue).reads_error_queue());
7478 assert!(
7479 Recvmsg::new()
7480 .with_flags(errqueue | libc::MSG_PEEK)
7481 .reads_error_queue()
7482 );
7483 assert!(
7484 Recvmmsg::new()
7485 .with_flags(errqueue as u32)
7486 .reads_error_queue()
7487 );
7488 assert!(
7489 !Recvfrom::new()
7490 .with_flags(libc::MSG_DONTWAIT)
7491 .reads_error_queue()
7492 );
7493 assert!(
7494 !Recvmsg::new()
7495 .with_flags(libc::MSG_PEEK)
7496 .reads_error_queue()
7497 );
7498 assert!(
7499 !Recvmmsg::new()
7500 .with_flags(libc::MSG_WAITFORONE as u32)
7501 .reads_error_queue()
7502 );
7503 assert!(!Recvmsg::new().with_flags(errqueue).flags_forbid_waiting());
7504 assert!(
7505 !Sendto::new()
7506 .with_flags(errqueue as u32)
7507 .reads_error_queue()
7508 );
7509 assert!(!Sendmsg::new().with_flags(errqueue).reads_error_queue());
7510 assert!(!Sendmmsg::new().with_flags(errqueue).reads_error_queue());
7511 assert!(!Read::new().reads_error_queue());
7512 }
7513
7514 #[test]
7515 fn signal_interruption_errno_matches_linux_restart_policy() {
7516 assert_eq!(
7517 reverie::syscalls::Poll::new().signal_interrupt_errno(),
7518 Errno::EINTR
7519 );
7520 assert_eq!(
7521 reverie::syscalls::Ppoll::new().signal_interrupt_errno(),
7522 Errno::EINTR
7523 );
7524 assert_eq!(
7525 reverie::syscalls::EpollWait::new().signal_interrupt_errno(),
7526 Errno::EINTR
7527 );
7528 let sigtimedwait = reverie::syscalls::RtSigtimedwait::new();
7529 assert_eq!(sigtimedwait.signal_interrupt_errno(), Errno::EINTR);
7530 assert!(sigtimedwait.syscall_would_have_blocked(Err(Errno::EAGAIN)));
7531 assert_eq!(sigtimedwait.timeout_return_val(), Err(Errno::EAGAIN));
7532 assert_eq!(
7533 reverie::syscalls::Read::new().signal_interrupt_errno(),
7534 Errno::ERESTARTSYS
7535 );
7536 assert_eq!(
7539 reverie::syscalls::Writev::new().signal_interrupt_errno(),
7540 Errno::ERESTARTSYS
7541 );
7542 assert_eq!(
7543 reverie::syscalls::Futex::new().signal_interrupt_errno(),
7544 Errno::ERESTARTSYS
7545 );
7546 }
7547
7548 #[test]
7549 fn kernel_restart_errno_matches_linux_restart_policy() {
7550 assert_eq!(
7558 reverie::syscalls::Poll::new()
7559 .with_timeout(300)
7560 .kernel_restart_errno(),
7561 Errno::ERESTART_RESTARTBLOCK
7562 );
7563 assert_eq!(
7566 reverie::syscalls::Poll::new().kernel_restart_errno(),
7567 Errno::ERESTARTNOHAND
7568 );
7569 assert_eq!(
7570 reverie::syscalls::Poll::new()
7571 .with_timeout(-1)
7572 .kernel_restart_errno(),
7573 Errno::ERESTARTNOHAND
7574 );
7575 assert_eq!(
7576 reverie::syscalls::Ppoll::new().kernel_restart_errno(),
7577 Errno::ERESTARTNOHAND
7578 );
7579 assert_eq!(
7581 reverie::syscalls::EpollWait::new().kernel_restart_errno(),
7582 Errno::EINTR
7583 );
7584 assert_eq!(
7585 reverie::syscalls::RtSigtimedwait::new().kernel_restart_errno(),
7586 Errno::EINTR
7587 );
7588 assert_eq!(
7589 reverie::syscalls::Futex::new().kernel_restart_errno(),
7590 Errno::ERESTARTSYS
7591 );
7592 let timeout = reverie::syscalls::Addr::from_raw(0x1000).unwrap();
7595 assert_eq!(
7596 reverie::syscalls::Futex::new()
7597 .with_timeout(Some(timeout))
7598 .kernel_restart_errno(),
7599 Errno::ERESTART_RESTARTBLOCK
7600 );
7601 let bitset = reverie::syscalls::Futex::new()
7607 .with_futex_op(libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG)
7608 .with_val3(-1);
7609 assert_eq!(bitset.kernel_restart_errno(), Errno::ERESTARTSYS);
7610 assert!(!bitset.restart_keeps_deadline());
7611 assert_eq!(
7612 bitset.with_timeout(Some(timeout)).kernel_restart_errno(),
7613 Errno::ERESTART_RESTARTBLOCK
7614 );
7615 assert!(bitset.with_timeout(Some(timeout)).restart_keeps_deadline());
7616 for op in [
7619 libc::FUTEX_LOCK_PI,
7620 libc::FUTEX_LOCK_PI | libc::FUTEX_PRIVATE_FLAG,
7621 libc::FUTEX_LOCK_PI2 | libc::FUTEX_CLOCK_REALTIME,
7622 ] {
7623 let lock = reverie::syscalls::Futex::new().with_futex_op(op);
7624 assert_eq!(lock.kernel_restart_errno(), Errno::ERESTARTNOINTR);
7625 let timed = lock.with_timeout(Some(timeout));
7626 assert_eq!(timed.kernel_restart_errno(), Errno::ERESTARTNOINTR);
7627 assert!(!timed.restart_keeps_deadline());
7628 }
7629 }
7630
7631 #[test]
7632 fn a_held_signal_is_reported_unless_it_is_sigstop() {
7633 for signal in [libc::SIGUSR1, libc::SIGTERM, libc::SIGTSTP, libc::SIGCHLD] {
7637 assert_eq!(
7638 held_signal_restart_errno(Errno::ERESTART_RESTARTBLOCK, signal),
7639 Errno::ERESTARTNOHAND,
7640 "signal {signal}"
7641 );
7642 }
7643 assert_eq!(
7645 held_signal_restart_errno(Errno::ERESTART_RESTARTBLOCK, libc::SIGSTOP),
7646 Errno::ERESTART_RESTARTBLOCK
7647 );
7648 for errno in [
7650 Errno::ERESTARTNOHAND,
7651 Errno::ERESTARTSYS,
7652 Errno::ERESTARTNOINTR,
7653 Errno::EINTR,
7654 ] {
7655 for signal in [libc::SIGUSR1, libc::SIGSTOP] {
7656 assert_eq!(held_signal_restart_errno(errno, signal), errno);
7657 }
7658 }
7659 }
7660
7661 #[test]
7662 fn resumed_kernel_wait_result_maps_only_erestartsys() {
7663 assert_eq!(
7666 resumed_kernel_wait_result(Err(Errno::ERESTARTSYS)),
7667 Err(Errno::ERESTART_RESTARTBLOCK)
7668 );
7669 for result in [
7671 Ok(0),
7672 Err(Errno::ERESTART_RESTARTBLOCK),
7673 Err(Errno::EINTR),
7674 Err(Errno::ETIMEDOUT),
7675 Err(Errno::EAGAIN),
7676 ] {
7677 assert_eq!(resumed_kernel_wait_result(result), result);
7678 }
7679 }
7680
7681 #[test]
7682 fn a_restart_block_resumes_only_at_the_interrupted_call() {
7683 let timeout = reverie::syscalls::Addr::from_raw(0x1000).unwrap();
7684 let block = RestartBlock {
7685 rip: 0x4000_1234,
7686 deadline: Some(LogicalTime::from_nanos(300_000_000)),
7687 call: RestartCall::Futex(reverie::syscalls::Futex::new().with_timeout(Some(timeout))),
7688 };
7689 assert_eq!(block.resumed_at(0x4000_1234), Some(block));
7691 assert_eq!(block.resumed_at(0x4000_1236), None);
7693 assert_eq!(block.resumed_at(0x4000_1232), None);
7694 }
7695
7696 #[test]
7697 fn writev_signal_result_uses_disposition_and_progress() {
7698 let call = reverie::syscalls::Writev::new();
7699 for disposition in [
7700 WaitSignalDisposition::Interrupt,
7701 WaitSignalDisposition::Restart,
7702 ] {
7703 assert!(matches!(
7704 interrupted_write_result(&call, 0, Some(disposition)),
7705 Some(Err(Error::Errno(Errno::ERESTARTSYS)))
7706 ));
7707 assert!(matches!(
7708 interrupted_write_result(&call, 17, Some(disposition)),
7709 Some(Ok(17))
7710 ));
7711 }
7712 assert!(interrupted_write_result(&call, 0, None).is_none());
7713 assert!(interrupted_write_result(&call, 17, None).is_none());
7714 }
7715
7716 #[test]
7717 fn pipe_writev_requests_signal_disposition() {
7718 let dettid = DetTid::from_raw(42);
7719 let call = reverie::syscalls::Writev::new();
7720 let request = pipe_writev_resources(dettid, call, true);
7721
7722 assert_eq!(
7723 request.signal_interrupt_errno(),
7724 Some(Errno::ERESTARTSYS.into_raw())
7725 );
7726 assert!(request.decides_signal_interruption_itself);
7727 assert_eq!(request.resources.len(), 1);
7728 assert!(
7729 request.relies_on_guest_held_marks,
7730 "an escaped read end can make a blocked pipe writev progress"
7731 );
7732 let external = pipe_writev_resources(dettid, call, false);
7733 assert!(
7734 !external.polls_guest_state_only,
7735 "a pipe imported from outside may have a host reader without any guest export"
7736 );
7737 assert!(!external.relies_on_guest_held_marks);
7738 }
7739}
7740
7741#[cfg(test)]
7747pub(crate) mod kernel_signal_wait_failures {
7748 use std::collections::VecDeque;
7749 use std::sync::Arc;
7750 use std::sync::Mutex;
7751
7752 use reverie::GlobalRPC;
7753 use reverie::GlobalTool;
7754 use reverie::Pid;
7755 use reverie::Signal;
7756 use reverie::syscalls::LocalMemory;
7757
7758 use super::*;
7759 use crate::Config;
7760 use crate::GlobalState;
7761 use crate::ThreadState;
7762 use crate::syscalls::threads::KernelSignalState;
7763 use crate::syscalls::threads::kernel_sigset_bit;
7764 use crate::syscalls::threads::signal_state_read_seam;
7765 use crate::types::DetPid;
7766
7767 #[derive(Clone, Copy, Debug)]
7769 enum MaskOutcome {
7770 Apply,
7772 StoppedBefore,
7775 Fail(Errno),
7777 StoppedBy(i32),
7782 }
7783
7784 #[derive(Default)]
7787 struct FakeKernel {
7788 blocked: KernelSigset,
7790 pending: KernelSigset,
7792 shared: KernelSigset,
7796 caught: KernelSigset,
7798 ignored: KernelSigset,
7800 stops_for_pending: bool,
7805 taken: Vec<i32>,
7807 read_failures: VecDeque<Errno>,
7809 gone: bool,
7811 outcomes: VecDeque<MaskOutcome>,
7813 empty_read_probes: Option<usize>,
7817 read_ready_after: Option<usize>,
7820 read_probe_masks: Vec<KernelSigset>,
7822 write_probe_masks: Option<Vec<KernelSigset>>,
7825 pending_after_first_write: KernelSigset,
7826 requested: Vec<KernelSigset>,
7828 repost_after_take: usize,
7833 pending_repost: KernelSigset,
7836 held_slot: Option<i32>,
7840 lost: Vec<i32>,
7844 setter: VecDeque<Option<MaskChangeRefused>>,
7848 set_calls: Vec<KernelSigset>,
7850 private_record: Option<(KernelSigset, KernelSigset)>,
7854 }
7855
7856 impl FakeKernel {
7857 fn hold(&mut self, signal: i32) {
7859 if let Some(previous) = self.held_slot.replace(signal)
7860 && !(previous == signal && signal < KERNEL_SIGRTMIN)
7861 {
7862 self.lost.push(previous);
7863 }
7864 }
7865 }
7866
7867 type Kernel = Arc<Mutex<FakeKernel>>;
7868
7869 const ARENA_WORDS: usize = 2;
7871
7872 struct WaitStack {
7876 commit_fails: bool,
7877 arena: usize,
7878 }
7879
7880 struct WaitStackGuard;
7881
7882 impl Drop for WaitStackGuard {
7883 fn drop(&mut self) {}
7884 }
7885
7886 impl reverie::Stack for WaitStack {
7887 type StackGuard = WaitStackGuard;
7888
7889 fn size(&self) -> usize {
7890 panic!("a blocked wait must not query the scratch size")
7891 }
7892 fn capacity(&self) -> usize {
7893 panic!("a blocked wait must not query the scratch capacity")
7894 }
7895 fn push<'stack, T>(&mut self, value: T) -> Addr<'stack, T> {
7896 assert!(std::mem::size_of::<T>() <= ARENA_WORDS * std::mem::size_of::<u64>());
7897 unsafe { std::ptr::write(self.arena as *mut T, value) };
7900 Addr::from_raw(self.arena).unwrap()
7901 }
7902 fn reserve<'stack, T>(&mut self) -> AddrMut<'stack, T> {
7903 panic!("a blocked wait pushes its mask rather than reserving room")
7904 }
7905 fn commit(self) -> Result<Self::StackGuard, Errno> {
7906 if self.commit_fails {
7907 Err(Errno::EFAULT)
7908 } else {
7909 Ok(WaitStackGuard)
7910 }
7911 }
7912 }
7913
7914 struct WaitGuest {
7915 config: Config,
7916 thread: ThreadState<()>,
7917 pid: Pid,
7918 tid: Pid,
7919 commit_fails: bool,
7920 arena: Box<[u64; ARENA_WORDS]>,
7921 kernel: Kernel,
7922 proof_script: Option<Arc<Mutex<ProofScript>>>,
7923 }
7924
7925 impl WaitGuest {
7926 fn new(tid: Pid, mask: KernelSigset) -> (Self, Kernel) {
7929 let config = Config::default();
7930 let thread = ThreadState::new(DetPid::from_raw(1), &config, ());
7931 let kernel = Arc::new(Mutex::new(FakeKernel {
7932 blocked: mask,
7933 ..FakeKernel::default()
7934 }));
7935 let guest = Self {
7936 config,
7937 thread,
7938 pid: Pid::from_raw(std::process::id() as i32),
7939 tid,
7940 commit_fails: false,
7941 arena: Box::new([u64::MAX; ARENA_WORDS]),
7942 kernel: kernel.clone(),
7943 proof_script: None,
7944 };
7945 (guest, kernel)
7946 }
7947
7948 fn live(mask: KernelSigset) -> (Self, Kernel) {
7950 Self::new(Pid::from_raw(unsafe { libc::gettid() }), mask)
7952 }
7953 }
7954
7955 #[test]
7965 fn msg_dontwait_runs_a_call_once_on_a_socket_the_guest_left_blocking() {
7966 use std::os::fd::AsRawFd;
7967
7968 use reverie::syscalls::Recvfrom;
7969 use reverie::syscalls::Recvmmsg;
7970 use reverie::syscalls::Recvmsg;
7971 use reverie::syscalls::Sendmmsg;
7972 use reverie::syscalls::Sendmsg;
7973 use reverie::syscalls::Sendto;
7974
7975 let (mut guest, _) = WaitGuest::live(guest_mask());
7976 let (socket, _peer) = std::os::unix::net::UnixDatagram::pair().unwrap();
7977 let fd = socket.as_raw_fd();
7978 guest
7979 .thread
7980 .add_fd(fd, nix::fcntl::OFlag::O_RDWR, FdType::Socket, None)
7981 .unwrap();
7982 let dontwait = libc::MSG_DONTWAIT;
7983 let waitforone = libc::MSG_WAITFORONE;
7984 let once = Ok(IOAction::PassThru);
7985 let retry = Ok(IOAction::NonblockizeRetry);
7986 let decisions = |guest: &mut WaitGuest| {
7987 [
7988 (
7989 "recvfrom",
7990 ioaction_based_on_fd_status(
7991 guest,
7992 Recvfrom::new().with_fd(fd).with_flags(dontwait),
7993 ),
7994 ioaction_based_on_fd_status(guest, Recvfrom::new().with_fd(fd)),
7995 ),
7996 (
7997 "recvmsg",
7998 ioaction_based_on_fd_status(
7999 guest,
8000 Recvmsg::new().with_sockfd(fd).with_flags(dontwait),
8001 ),
8002 ioaction_based_on_fd_status(guest, Recvmsg::new().with_sockfd(fd)),
8003 ),
8004 (
8005 "recvmmsg",
8006 ioaction_based_on_fd_status(
8007 guest,
8008 Recvmmsg::new()
8009 .with_fd(fd)
8010 .with_flags((waitforone | dontwait) as u32),
8011 ),
8012 ioaction_based_on_fd_status(
8013 guest,
8014 Recvmmsg::new().with_fd(fd).with_flags(waitforone as u32),
8015 ),
8016 ),
8017 (
8018 "sendto",
8019 ioaction_based_on_fd_status(
8020 guest,
8021 Sendto::new().with_fd(fd).with_flags(dontwait as u32),
8022 ),
8023 ioaction_based_on_fd_status(guest, Sendto::new().with_fd(fd)),
8024 ),
8025 (
8026 "sendmsg",
8027 ioaction_based_on_fd_status(
8028 guest,
8029 Sendmsg::new().with_fd(fd).with_flags(dontwait),
8030 ),
8031 ioaction_based_on_fd_status(guest, Sendmsg::new().with_fd(fd)),
8032 ),
8033 (
8034 "sendmmsg",
8035 ioaction_based_on_fd_status(
8036 guest,
8037 Sendmmsg::new().with_sockfd(fd).with_flags(dontwait),
8038 ),
8039 ioaction_based_on_fd_status(guest, Sendmmsg::new().with_sockfd(fd)),
8040 ),
8041 ]
8042 };
8043 for (name, with_dontwait, without) in decisions(&mut guest) {
8046 let blocking = Ok(IOAction::Blocking);
8047 assert_eq!(
8048 with_dontwait, blocking,
8049 "{name} with MSG_DONTWAIT, blocking"
8050 );
8051 assert_eq!(without, blocking, "{name} without MSG_DONTWAIT, blocking");
8052 }
8053 guest
8054 .thread
8055 .with_detfd(fd, |d| d.set_physically_nonblocking())
8056 .unwrap();
8057 for (name, with_dontwait, without) in decisions(&mut guest) {
8058 assert_eq!(with_dontwait, once, "{name} with MSG_DONTWAIT");
8059 assert_eq!(without, retry, "{name} without MSG_DONTWAIT");
8060 }
8061 guest
8062 .thread
8063 .with_detfd(fd, |d| d.set_nonblocking(true))
8064 .unwrap();
8065 for (name, with_dontwait, without) in decisions(&mut guest) {
8066 assert_eq!(with_dontwait, once, "{name} with MSG_DONTWAIT, O_NONBLOCK");
8067 assert_eq!(without, once, "{name} without MSG_DONTWAIT, O_NONBLOCK");
8068 }
8069 }
8070
8071 #[derive(Clone, Copy, Debug)]
8072 enum ProofAction {
8073 None,
8074 Dup { from: i32, to: i32 },
8075 Operand { address: usize, fd: i32 },
8076 Mark { fd: i32, held: bool },
8077 }
8078
8079 #[derive(Default)]
8080 struct ProofScript {
8081 requests: Vec<Resources>,
8082 actions: VecDeque<ProofAction>,
8083 probes: usize,
8084 observe_retry_masks: bool,
8085 published_mask: Option<KernelSigset>,
8086 publications: Vec<Option<KernelSigset>>,
8087 retry_boundaries: Vec<(&'static str, KernelSigset, Option<KernelSigset>)>,
8088 route_child_on_retry: bool,
8089 }
8090
8091 impl WaitGuest {
8092 async fn proof_action(&self, action: ProofAction) {
8093 match action {
8094 ProofAction::None => {}
8095 ProofAction::Dup { from, to } => {
8096 let (mut sibling, _) = Self::new(Pid::from_raw(2), 0);
8099 sibling.config = self.config.clone();
8100 sibling.thread.dettid = DetTid::from_raw(2);
8101 sibling.thread.detpid = self.thread.detpid;
8102 sibling.thread.file_metadata = self.thread.file_metadata.clone();
8103 sibling.proof_script = Some(Arc::new(Mutex::new(ProofScript::default())));
8104 let tool = Detcore {
8105 detpid: self.thread.detpid.unwrap(),
8106 cfg: self.config.clone(),
8107 record_or_replay: crate::record_or_replay::NoopTool,
8108 };
8109 assert_eq!(
8110 tool.handle_dup2(
8111 &mut sibling,
8112 syscalls::Dup2::new().with_oldfd(from).with_newfd(to),
8113 )
8114 .await
8115 .unwrap(),
8116 i64::from(to)
8117 );
8118 }
8119 ProofAction::Operand { address, fd } => {
8120 LocalMemory::new()
8123 .write_value(AddrMut::<i32>::from_raw(address).unwrap(), &fd)
8124 .unwrap();
8125 }
8126 ProofAction::Mark { fd, held } => {
8127 self.thread
8128 .with_detfd(fd, |entry| {
8129 if held {
8130 entry.set_guest_held();
8131 } else {
8132 entry.clear_guest_held();
8133 }
8134 })
8135 .unwrap();
8136 }
8137 }
8138 }
8139 }
8140
8141 pub(crate) fn fd_test_guest() -> impl Guest<Detcore> {
8143 WaitGuest::live(0).0
8144 }
8145
8146 #[test]
8157 fn a_signal_ends_an_inotify_read_made_nonblocking_meanwhile_with_eagain() {
8158 use std::os::fd::AsRawFd;
8159 use std::os::fd::FromRawFd;
8160
8161 let (mut guest, _) = WaitGuest::live(guest_mask());
8162 let inotify = unsafe {
8164 std::fs::File::from_raw_fd(libc::inotify_init1(libc::IN_NONBLOCK | libc::IN_CLOEXEC))
8165 };
8166 let (pipe, _writer) = std::os::unix::net::UnixStream::pair().unwrap();
8167 let (inotify, pipe) = (inotify.as_raw_fd(), pipe.as_raw_fd());
8168 guest
8169 .thread
8170 .add_fd(inotify, nix::fcntl::OFlag::O_RDONLY, FdType::Inotify, None)
8171 .unwrap();
8172 guest
8173 .thread
8174 .add_fd(pipe, nix::fcntl::OFlag::O_RDONLY, FdType::Pipe, None)
8175 .unwrap();
8176 for fd in [inotify, pipe] {
8177 guest
8178 .thread
8179 .with_detfd(fd, |d| d.set_physically_nonblocking())
8180 .unwrap();
8181 }
8182 let read = |fd| syscalls::Read::new().with_fd(fd);
8183 let inotify_read = inotify_description_of(&guest, read(inotify));
8184 let pipe_read = inotify_description_of(&guest, read(pipe));
8185 assert!(inotify_read.is_some());
8186 assert!(pipe_read.is_none(), "only an inotify read is bound");
8187 assert_eq!(
8188 interrupted_wait_errno(inotify_read.as_ref(), read(inotify)),
8189 Errno::ERESTARTSYS
8190 );
8191 assert_eq!(
8192 interrupted_wait_errno(pipe_read.as_ref(), read(pipe)),
8193 Errno::ERESTARTSYS
8194 );
8195 for fd in [inotify, pipe] {
8196 guest
8197 .thread
8198 .with_detfd(fd, |d| d.set_logical_nonblocking(true))
8199 .unwrap();
8200 }
8201 assert_eq!(
8202 interrupted_wait_errno(inotify_read.as_ref(), read(inotify)),
8203 Errno::EAGAIN
8204 );
8205 assert_eq!(
8206 interrupted_wait_errno(
8207 inotify_read.as_ref(),
8208 syscalls::Readv::new().with_fd(inotify)
8209 ),
8210 Errno::EAGAIN
8211 );
8212 assert_eq!(
8213 interrupted_wait_errno(pipe_read.as_ref(), read(pipe)),
8214 Errno::ERESTARTSYS
8215 );
8216 guest
8220 .thread
8221 .dup_fd(inotify, pipe, nix::fcntl::OFlag::empty())
8222 .unwrap();
8223 assert_eq!(
8224 interrupted_wait_errno(pipe_read.as_ref(), read(pipe)),
8225 Errno::ERESTARTSYS
8226 );
8227 assert!(inotify_description_of(&guest, read(pipe)).is_some());
8228 }
8229
8230 #[test]
8237 fn a_part_carries_the_signal_its_turn_found_to_the_next() {
8238 let pending = TurnSignals {
8239 named: 0,
8240 pending: kernel_sigset_bit(libc::SIGUSR1),
8241 };
8242 let named = TurnSignals {
8243 named: kernel_sigset_bit(libc::SIGUSR1),
8244 pending: 0,
8245 };
8246 let wake = ResumeStatus::Signaled(None);
8247 assert!(matches!(
8248 carried_signal(Some(pending), &ResumeStatus::Normal, false),
8249 Some(CarriedSignal::Decided)
8250 ));
8251 assert!(matches!(
8252 carried_signal(Some(named), &wake, false),
8253 Some(CarriedSignal::Decided)
8254 ));
8255 assert!(carried_signal(Some(TurnSignals::default()), &wake, false).is_none());
8256 assert!(matches!(
8257 carried_signal(None, &wake, false),
8258 Some(CarriedSignal::Wake(ResumeStatus::Signaled(None)))
8259 ));
8260 assert!(carried_signal(None, &wake, true).is_none());
8261 assert!(carried_signal(None, &ResumeStatus::Normal, false).is_none());
8262
8263 let (mut guest, _) = WaitGuest::live(guest_mask());
8264 let ends = |guest: &mut WaitGuest, carried, decides_itself, gated| {
8265 guest.thread.call_parts = Some(CallParts {
8266 progress: true,
8267 carried,
8268 });
8269 let ends =
8270 futures::executor::block_on(carried_signal_ends(guest, decides_itself, gated))
8271 .unwrap();
8272 let left = guest.thread.call_parts.as_ref().unwrap().carried.is_some();
8273 (ends, left)
8274 };
8275 assert_eq!(
8276 ends(&mut guest, Some(CarriedSignal::Decided), false, false),
8277 (true, false)
8278 );
8279 assert_eq!(
8280 ends(
8281 &mut guest,
8282 Some(CarriedSignal::Wake(wake.clone())),
8283 false,
8284 false
8285 ),
8286 (true, false)
8287 );
8288 assert_eq!(
8289 ends(&mut guest, Some(CarriedSignal::Wake(wake)), false, true),
8290 (false, false)
8291 );
8292 assert_eq!(ends(&mut guest, None, false, false), (false, false));
8293 }
8294
8295 #[test]
8296 fn inherited_input_exempts_only_authenticated_reads_from_the_internal_guard() {
8297 use std::os::fd::AsRawFd;
8298 let (mut guest, _) = WaitGuest::live(guest_mask());
8299 let slot = tempfile::tempfile().unwrap();
8300 let fd = slot.as_raw_fd();
8301 let input = reverie::InheritedInput::pipe(fd).unwrap();
8302 let identity = input.identity().clone();
8303 let (reader, _writer) = input.into_reader_and_writer();
8304 assert_eq!(unsafe { libc::dup2(reader.as_raw_fd(), fd) }, fd);
8305 guest
8306 .thread
8307 .add_fd(fd, nix::fcntl::OFlag::O_RDONLY, FdType::Pipe, None)
8308 .unwrap();
8309 let calls = [
8310 Syscall::from(syscalls::Read::new().with_fd(fd)),
8311 Syscall::from(syscalls::Readv::new().with_fd(fd)),
8312 Syscall::from(syscalls::Write::new().with_fd(fd)),
8313 Syscall::from(syscalls::Writev::new().with_fd(fd)),
8314 ];
8315 for call in calls {
8316 assert!(syscall_targets_internal_fd(&mut guest, call));
8317 }
8318 guest
8319 .thread
8320 .file_metadata
8321 .lock()
8322 .unwrap()
8323 .apply_exec_fd_state(
8324 guest.thread.dettid,
8325 crate::tool_local::ExecFdState {
8326 initial_input: Some(identity),
8327 ..Default::default()
8328 },
8329 true,
8330 )
8331 .unwrap();
8332 for (index, call) in calls.into_iter().enumerate() {
8333 assert_eq!(syscall_targets_internal_fd(&mut guest, call), index >= 2);
8334 }
8335 assert!(
8336 !guest
8337 .thread
8338 .with_detfd(fd, |d| d.physically_nonblocking())
8339 .unwrap()
8340 );
8341 assert_eq!(
8342 unsafe { libc::fcntl(fd, libc::F_GETFL) } & libc::O_NONBLOCK,
8343 0
8344 );
8345 let (internal, _writer) = nix::unistd::pipe().unwrap();
8346 guest
8347 .thread
8348 .add_fd(
8349 internal.as_raw_fd(),
8350 nix::fcntl::OFlag::O_RDONLY,
8351 FdType::Pipe,
8352 None,
8353 )
8354 .unwrap();
8355 assert!(syscall_targets_internal_fd(
8356 &mut guest,
8357 syscalls::Read::new().with_fd(internal.as_raw_fd()).into()
8358 ));
8359 let (socket, _peer) = std::os::unix::net::UnixStream::pair().unwrap();
8361 guest
8362 .thread
8363 .add_fd(
8364 socket.as_raw_fd(),
8365 nix::fcntl::OFlag::O_RDWR,
8366 FdType::Socket,
8367 None,
8368 )
8369 .unwrap();
8370 guest
8371 .thread
8372 .with_detfd(socket.as_raw_fd(), |d| d.set_socketpair_endpoint())
8373 .unwrap();
8374 assert!(syscall_targets_internal_fd(
8375 &mut guest,
8376 syscalls::Readv::new().with_fd(socket.as_raw_fd()).into()
8377 ));
8378 }
8379
8380 #[test]
8381 fn a_socketpair_poll_uses_revocable_provenance_without_changing_internal_routing() {
8382 use std::os::fd::AsRawFd;
8383
8384 use nix::sys::socket::AddressFamily;
8385 use nix::sys::socket::SockFlag;
8386 use nix::sys::socket::SockType;
8387 use nix::sys::socket::socketpair;
8388
8389 let (mut guest, _) = WaitGuest::live(guest_mask());
8390 let pairs = [SockType::Datagram, SockType::Stream, SockType::SeqPacket].map(|kind| {
8391 let (socket, peer) =
8392 socketpair(AddressFamily::Unix, kind, None, SockFlag::empty()).unwrap();
8393 (kind, socket, peer)
8394 });
8395 for (kind, socket, _peer) in &pairs {
8396 let fd = socket.as_raw_fd();
8397 guest
8398 .thread
8399 .add_fd(fd, nix::fcntl::OFlag::O_RDWR, FdType::Socket, None)
8400 .unwrap();
8401 let alias = guest
8402 .thread
8403 .with_detfd(fd, |d| {
8404 d.set_socketpair_endpoint();
8405 d.clone()
8406 })
8407 .unwrap();
8408 let connection = *kind != SockType::Datagram;
8412 if connection {
8413 alias.set_guest_held();
8414 }
8415 let calls = [
8416 syscalls::Read::new().with_fd(fd).into(),
8417 syscalls::Readv::new().with_fd(fd).into(),
8418 syscalls::Recvfrom::new().with_fd(fd).into(),
8419 syscalls::Recvmsg::new().with_sockfd(fd).into(),
8420 ];
8421 for call in calls {
8422 let internal = syscall_targets_internal_fd(&mut guest, call);
8423 assert!(internal, "{kind:?}: live record/replay routing is retained");
8424 let readiness = single_fd_poll_readiness(&mut guest, call, internal);
8425 assert_eq!(
8426 readiness,
8427 (connection, connection),
8428 "{kind:?}: readiness needs revocable provenance"
8429 );
8430 let mut request = Resources::new(DetTid::from_raw(5));
8431 request.insert(ResourceID::InternalIOPolling, Permission::W);
8432 request.poll_attempt = 1;
8433 (
8434 request.polls_guest_state_only,
8435 request.relies_on_guest_held_marks,
8436 ) = readiness;
8437 crate::scheduler::assert_poll_export_revocation_for_test(request, connection);
8438 }
8439 alias.set_guest_peer();
8442 for call in calls {
8443 let internal = syscall_targets_internal_fd(&mut guest, call);
8444 assert_eq!(
8445 single_fd_poll_readiness(&mut guest, call, internal),
8446 (connection, connection),
8447 "cached guest-peer routing is not socketpair readiness proof"
8448 );
8449 }
8450 alias.clear_guest_held();
8451 for call in calls {
8452 let internal = syscall_targets_internal_fd(&mut guest, call);
8453 assert!(internal, "revocation must not change live routing");
8454 assert_eq!(
8455 single_fd_poll_readiness(&mut guest, call, internal),
8456 (false, false),
8457 "withdrawing a mark through an alias withdraws readiness"
8458 );
8459 }
8460 }
8461
8462 let (pipe, _writer) = nix::unistd::pipe().unwrap();
8464 guest
8465 .thread
8466 .add_fd(
8467 pipe.as_raw_fd(),
8468 nix::fcntl::OFlag::O_RDONLY,
8469 FdType::Pipe,
8470 None,
8471 )
8472 .unwrap();
8473 let (socket, _peer) = std::os::unix::net::UnixStream::pair().unwrap();
8474 guest
8475 .thread
8476 .add_fd(
8477 socket.as_raw_fd(),
8478 nix::fcntl::OFlag::O_RDWR,
8479 FdType::Socket,
8480 None,
8481 )
8482 .unwrap();
8483 guest
8484 .thread
8485 .with_detfd(socket.as_raw_fd(), |d| d.set_guest_peer())
8486 .unwrap();
8487 for fd in [pipe.as_raw_fd(), socket.as_raw_fd()] {
8488 let call = syscalls::Read::new().with_fd(fd).into();
8489 let internal = syscall_targets_internal_fd(&mut guest, call);
8490 assert!(internal);
8491 assert_eq!(
8492 single_fd_poll_readiness(&mut guest, call, internal),
8493 (true, false)
8494 );
8495 }
8496 }
8497
8498 fn scripted_proc(kernel: &Kernel) -> signal_state_read_seam::Installed {
8500 let kernel = kernel.clone();
8501 signal_state_read_seam::install(move |_, _| {
8502 let mut kernel = kernel.lock().unwrap();
8503 let read = match kernel
8504 .gone
8505 .then_some(Errno::ESRCH)
8506 .or_else(|| kernel.read_failures.pop_front())
8507 {
8508 Some(errno) => Err(errno),
8509 None => Ok(KernelSignalState {
8510 pending: kernel.pending,
8511 thread_pending: kernel.pending & !kernel.shared,
8512 shared_pending: kernel.pending & kernel.shared,
8513 blocked: kernel.blocked,
8514 caught: kernel.caught,
8515 ignored: kernel.ignored,
8516 }),
8517 };
8518 kernel.pending |= std::mem::take(&mut kernel.pending_repost);
8519 Some(read)
8520 })
8521 }
8522
8523 #[reverie::tool]
8524 impl GlobalRPC<GlobalState> for WaitGuest {
8525 async fn send_rpc(
8526 &self,
8527 message: <GlobalState as GlobalTool>::Request,
8528 ) -> <GlobalState as GlobalTool>::Response {
8529 if let Some(script) = &self.proof_script {
8530 use crate::tool_global::GlobalRequest;
8531 use crate::tool_global::GlobalResponse;
8532 match &message.2 {
8533 GlobalRequest::RequestResources(resources, _) => {
8534 let (action, signal) = {
8535 let mut script = script.lock().unwrap();
8536 assert!(script.requests.len() < 4, "unbounded retry fixture");
8537 script.requests.push(resources.clone());
8538 let mut signal = None;
8539 if script.observe_retry_masks {
8540 let mut kernel = self.kernel.lock().unwrap();
8541 let published = script.published_mask;
8542 script.retry_boundaries.push((
8543 "request",
8544 kernel.blocked,
8545 published,
8546 ));
8547 if script.route_child_on_retry
8551 && script.requests.len() == 2
8552 && kernel.blocked & kernel_sigset_bit(libc::SIGCHLD) == 0
8553 {
8554 kernel.pending |= kernel_sigset_bit(libc::SIGCHLD);
8555 signal = Some(crate::types::SigWrapper::from(Signal::SIGCHLD));
8556 }
8557 }
8558 (
8559 script.actions.pop_front().unwrap_or(ProofAction::None),
8560 signal,
8561 )
8562 };
8563 self.proof_action(action).await;
8564 return (
8565 None,
8566 GlobalResponse::RequestResources(
8567 signal.map_or(ResumeStatus::Normal, |signal| {
8568 ResumeStatus::Signaled(Some(vec![signal]))
8569 }),
8570 ),
8571 );
8572 }
8573 GlobalRequest::ReleasePort(_) => {
8574 return (None, GlobalResponse::ReleasePort(None));
8575 }
8576 GlobalRequest::HostTimedAndPrivateSigchld(_) => {
8577 return (None, GlobalResponse::HostTimedAndPrivateSigchld(0, 0));
8578 }
8579 GlobalRequest::SetSavedGuestSigmask(mask) => {
8580 let mut script = script.lock().unwrap();
8581 script.published_mask = *mask;
8582 script.publications.push(*mask);
8583 return (None, GlobalResponse::SetSavedGuestSigmask(()));
8584 }
8585 GlobalRequest::TraceSchedEvent(..) => {
8586 let mut script = script.lock().unwrap();
8587 assert!(script.observe_retry_masks);
8588 let blocked = self.kernel.lock().unwrap().blocked;
8589 let published = script.published_mask;
8590 script.retry_boundaries.push(("trace", blocked, published));
8591 return (
8592 None,
8593 GlobalResponse::TraceSchedEvent(
8594 serde_json::from_value(serde_json::json!({
8595 "print_stack_strace": null,
8596 "timeslice": null,
8597 "schedule_released": false,
8598 "schedule_over": false,
8599 }))
8600 .unwrap(),
8601 ),
8602 );
8603 }
8604 GlobalRequest::DiscardedSignal(_) => {
8605 return (
8606 None,
8607 GlobalResponse::DiscardedSignal(
8608 crate::scheduler::discarded_at_send::DiscardedSignalAnswer::Bits {
8609 thread: 0,
8610 process: 0,
8611 },
8612 ),
8613 );
8614 }
8615 GlobalRequest::PendingRealTimeExpirations(_) => {
8616 return (None, GlobalResponse::PendingRealTimeExpirations(0));
8617 }
8618 _ => panic!("unexpected proof fixture RPC: {:?}", message.2),
8619 }
8620 }
8621 match (&message.2, self.kernel.lock().unwrap().private_record) {
8622 (
8623 crate::tool_global::GlobalRequest::HostTimedAndPrivateSigchld(_),
8624 Some((host_timed, private)),
8625 ) => (
8626 None,
8627 crate::tool_global::GlobalResponse::HostTimedAndPrivateSigchld(
8628 host_timed, private,
8629 ),
8630 ),
8631 _ => panic!("these waits must not send an RPC: {:?}", message.2),
8632 }
8633 }
8634 fn config(&self) -> &Config {
8635 &self.config
8636 }
8637 }
8638
8639 #[reverie::tool]
8640 impl Guest<Detcore> for WaitGuest {
8641 type Memory = LocalMemory;
8642 type Stack = WaitStack;
8643
8644 fn tid(&self) -> Pid {
8645 self.tid
8646 }
8647 fn pid(&self) -> Pid {
8648 self.pid
8649 }
8650 fn ppid(&self) -> Option<Pid> {
8651 None
8652 }
8653 fn memory(&self) -> Self::Memory {
8654 LocalMemory::new()
8655 }
8656 fn thread_state_mut(&mut self) -> &mut ThreadState<()> {
8657 &mut self.thread
8658 }
8659 fn thread_state(&self) -> &ThreadState<()> {
8660 &self.thread
8661 }
8662 async fn regs(&mut self) -> libc::user_regs_struct {
8663 if self
8664 .proof_script
8665 .as_ref()
8666 .is_some_and(|script| script.lock().unwrap().observe_retry_masks)
8667 {
8668 let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
8669 regs.rip = self.arena.as_ptr() as u64;
8670 return regs;
8671 }
8672 panic!("a blocked wait must not read registers")
8673 }
8674 async fn stack(&mut self) -> Self::Stack {
8675 WaitStack {
8676 commit_fails: self.commit_fails,
8677 arena: self.arena.as_mut_ptr() as usize,
8678 }
8679 }
8680 async fn daemonize(&mut self) {
8681 panic!("a blocked wait must not daemonize")
8682 }
8683 async fn inject<S: SyscallInfo>(&mut self, syscall: S) -> Result<i64, Errno> {
8684 let (number, args) = syscall.into_parts();
8685 if let Some(script) = &self.proof_script {
8686 match Syscall::from_raw(number, args) {
8687 Syscall::Dup2(call) => {
8688 let result = unsafe { libc::dup2(call.oldfd(), call.newfd()) };
8691 assert!(result >= 0, "owned fixture descriptor duplication failed");
8692 return Ok(i64::from(result));
8693 }
8694 Syscall::Readv(_) | Syscall::Poll(_) | Syscall::Ppoll(_) => {
8695 let mut script = script.lock().unwrap();
8696 script.probes += 1;
8697 return if script.probes == 4 {
8698 Ok(1)
8699 } else if matches!(Syscall::from_raw(number, args), Syscall::Readv(_)) {
8700 Err(Errno::EAGAIN)
8701 } else {
8702 Ok(0)
8703 };
8704 }
8705 _ => {}
8706 }
8707 }
8708 if matches!(Syscall::from_raw(number, args), Syscall::Read(_)) {
8709 let mut kernel = self.kernel.lock().unwrap();
8710 assert!(kernel.empty_read_probes.is_some(), "unexpected read probe");
8711 let deliverable = kernel.pending & !kernel.blocked;
8712 if kernel.stops_for_pending && deliverable != 0 {
8713 let signal = deliverable.trailing_zeros() as i32 + 1;
8714 kernel.pending &= !kernel_sigset_bit(signal);
8715 kernel.taken.push(signal);
8716 kernel.hold(signal);
8717 return Err(Errno::ERESTARTSYS);
8718 }
8719 *kernel.empty_read_probes.as_mut().unwrap() += 1;
8720 let blocked = kernel.blocked;
8721 kernel.read_probe_masks.push(blocked);
8722 if kernel
8723 .read_ready_after
8724 .is_some_and(|ready| kernel.empty_read_probes.unwrap() >= ready)
8725 {
8726 return Ok(1);
8727 }
8728 return Err(Errno::EAGAIN);
8729 }
8730 if matches!(Syscall::from_raw(number, args), Syscall::Write(_)) {
8731 let mut kernel = self.kernel.lock().unwrap();
8732 let blocked = kernel.blocked;
8733 let probes = kernel
8734 .write_probe_masks
8735 .as_mut()
8736 .expect("unexpected write probe");
8737 probes.push(blocked);
8738 let attempt = probes.len();
8739 assert!(attempt <= 3, "unbounded scalar-write fixture");
8740 if attempt == 1 {
8741 let pending = std::mem::take(&mut kernel.pending_after_first_write);
8742 kernel.pending |= pending;
8743 }
8744 return if attempt == 3 {
8745 Ok(1)
8746 } else {
8747 Err(Errno::EAGAIN)
8748 };
8749 }
8750 let Syscall::RtSigprocmask(call) = Syscall::from_raw(number, args) else {
8751 panic!("a blocked wait injected {number}, not rt_sigprocmask")
8752 };
8753 assert_eq!(call.how(), libc::SIG_SETMASK);
8754 assert!(call.oldset().is_none());
8755 let set = call.set().expect("rt_sigprocmask without a mask");
8756 let mask: KernelSigset = LocalMemory::new().read_value(set.cast::<KernelSigset>())?;
8757 let mut kernel = self.kernel.lock().unwrap();
8758 kernel.requested.push(mask);
8759 let deliverable = kernel.pending & !kernel.blocked;
8760 if kernel.stops_for_pending && deliverable != 0 {
8761 let signal = deliverable.trailing_zeros() as i32 + 1;
8762 kernel.pending &= !kernel_sigset_bit(signal);
8763 kernel.taken.push(signal);
8764 kernel.hold(signal);
8765 if kernel.repost_after_take > 0 {
8766 kernel.repost_after_take -= 1;
8767 kernel.pending_repost |= kernel_sigset_bit(signal);
8768 }
8769 return Err(Errno::ERESTARTSYS);
8770 }
8771 match kernel.outcomes.pop_front().unwrap_or(MaskOutcome::Apply) {
8772 MaskOutcome::Apply => {
8773 kernel.blocked = mask;
8774 Ok(0)
8775 }
8776 MaskOutcome::StoppedBefore => Err(Errno::ERESTARTNOINTR),
8777 MaskOutcome::StoppedBy(signal) => {
8778 kernel.hold(signal);
8779 Err(Errno::ERESTARTNOINTR)
8780 }
8781 MaskOutcome::Fail(errno) => Err(errno),
8782 }
8783 }
8784 async fn set_blocked_signals(&mut self, mask: u64) -> Result<u64, MaskChangeRefused> {
8785 let mut kernel = self.kernel.lock().unwrap();
8786 kernel.set_calls.push(mask);
8787 match kernel.setter.pop_front() {
8788 Some(None) => Ok(std::mem::replace(&mut kernel.blocked, mask)),
8789 Some(Some(refused)) => {
8790 if matches!(refused, MaskChangeRefused::Gone) {
8791 kernel.gone = true;
8792 }
8793 Err(refused)
8794 }
8795 None => Err(MaskChangeRefused::Unsupported),
8796 }
8797 }
8798
8799 async fn tail_inject<S: SyscallInfo>(&mut self, _: S) -> reverie::Never {
8800 panic!("a blocked wait must not retire the guest")
8801 }
8802 fn set_timer(&mut self, _: reverie::TimerSchedule) -> Result<(), Error> {
8803 panic!("a blocked wait must not set a timer")
8804 }
8805 fn set_timer_precise(&mut self, _: reverie::TimerSchedule) -> Result<(), Error> {
8806 panic!("a blocked wait must not set a timer")
8807 }
8808 fn read_clock(&mut self) -> Result<u64, Error> {
8809 panic!("a blocked wait must not read a clock")
8810 }
8811 }
8812
8813 async fn proof_requests(case: &str, kernel_wait: bool, masked_ppoll: bool) -> Vec<Resources> {
8817 proof_requests_from(case, kernel_wait, masked_ppoll, false).await
8818 }
8819
8820 async fn proof_requests_from(
8821 case: &str,
8822 kernel_wait: bool,
8823 masked_ppoll: bool,
8824 generic_fd_entry: bool,
8825 ) -> Vec<Resources> {
8826 use std::os::fd::AsRawFd;
8827
8828 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(3), 0);
8829 guest.config.sequentialize_threads = true;
8830 guest.config.backend.kernel_signal_targets_are_predictable = false;
8831 guest.thread.dettid = DetTid::from_raw(3);
8832 guest.thread.detpid = Some(DetPid::from_raw(3));
8833 let _proc = scripted_proc(&kernel);
8834 let (internal, _internal_writer) =
8835 nix::unistd::pipe2(nix::fcntl::OFlag::O_NONBLOCK).unwrap();
8836 let (external, _external_writer) =
8837 nix::unistd::pipe2(nix::fcntl::OFlag::O_NONBLOCK).unwrap();
8838 let saved = nix::unistd::dup(&internal).unwrap();
8839 let fd = internal.as_raw_fd();
8840 let external_fd = external.as_raw_fd();
8841 for raw in [fd, external_fd] {
8842 guest
8843 .thread
8844 .add_fd(raw, nix::fcntl::OFlag::O_RDONLY, FdType::Pipe, None)
8845 .unwrap();
8846 guest
8847 .thread
8848 .with_detfd(raw, |entry| entry.set_physically_nonblocking())
8849 .unwrap();
8850 }
8851 let initially_held = !matches!(case, "unclaimed-dup" | "late-mark");
8852 if initially_held {
8853 guest
8854 .thread
8855 .with_detfd(fd, |entry| entry.set_guest_held())
8856 .unwrap();
8857 }
8858 if matches!(case, "dup-guest" | "unclaimed-dup") {
8859 guest
8860 .thread
8861 .with_detfd(external_fd, |entry| entry.set_guest_held())
8862 .unwrap();
8863 }
8864 guest
8865 .thread
8866 .dup_fd(fd, saved.as_raw_fd(), nix::fcntl::OFlag::empty())
8867 .unwrap();
8868 let original = guest
8869 .thread
8870 .with_detfd(fd, |entry| entry.open_file_id())
8871 .unwrap();
8872 let mut operands = [libc::pollfd {
8873 fd,
8874 events: libc::POLLIN,
8875 revents: 0,
8876 }];
8877 let address = operands.as_mut_ptr() as usize;
8878 let (change, restore) = match case {
8879 "same" => (ProofAction::None, ProofAction::None),
8880 "noop-dup" => (ProofAction::Dup { from: fd, to: fd }, ProofAction::None),
8881 "dup" | "dup-guest" | "unclaimed-dup" => (
8882 ProofAction::Dup {
8883 from: external_fd,
8884 to: fd,
8885 },
8886 ProofAction::Dup {
8887 from: saved.as_raw_fd(),
8888 to: fd,
8889 },
8890 ),
8891 "operand" => (
8892 ProofAction::Operand {
8893 address,
8894 fd: external_fd,
8895 },
8896 ProofAction::Operand { address, fd },
8897 ),
8898 "mark" => (
8899 ProofAction::Mark { fd, held: false },
8900 ProofAction::Mark { fd, held: true },
8901 ),
8902 "late-mark" => (ProofAction::Mark { fd, held: true }, ProofAction::None),
8903 "unknown" => {
8904 guest.thread.remove_fd(fd);
8905 (ProofAction::None, ProofAction::None)
8906 }
8907 _ => panic!("unknown proof case {case}"),
8908 };
8909 let script = Arc::new(Mutex::new(ProofScript {
8910 actions: [ProofAction::None, change, restore, ProofAction::None].into(),
8911 ..Default::default()
8912 }));
8913 guest.proof_script = Some(script.clone());
8914 let mut resources = Resources::new(guest.thread.dettid);
8915 resources.insert(ResourceID::InternalIOPolling, Permission::W);
8916 if !generic_fd_entry {
8917 resources.polls_guest_state_only = true;
8918 resources.relies_on_guest_held_marks = true;
8919 }
8920 if kernel_wait || matches!(case, "operand" | "unknown") {
8923 resources.set_signal_interrupt_errno(Errno::EINTR);
8924 resources.signal_state_task = guest_signal_state_task(&guest);
8925 }
8926 let mask = 0_u64;
8927 let zero_timeout = reverie::syscalls::Timespec {
8928 tv_sec: 0,
8929 tv_nsec: 0,
8930 };
8931 let mut byte = 0_u8;
8932 let iov = libc::iovec {
8933 iov_base: (&mut byte as *mut u8).cast(),
8934 iov_len: 1,
8935 };
8936 let result = if generic_fd_entry {
8937 let tool = Detcore {
8938 detpid: guest.thread.detpid.unwrap(),
8939 cfg: guest.config.clone(),
8940 record_or_replay: crate::record_or_replay::NoopTool,
8941 };
8942 tool.execute_nonblockable_fd_syscall(
8945 &mut guest,
8946 syscalls::Readv::new()
8947 .with_fd(fd)
8948 .with_iov(Addr::from_raw(&iov as *const _ as usize))
8949 .with_len(1),
8950 )
8951 .await
8952 } else if masked_ppoll {
8953 let tool = Detcore {
8954 detpid: guest.thread.detpid.unwrap(),
8955 cfg: guest.config.clone(),
8956 record_or_replay: crate::record_or_replay::NoopTool,
8957 };
8958 tool.masked_ppoll_probe_for_test(
8959 &mut guest,
8960 syscalls::Ppoll::new()
8961 .with_fds(AddrMut::from_raw(address))
8962 .with_nfds(1)
8963 .with_timeout(AddrMut::from_raw(&zero_timeout as *const _ as usize))
8964 .with_sigmask(Addr::from_raw(&mask as *const _ as usize))
8965 .with_sigsetsize(KERNEL_SIGSET_SIZE),
8966 &mask as *const _ as usize,
8967 )
8968 .await
8969 } else if kernel_wait {
8970 retry_blocking_wait_under_mask(
8971 &mut guest,
8972 syscalls::Poll::new()
8973 .with_fds(AddrMut::from_raw(address))
8974 .with_nfds(1)
8975 .with_timeout(-1),
8976 resources,
8977 None,
8978 None,
8979 )
8980 .await
8981 .0
8982 } else if matches!(case, "operand" | "unknown") {
8983 retry_nonblocking_syscall_helper(
8984 &mut guest,
8985 syscalls::Poll::new()
8986 .with_fds(AddrMut::from_raw(address))
8987 .with_nfds(1)
8988 .with_timeout(-1),
8989 resources,
8990 None,
8991 None,
8992 )
8993 .await
8994 } else {
8995 retry_nonblocking_syscall_helper(
8996 &mut guest,
8997 syscalls::Readv::new().with_fd(fd),
8998 resources,
8999 None,
9000 None,
9001 )
9002 .await
9003 };
9004 assert_eq!(result.unwrap(), 1, "retry syscall result is unchanged");
9005 let script = script.lock().unwrap();
9006 assert_eq!(script.probes, 4);
9007 assert_eq!(
9008 script
9009 .requests
9010 .iter()
9011 .map(|r| r.poll_attempt)
9012 .collect::<Vec<_>>(),
9013 [0, 1, 2, 3]
9014 );
9015 if case != "unknown" {
9016 assert_eq!(
9017 guest
9018 .thread
9019 .with_detfd(fd, |entry| entry.open_file_id())
9020 .unwrap(),
9021 original
9022 );
9023 if case == "unclaimed-dup" {
9024 assert!(
9025 !guest
9026 .thread
9027 .with_detfd(fd, |entry| entry.is_guest_held())
9028 .unwrap()
9029 );
9030 } else {
9031 assert!(
9032 guest
9033 .thread
9034 .with_detfd(fd, |entry| entry.is_guest_held())
9035 .unwrap()
9036 );
9037 }
9038 assert_eq!(
9039 operands[0].fd, fd,
9040 "restored operands cannot restore a lost proof"
9041 );
9042 }
9043 script.requests.clone()
9044 }
9045
9046 #[tokio::test]
9047 async fn a_pipe_readv_entry_retains_only_its_original_poll_identity() {
9048 for case in [
9049 "same",
9050 "noop-dup",
9051 "dup-guest",
9052 "dup",
9053 "mark",
9054 "unclaimed-dup",
9055 "late-mark",
9056 ] {
9057 let requests = proof_requests_from(case, false, false, true).await;
9058 let initially_held = !matches!(case, "unclaimed-dup" | "late-mark");
9059 let valid = matches!(case, "same" | "noop-dup");
9060 for request in &requests[..2] {
9061 assert_eq!(
9062 request.polls_guest_state_only, initially_held,
9063 "{case}: before replacement"
9064 );
9065 assert_eq!(
9066 request.relies_on_guest_held_marks, initially_held,
9067 "{case}: before replacement"
9068 );
9069 }
9070 for request in &requests[2..] {
9071 assert_eq!(
9072 crate::scheduler::request_reports_idle_deadlock_for_test(request.clone()),
9073 valid,
9074 "{case}, attempt {}: actual entry's terminal verdict",
9075 request.poll_attempt
9076 );
9077 assert_eq!(request.polls_guest_state_only, valid, "{case}");
9078 assert_eq!(request.relies_on_guest_held_marks, valid, "{case}");
9079 }
9080 }
9081 }
9082
9083 #[tokio::test]
9084 async fn a_retried_descriptor_proof_does_not_survive_sibling_replacement() {
9085 for kernel_wait in [false, true] {
9086 for case in ["same", "noop-dup", "dup", "dup-guest", "mark"] {
9087 let requests = proof_requests(case, kernel_wait, false).await;
9088 let valid = matches!(case, "same" | "noop-dup");
9089 assert!(requests[0].polls_guest_state_only);
9090 assert!(requests[1].polls_guest_state_only);
9091 for request in &requests[2..] {
9092 assert_eq!(
9093 crate::scheduler::request_reports_idle_deadlock_for_test(request.clone()),
9094 valid,
9095 "{case}, kernel_wait={kernel_wait}: actual published request's terminal verdict"
9096 );
9097 assert_eq!(
9098 request.polls_guest_state_only, valid,
9099 "{case}, kernel_wait={kernel_wait}"
9100 );
9101 assert_eq!(
9102 request.relies_on_guest_held_marks, valid,
9103 "{case}, kernel_wait={kernel_wait}"
9104 );
9105 }
9106 }
9107 }
9108 }
9109
9110 #[tokio::test]
9111 async fn a_retried_poll_proof_does_not_survive_sibling_operand_replacement() {
9112 for kernel_wait in [false, true] {
9113 let requests = proof_requests("operand", kernel_wait, false).await;
9114 assert!(requests[0].polls_guest_state_only);
9115 assert!(requests[1].polls_guest_state_only);
9116 for request in &requests[2..] {
9117 assert!(
9118 !crate::scheduler::request_reports_idle_deadlock_for_test(request.clone()),
9119 "kernel_wait={kernel_wait}: replaced operand cannot prove terminal deadlock"
9120 );
9121 assert!(!request.polls_guest_state_only, "kernel_wait={kernel_wait}");
9122 assert!(
9123 !request.relies_on_guest_held_marks,
9124 "kernel_wait={kernel_wait}"
9125 );
9126 }
9127 }
9128 }
9129
9130 #[tokio::test]
9131 async fn a_retried_descriptor_proof_requires_an_initial_identity() {
9132 for kernel_wait in [false, true] {
9133 for request in proof_requests("unknown", kernel_wait, false).await {
9134 if request.poll_attempt > 0 {
9135 assert!(
9136 !crate::scheduler::request_reports_idle_deadlock_for_test(request.clone()),
9137 "kernel_wait={kernel_wait}: missing identity cannot prove terminal deadlock"
9138 );
9139 }
9140 assert!(!request.polls_guest_state_only, "kernel_wait={kernel_wait}");
9141 assert!(
9142 !request.relies_on_guest_held_marks,
9143 "kernel_wait={kernel_wait}"
9144 );
9145 }
9146 }
9147 }
9148
9149 #[tokio::test]
9150 async fn a_masked_ppoll_proof_does_not_survive_sibling_replacement() {
9151 for case in ["same", "noop-dup", "dup", "dup-guest", "operand", "mark"] {
9152 let requests = proof_requests(case, false, true).await;
9153 let valid = matches!(case, "same" | "noop-dup");
9154 assert!(requests[0].polls_guest_state_only);
9155 assert!(requests[1].polls_guest_state_only);
9156 for request in &requests[2..] {
9157 assert_eq!(
9158 crate::scheduler::request_reports_idle_deadlock_for_test(request.clone()),
9159 valid,
9160 "{case}: masked ppoll's actual published request"
9161 );
9162 assert_eq!(request.polls_guest_state_only, valid);
9163 assert_eq!(request.relies_on_guest_held_marks, valid);
9164 }
9165 }
9166 }
9167
9168 #[tokio::test]
9172 async fn a_generic_reads_first_probe_keeps_a_late_signal_visible() {
9173 use std::os::fd::AsRawFd;
9174
9175 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
9176 for (sent, blocked) in [(true, false), (false, false), (true, true)] {
9177 let own_mask = guest_mask() | if blocked { usr1 } else { 0 };
9178 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(3), own_mask);
9179 guest.config.sequentialize_threads = true;
9180 guest.config.backend.kernel_signal_targets_are_predictable = true;
9181 guest.thread.dettid = DetTid::from_raw(3);
9182 guest.thread.detpid = Some(DetPid::from_raw(3));
9183 let (reader, _writer) = nix::unistd::pipe2(nix::fcntl::OFlag::O_NONBLOCK).unwrap();
9184 let fd = reader.as_raw_fd();
9185 guest
9186 .thread
9187 .add_fd(fd, nix::fcntl::OFlag::O_RDONLY, FdType::Pipe, None)
9188 .unwrap();
9189 guest
9190 .thread
9191 .with_detfd(fd, |entry| entry.set_physically_nonblocking())
9192 .unwrap();
9193 let script = Arc::new(Mutex::new(ProofScript::default()));
9194 guest.proof_script = Some(script.clone());
9195 {
9196 let mut kernel = kernel.lock().unwrap();
9197 kernel.caught = usr1;
9198 kernel.pending_repost = if sent { usr1 } else { 0 };
9199 kernel.stops_for_pending = true;
9200 kernel.empty_read_probes = Some(0);
9201 kernel.read_ready_after = Some(4);
9202 kernel.setter.extend(std::iter::repeat_n(None, 8));
9205 }
9206 let _proc = scripted_proc(&kernel);
9207 let mut resources = Resources::new(guest.thread.dettid);
9208 resources.insert(ResourceID::InternalIOPolling, Permission::W);
9209 let result = retry_nonblocking_syscall_helper(
9210 &mut guest,
9211 syscalls::Read::new().with_fd(fd).with_len(1),
9212 resources,
9213 None,
9214 None,
9215 )
9216 .await
9217 .map_err(|error| error.into_errno().expect("guest errno"));
9218 let kernel = kernel.lock().unwrap();
9219 assert_eq!(
9220 kernel.blocked, own_mask,
9221 "the original guest mask is restored"
9222 );
9223 assert_eq!(
9224 result,
9225 if sent && !blocked {
9226 Err(Errno::ERESTARTSYS)
9227 } else {
9228 Ok(1)
9229 },
9230 "sent={sent}, blocked={blocked}, pending={:#x}, held={:?}, taken={:?}",
9231 kernel.pending,
9232 kernel.held_slot,
9233 kernel.taken
9234 );
9235 assert_eq!(kernel.held_slot, None);
9236 assert_eq!(kernel.pending, if sent { usr1 } else { 0 });
9237 assert_eq!(
9238 kernel.empty_read_probes,
9239 Some(if sent && !blocked { 2 } else { 4 })
9240 );
9241 assert_eq!(
9242 script.lock().unwrap().requests.len(),
9243 if sent && !blocked { 2 } else { 4 }
9244 );
9245 }
9246 }
9247
9248 async fn read_observing_retry_masks(
9251 route_child: bool,
9252 late_signal: bool,
9253 setter: Vec<Option<MaskChangeRefused>>,
9254 outcomes: Vec<MaskOutcome>,
9255 ) -> (Result<i64, Error>, Kernel, Arc<Mutex<ProofScript>>) {
9256 use std::os::fd::AsRawFd;
9257
9258 let own_mask = guest_mask();
9259 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(3), own_mask);
9260 guest.config.sequentialize_threads = true;
9261 guest.config.record_preemptions = true;
9262 guest.config.panic_on_unsupported_syscalls = false;
9263 guest.config.backend.kernel_signal_targets_are_predictable = true;
9264 guest.thread.dettid = DetTid::from_raw(3);
9265 guest.thread.detpid = Some(DetPid::from_raw(3));
9266 let (reader, _writer) = nix::unistd::pipe2(nix::fcntl::OFlag::O_NONBLOCK).unwrap();
9267 let fd = reader.as_raw_fd();
9268 guest
9269 .thread
9270 .add_fd(fd, nix::fcntl::OFlag::O_RDONLY, FdType::Pipe, None)
9271 .unwrap();
9272 guest
9273 .thread
9274 .with_detfd(fd, |entry| entry.set_physically_nonblocking())
9275 .unwrap();
9276 let script = Arc::new(Mutex::new(ProofScript {
9277 observe_retry_masks: true,
9278 route_child_on_retry: route_child,
9279 ..ProofScript::default()
9280 }));
9281 guest.proof_script = Some(script.clone());
9282 {
9283 let mut kernel = kernel.lock().unwrap();
9284 kernel.caught = kernel_sigset_bit(libc::SIGCHLD) | kernel_sigset_bit(libc::SIGUSR1);
9285 kernel.pending_repost = if late_signal {
9286 kernel_sigset_bit(libc::SIGUSR1)
9287 } else {
9288 0
9289 };
9290 kernel.stops_for_pending = true;
9291 kernel.empty_read_probes = Some(0);
9292 kernel.read_ready_after = Some(4);
9293 kernel.setter.extend(setter);
9294 kernel.outcomes.extend(outcomes);
9295 }
9296 let _proc = scripted_proc(&kernel);
9297 let mut resources = Resources::new(guest.thread.dettid);
9298 resources.insert(ResourceID::InternalIOPolling, Permission::W);
9299 let result = retry_nonblocking_syscall_helper(
9300 &mut guest,
9301 syscalls::Read::new().with_fd(fd).with_len(1),
9302 resources,
9303 None,
9304 None,
9305 )
9306 .await;
9307 (result, kernel, script)
9308 }
9309
9310 #[tokio::test]
9311 async fn a_generic_read_restores_its_mask_before_retry_yields() {
9312 for (route_child, late_signal) in [(true, false), (false, true), (false, false)] {
9313 let (result, kernel, script) =
9314 read_observing_retry_masks(route_child, late_signal, vec![None; 8], vec![]).await;
9315 let kernel = kernel.lock().unwrap();
9316 let script = script.lock().unwrap();
9317 assert_eq!(
9318 result.map_err(|error| error.into_errno().expect("guest errno")),
9319 if route_child || late_signal {
9320 Err(Errno::ERESTARTSYS)
9321 } else {
9322 Ok(1)
9323 },
9324 "route_child={route_child}, late_signal={late_signal}",
9325 );
9326 let attempts = if route_child || late_signal { 2 } else { 4 };
9327 assert_eq!(script.requests.len(), attempts);
9328 assert_eq!(kernel.empty_read_probes, Some(attempts));
9329 assert_eq!(
9330 kernel.read_probe_masks,
9331 vec![blocked_signal_mask() | guest_mask(); attempts]
9332 );
9333 assert_eq!(script.retry_boundaries.len(), attempts * 2 - 1);
9334 for (boundary, blocked, published) in &script.retry_boundaries {
9335 assert_eq!(
9336 *blocked,
9337 guest_mask(),
9338 "{boundary} must see the guest's mask"
9339 );
9340 assert_eq!(
9341 *published, None,
9342 "{boundary} must not keep a private-mask publication"
9343 );
9344 }
9345 assert_eq!(
9346 script.publications,
9347 [Some(guest_mask()), None].repeat(attempts)
9348 );
9349 assert_eq!(kernel.blocked, guest_mask());
9350 assert_eq!(
9351 kernel.pending,
9352 if route_child {
9353 kernel_sigset_bit(libc::SIGCHLD)
9354 } else if late_signal {
9355 kernel_sigset_bit(libc::SIGUSR1)
9356 } else {
9357 0
9358 }
9359 );
9360 assert_eq!(kernel.held_slot, None);
9361 assert!(kernel.taken.is_empty());
9362 assert!(kernel.lost.is_empty());
9363 }
9364 }
9365
9366 #[tokio::test]
9367 async fn a_generic_read_does_not_retry_after_an_in_place_release_refusal() {
9368 for refused in [
9369 MaskChangeRefused::Unsupported,
9370 MaskChangeRefused::RestorePending {
9371 saved: guest_mask(),
9372 effective: blocked_signal_mask() | guest_mask(),
9373 },
9374 MaskChangeRefused::Failed {
9375 errno: Errno::EIO,
9376 mask_unknown: false,
9377 },
9378 ] {
9379 let (result, kernel, script) =
9380 read_observing_retry_masks(false, false, vec![None, Some(refused), None], vec![])
9381 .await;
9382 assert!(matches!(
9383 diagnostic(result),
9384 BlockedWaitSignalError::MaskNotRestored { attempts: 1, .. }
9385 ));
9386 let kernel = kernel.lock().unwrap();
9387 let script = script.lock().unwrap();
9388 assert_eq!(kernel.empty_read_probes, Some(1));
9389 assert_eq!(script.requests.len(), 1);
9390 assert_eq!(
9391 script.retry_boundaries,
9392 vec![("request", guest_mask(), None)]
9393 );
9394 assert_eq!(
9395 kernel.blocked,
9396 guest_mask(),
9397 "final cleanup still restores the mask"
9398 );
9399 assert_eq!(script.publications, vec![Some(guest_mask()), None]);
9400 assert!(
9401 kernel.requested.is_empty(),
9402 "no injected restoration was needed"
9403 );
9404 assert_eq!(kernel.held_slot, None);
9405 }
9406 }
9407
9408 #[tokio::test]
9409 async fn a_generic_read_stops_immediately_when_release_leaves_the_mask_unknown() {
9410 let (result, kernel, script) = read_observing_retry_masks(
9411 false,
9412 false,
9413 vec![
9414 None,
9415 Some(MaskChangeRefused::Failed {
9416 errno: Errno::EIO,
9417 mask_unknown: true,
9418 }),
9419 ],
9420 vec![],
9421 )
9422 .await;
9423 assert!(matches!(
9424 diagnostic(result),
9425 BlockedWaitSignalError::MaskNotRestored {
9426 attempts: 1,
9427 last_error: Some(Errno::EIO),
9428 ..
9429 }
9430 ));
9431 let kernel = kernel.lock().unwrap();
9432 let script = script.lock().unwrap();
9433 assert_eq!(kernel.empty_read_probes, Some(1));
9434 assert_eq!(script.requests.len(), 1);
9435 assert_eq!(
9436 script.retry_boundaries,
9437 vec![("request", guest_mask(), None)]
9438 );
9439 assert_eq!(
9440 kernel.set_calls.len(),
9441 2,
9442 "an unknown mask is not probed or restored speculatively"
9443 );
9444 assert!(kernel.requested.is_empty());
9445 }
9446
9447 #[tokio::test]
9448 async fn a_generic_read_keeps_a_release_failure_fatal_after_loss_during_cleanup() {
9449 let (result, kernel, script) = read_observing_retry_masks(
9450 false,
9451 false,
9452 vec![
9453 None,
9454 Some(MaskChangeRefused::Unsupported),
9455 Some(MaskChangeRefused::Unsupported),
9456 ],
9457 vec![
9458 MaskOutcome::StoppedBy(libc::SIGUSR1),
9459 MaskOutcome::StoppedBy(libc::SIGUSR2),
9460 MaskOutcome::Apply,
9461 ],
9462 )
9463 .await;
9464 assert!(matches!(
9467 diagnostic(result),
9468 BlockedWaitSignalError::MaskNotRestored { attempts: 1, .. }
9469 ));
9470 let kernel = kernel.lock().unwrap();
9471 let script = script.lock().unwrap();
9472 assert_eq!(kernel.empty_read_probes, Some(1));
9473 assert_eq!(script.requests.len(), 1);
9474 assert_eq!(
9475 script.retry_boundaries,
9476 vec![("request", guest_mask(), None)]
9477 );
9478 assert_eq!(kernel.blocked, guest_mask());
9479 assert_eq!(kernel.requested, vec![guest_mask(); 3]);
9480 assert_eq!(kernel.lost, vec![libc::SIGUSR1]);
9481 assert_eq!(kernel.held_slot, Some(libc::SIGUSR2));
9482 }
9483
9484 #[tokio::test]
9485 async fn a_scalar_write_keeps_its_pending_only_probe_mask_policy() {
9486 use std::os::fd::AsRawFd;
9487
9488 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
9489 for (sent, blocked) in [(false, false), (true, false), (true, true)] {
9490 let own_mask = guest_mask() | if blocked { usr1 } else { 0 };
9491 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(3), own_mask);
9492 guest.config.sequentialize_threads = true;
9493 guest.config.backend.kernel_signal_targets_are_predictable = true;
9494 guest.thread.dettid = DetTid::from_raw(3);
9495 guest.thread.detpid = Some(DetPid::from_raw(3));
9496 let (_reader, writer) = nix::unistd::pipe2(nix::fcntl::OFlag::O_NONBLOCK).unwrap();
9497 let fd = writer.as_raw_fd();
9498 guest
9499 .thread
9500 .add_fd(fd, nix::fcntl::OFlag::O_WRONLY, FdType::Pipe, None)
9501 .unwrap();
9502 let open_file = guest
9503 .thread
9504 .with_detfd(fd, |entry| {
9505 entry.set_physically_nonblocking();
9506 entry.open_file_id()
9507 })
9508 .unwrap();
9509 let script = Arc::new(Mutex::new(ProofScript::default()));
9510 guest.proof_script = Some(script.clone());
9511 {
9512 let mut kernel = kernel.lock().unwrap();
9513 kernel.caught = usr1;
9514 kernel.write_probe_masks = Some(vec![]);
9515 kernel.pending_after_first_write = if sent { usr1 } else { 0 };
9516 kernel.setter.extend([None, None]);
9517 }
9518 let _proc = scripted_proc(&kernel);
9519 let tool = Detcore {
9520 detpid: guest.thread.detpid.unwrap(),
9521 cfg: guest.config.clone(),
9522 record_or_replay: crate::record_or_replay::NoopTool,
9523 };
9524 let byte = 0u8;
9525 let result = tool
9526 .execute_blocking_pipe_write(
9527 &mut guest,
9528 syscalls::Write::new()
9529 .with_fd(fd)
9530 .with_buf(Addr::from_ptr(&byte))
9531 .with_len(1),
9532 open_file,
9533 )
9534 .await
9535 .map_err(|error| error.into_errno().expect("guest errno"));
9536 let kernel = kernel.lock().unwrap();
9537 let script = script.lock().unwrap();
9538 if sent && !blocked {
9539 assert_eq!(result, Err(Errno::ERESTARTSYS));
9540 assert_eq!(
9541 kernel.write_probe_masks.as_deref(),
9542 Some([own_mask, blocked_signal_mask() | own_mask].as_slice())
9543 );
9544 assert_eq!(
9545 kernel.set_calls,
9546 vec![blocked_signal_mask() | own_mask, own_mask]
9547 );
9548 assert_eq!(script.publications, vec![Some(own_mask), None]);
9549 assert_eq!(script.requests.len(), 1);
9550 } else {
9551 assert_eq!(result, Ok(1));
9552 assert_eq!(
9553 kernel.write_probe_masks.as_deref(),
9554 Some([own_mask; 3].as_slice()),
9555 "sent={sent}, blocked={blocked}"
9556 );
9557 assert!(
9558 kernel.set_calls.is_empty(),
9559 "an empty or guest-blocked snapshot must not change the scalar writer's mask"
9560 );
9561 assert!(script.publications.is_empty());
9562 assert_eq!(script.requests.len(), 2);
9563 }
9564 assert_eq!(kernel.blocked, own_mask);
9565 assert_eq!(kernel.pending, if sent { usr1 } else { 0 });
9566 assert_eq!(kernel.held_slot, None);
9567 assert!(kernel.lost.is_empty());
9568 }
9569 }
9570
9571 #[tokio::test]
9572 async fn a_generic_read_does_not_handoff_after_release_reports_exit() {
9573 let (result, kernel, script) = read_observing_retry_masks(
9574 false,
9575 false,
9576 vec![None, Some(MaskChangeRefused::Gone)],
9577 vec![],
9578 )
9579 .await;
9580 assert_eq!(
9583 result.map_err(|error| error.into_errno().expect("gone-thread errno")),
9584 Err(Errno::ERESTARTNOINTR)
9585 );
9586 let kernel = kernel.lock().unwrap();
9587 let script = script.lock().unwrap();
9588 assert!(kernel.gone);
9589 assert_eq!(kernel.empty_read_probes, Some(1));
9590 assert_eq!(script.requests.len(), 1);
9591 assert_eq!(
9592 script.retry_boundaries,
9593 vec![("request", guest_mask(), None)]
9594 );
9595 assert_eq!(kernel.set_calls.len(), 2);
9596 assert!(kernel.requested.is_empty());
9597 }
9598
9599 fn guest_mask() -> KernelSigset {
9601 kernel_sigset_bit(libc::SIGUSR2)
9602 }
9603
9604 fn diagnostic<V: std::fmt::Debug>(result: Result<V, Error>) -> BlockedWaitSignalError {
9606 match result {
9607 Err(Error::Tool(error)) => *error
9608 .downcast_ref::<BlockedWaitSignalError>()
9609 .unwrap_or_else(|| panic!("not a blocked-wait diagnostic: {error:#}")),
9610 other => panic!("expected the run to end with a diagnostic, got {other:?}"),
9611 }
9612 }
9613
9614 fn refusal<V: std::fmt::Debug>(result: Result<V, Error>) -> HeldSignalRefusal {
9616 match result {
9617 Err(Error::Tool(error)) => *error
9618 .downcast_ref::<HeldSignalRefusal>()
9619 .unwrap_or_else(|| panic!("not a held-signal refusal: {error:#}")),
9620 other => panic!("expected the wait to end with a held-signal refusal, got {other:?}"),
9621 }
9622 }
9623
9624 async fn blocked_wait(guest: &mut WaitGuest, kernel: &Kernel) -> KernelSignalWait {
9626 let mut wait = KernelSignalWait::new(guest, 0, false, Errno::ERESTARTSYS);
9627 wait.block(guest, None).await.unwrap();
9628 assert!(!wait.needs_block());
9629 assert_eq!(kernel.lock().unwrap().blocked, blocked_signal_mask());
9630 wait
9631 }
9632
9633 #[tokio::test]
9641 async fn restore_repeats_the_mask_change_until_the_kernel_reports_it() {
9642 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9643 let _proc = scripted_proc(&kernel);
9644 let mut wait = blocked_wait(&mut guest, &kernel).await;
9645 kernel
9646 .lock()
9647 .unwrap()
9648 .outcomes
9649 .extend([MaskOutcome::StoppedBefore; 5]);
9650
9651 let result = wait.restore(&mut guest, None).await;
9652
9653 assert_eq!(
9654 refusal(result),
9655 HeldSignalRefusal {
9656 wait: None,
9657 held: None,
9658 replacing: None,
9659 loss: HeldSignalLoss::Restoration,
9660 }
9661 );
9662 let kernel = kernel.lock().unwrap();
9663 assert_eq!(kernel.blocked, guest_mask());
9664 assert_eq!(
9665 kernel.requested.len(),
9666 1 + 6,
9667 "block, then five stopped attempts and one that ran"
9668 );
9669 assert!(
9670 kernel.requested[1..]
9671 .iter()
9672 .all(|&mask| mask == guest_mask())
9673 );
9674 }
9675
9676 #[tokio::test]
9677 async fn a_mask_that_signals_keep_stopping_ends_the_run_instead_of_resuming_the_guest() {
9678 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9679 let _proc = scripted_proc(&kernel);
9680 let mut wait = blocked_wait(&mut guest, &kernel).await;
9681 kernel
9682 .lock()
9683 .unwrap()
9684 .outcomes
9685 .extend([MaskOutcome::StoppedBefore; 64]);
9686
9687 let error = diagnostic(wait.restore(&mut guest, None).await);
9688
9689 assert_eq!(
9690 error,
9691 BlockedWaitSignalError::MaskNotRestored {
9692 pid: guest.pid,
9693 tid: guest.tid,
9694 attempts: 16,
9695 last_error: Some(Errno::ERESTARTNOINTR),
9696 }
9697 );
9698 let message = error.to_string();
9699 assert!(
9700 message.starts_with("cannot restore the signal mask of guest thread")
9701 && message.contains("(16 attempts, last error ")
9702 && message.contains("ERESTARTNOINTR")
9703 && message.ends_with("); it would resume with every signal blocked"),
9704 "{message}"
9705 );
9706 assert_eq!(kernel.lock().unwrap().requested.len(), 1 + 16);
9707 }
9708
9709 #[tokio::test]
9710 async fn a_mask_change_that_cannot_run_ends_the_run_instead_of_resuming_the_guest() {
9711 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9712 let _proc = scripted_proc(&kernel);
9713 let mut wait = blocked_wait(&mut guest, &kernel).await;
9714 kernel
9715 .lock()
9716 .unwrap()
9717 .outcomes
9718 .push_back(MaskOutcome::Fail(Errno::EFAULT));
9719
9720 let error = diagnostic(wait.restore(&mut guest, None).await);
9721
9722 assert_eq!(
9723 error,
9724 BlockedWaitSignalError::MaskNotRestored {
9725 pid: guest.pid,
9726 tid: guest.tid,
9727 attempts: 1,
9728 last_error: Some(Errno::EFAULT),
9729 }
9730 );
9731 }
9732
9733 #[tokio::test]
9738 async fn an_in_place_block_and_its_restore_make_no_injection() {
9739 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9740 let _proc = scripted_proc(&kernel);
9741 guest.commit_fails = true;
9742 kernel.lock().unwrap().setter.extend([None, None]);
9743 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9744 assert!(wait.block_in_place(&mut guest, 0).await.unwrap());
9745 assert_eq!(wait.effective_mask(), Some(guest_mask()));
9746 assert_eq!(
9747 kernel.lock().unwrap().blocked,
9748 blocked_signal_mask() | guest_mask()
9749 );
9750 wait.restore(&mut guest, None).await.unwrap();
9751 let kernel = kernel.lock().unwrap();
9752 assert_eq!(kernel.blocked, guest_mask());
9753 assert_eq!(
9754 kernel.set_calls,
9755 vec![blocked_signal_mask() | guest_mask(), guest_mask()]
9756 );
9757 assert!(
9758 kernel.requested.is_empty(),
9759 "no rt_sigprocmask was injected"
9760 );
9761 assert_eq!(wait.effective_mask(), None);
9762 }
9763
9764 #[tokio::test]
9770 async fn an_in_place_block_holds_the_host_timed_signals() {
9771 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9772 let _proc = scripted_proc(&kernel);
9773 kernel.lock().unwrap().setter.push_back(None);
9774 let host_timed = kernel_sigset_bit(libc::SIGIO) | kernel_sigset_bit(libc::SIGHUP);
9775 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9776 assert!(wait.block_in_place(&mut guest, host_timed).await.unwrap());
9777 assert_eq!(wait.held_until_return & host_timed, host_timed);
9778 assert_eq!(wait.host_timed & host_timed, host_timed);
9779 }
9780
9781 #[tokio::test]
9785 async fn a_refused_in_place_block_leaves_the_guest_mask() {
9786 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9787 let _proc = scripted_proc(&kernel);
9788 kernel
9789 .lock()
9790 .unwrap()
9791 .setter
9792 .push_back(Some(MaskChangeRefused::RestorePending {
9793 saved: guest_mask(),
9794 effective: 0,
9795 }));
9796 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9797 assert!(!wait.block_in_place(&mut guest, 0).await.unwrap());
9798 assert!(!wait.needs_block());
9799 assert_eq!(wait.effective_mask(), None);
9800 wait.restore(&mut guest, None).await.unwrap();
9801 let kernel = kernel.lock().unwrap();
9802 assert_eq!(kernel.blocked, guest_mask());
9803 assert_eq!(kernel.set_calls.len(), 1);
9804 assert!(kernel.requested.is_empty());
9805 }
9806
9807 #[tokio::test]
9811 async fn a_refused_in_place_restore_falls_back_to_an_injection() {
9812 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9813 let _proc = scripted_proc(&kernel);
9814 kernel
9815 .lock()
9816 .unwrap()
9817 .setter
9818 .extend([None, Some(MaskChangeRefused::Unsupported)]);
9819 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9820 assert!(wait.block_in_place(&mut guest, 0).await.unwrap());
9821 wait.restore(&mut guest, None).await.unwrap();
9822 let kernel = kernel.lock().unwrap();
9823 assert_eq!(kernel.blocked, guest_mask());
9824 assert_eq!(kernel.requested, vec![guest_mask()], "one injected restore");
9825 }
9826
9827 #[tokio::test]
9830 async fn an_unknown_mask_after_an_in_place_change_ends_the_run() {
9831 let unknown = Some(MaskChangeRefused::Failed {
9832 errno: Errno::EIO,
9833 mask_unknown: true,
9834 });
9835 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9836 let _proc = scripted_proc(&kernel);
9837 let not_restored = BlockedWaitSignalError::MaskNotRestored {
9838 pid: guest.pid,
9839 tid: guest.tid,
9840 attempts: 1,
9841 last_error: Some(Errno::EIO),
9842 };
9843 kernel.lock().unwrap().setter.push_back(unknown);
9844 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9845 assert_eq!(
9846 diagnostic(wait.block_in_place(&mut guest, 0).await),
9847 not_restored
9848 );
9849
9850 kernel.lock().unwrap().setter.extend([None, unknown]);
9851 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9852 assert!(wait.block_in_place(&mut guest, 0).await.unwrap());
9853 assert_eq!(
9854 diagnostic(wait.restore(&mut guest, None).await),
9855 not_restored
9856 );
9857 assert!(kernel.lock().unwrap().requested.is_empty());
9858 }
9859
9860 #[tokio::test]
9863 async fn a_shield_lowered_for_a_gone_thread_succeeds() {
9864 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9865 kernel
9866 .lock()
9867 .unwrap()
9868 .setter
9869 .push_back(Some(MaskChangeRefused::Gone));
9870 let shield = IgnoredSignalShield {
9871 guest_mask: Some(guest_mask()),
9872 };
9873 shield.lower(&mut guest).await.unwrap();
9874 assert_eq!(kernel.lock().unwrap().set_calls.len(), 1);
9875 }
9876
9877 #[tokio::test]
9879 async fn a_gone_thread_needs_no_restore() {
9880 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9881 let _proc = scripted_proc(&kernel);
9882 kernel
9883 .lock()
9884 .unwrap()
9885 .setter
9886 .extend([None, Some(MaskChangeRefused::Gone)]);
9887 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9888 assert!(wait.block_in_place(&mut guest, 0).await.unwrap());
9889 wait.restore(&mut guest, None).await.unwrap();
9890 assert!(kernel.lock().unwrap().requested.is_empty());
9891 }
9892
9893 #[tokio::test]
9894 async fn an_unreadable_signal_state_of_a_live_thread_ends_the_run() {
9895 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9896 let _proc = scripted_proc(&kernel);
9897 let (pid, tid) = (guest.pid, guest.tid);
9898 let expected = |errno| BlockedWaitSignalError::StateUnreadable { pid, tid, errno };
9899
9900 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9902 kernel.lock().unwrap().read_failures.push_back(Errno::EIO);
9903 assert_eq!(diagnostic(wait.interrupted()), expected(Errno::EIO));
9904
9905 kernel.lock().unwrap().read_failures.push_back(Errno::ESRCH);
9907 assert_eq!(
9908 diagnostic(wait.block(&mut guest, None).await),
9909 expected(Errno::ESRCH)
9910 );
9911 assert!(kernel.lock().unwrap().requested.is_empty());
9912
9913 wait.block(&mut guest, None).await.unwrap();
9915 kernel.lock().unwrap().read_failures.push_back(Errno::ESRCH);
9916 assert_eq!(
9917 diagnostic(wait.restore(&mut guest, None).await),
9918 expected(Errno::ESRCH)
9919 );
9920 let error = expected(Errno::EIO).to_string();
9921 assert!(
9922 error.starts_with("cannot read the signal state of guest thread"),
9923 "{error}"
9924 );
9925 }
9926
9927 #[tokio::test]
9928 async fn a_thread_that_no_longer_exists_ends_its_wait_with_erestartnointr() {
9929 let exited = std::thread::spawn(|| unsafe { libc::gettid() })
9931 .join()
9932 .unwrap();
9933 let task = format!("/proc/self/task/{exited}");
9944 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
9945 while std::path::Path::new(&task).exists() {
9946 assert!(
9947 std::time::Instant::now() < deadline,
9948 "thread {exited} is still in /proc 10 s after it was joined"
9949 );
9950 std::thread::sleep(std::time::Duration::from_millis(1));
9951 }
9952 let (guest, _kernel) = WaitGuest::new(Pid::from_raw(exited), guest_mask());
9953 let wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9956 assert!(matches!(
9957 wait.interrupted(),
9958 Err(Error::Errno(Errno::ERESTARTNOINTR))
9959 ));
9960 assert!(matches!(
9961 read_wait_signal_state(guest.pid, guest.tid),
9962 Err(Error::Errno(Errno::ERESTARTNOINTR))
9963 ));
9964 }
9965
9966 #[tokio::test]
9967 async fn a_stack_with_no_scratch_room_leaves_the_wait_under_the_guest_mask() {
9968 let (mut guest, kernel) = WaitGuest::live(guest_mask());
9969 guest.commit_fails = true;
9970 let _proc = scripted_proc(&kernel);
9971 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
9972
9973 wait.block(&mut guest, None).await.unwrap();
9974
9975 assert!(!wait.needs_block(), "block must not be tried again");
9976 assert_eq!(kernel.lock().unwrap().blocked, guest_mask());
9977 assert!(kernel.lock().unwrap().requested.is_empty());
9978
9979 {
9981 let mut kernel = kernel.lock().unwrap();
9982 kernel.caught = kernel_sigset_bit(libc::SIGUSR1) | kernel_sigset_bit(libc::SIGUSR2);
9983 kernel.pending = kernel_sigset_bit(libc::SIGUSR2);
9984 }
9985 assert!(!wait.interrupted().unwrap(), "SIGUSR2 is blocked");
9986 kernel.lock().unwrap().pending = kernel_sigset_bit(libc::SIGUSR1);
9987 assert!(wait.interrupted().unwrap(), "SIGUSR1 is not blocked");
9988
9989 wait.restore(&mut guest, None).await.unwrap();
9990 assert!(
9991 kernel.lock().unwrap().requested.is_empty(),
9992 "nothing to put back"
9993 );
9994 assert_eq!(kernel.lock().unwrap().blocked, guest_mask());
9995 }
9996
9997 fn stopping_guest(pending: KernelSigset, ignored: KernelSigset) -> (WaitGuest, Kernel) {
10000 let (guest, kernel) = WaitGuest::live(guest_mask());
10001 {
10002 let mut kernel = kernel.lock().unwrap();
10003 kernel.stops_for_pending = true;
10004 kernel.pending = pending;
10005 kernel.ignored = ignored;
10006 }
10007 (guest, kernel)
10008 }
10009
10010 async fn inject_mask_absorbing(
10012 wait: &mut KernelSignalWait,
10013 guest: &mut WaitGuest,
10014 mask: KernelSigset,
10015 ) -> Result<Result<i64, Errno>, Error> {
10016 let mut stack = guest.stack().await;
10017 let cell = stack.push(mask);
10018 let _guard = stack.commit().unwrap();
10019 wait.inject_absorbing(guest, set_signal_mask_call(cell))
10020 .await
10021 }
10022
10023 #[tokio::test]
10029 async fn an_ignored_signal_that_stops_the_mask_change_does_not_end_the_wait() {
10030 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10031 let (mut guest, kernel) = stopping_guest(usr1, usr1);
10032 let _proc = scripted_proc(&kernel);
10033 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10034
10035 wait.block(&mut guest, None).await.unwrap();
10036
10037 assert!(!wait.needs_block());
10038 let all_blocked = blocked_signal_mask() | guest_mask();
10039 {
10040 let kernel = kernel.lock().unwrap();
10041 assert_eq!(kernel.taken, vec![libc::SIGUSR1]);
10042 assert_eq!(kernel.requested, vec![all_blocked, all_blocked]);
10043 assert_eq!(kernel.blocked, all_blocked);
10044 }
10045 assert_eq!(
10046 wait.held,
10047 Some(HeldSignal {
10048 signal: libc::SIGUSR1,
10049 queue: SignalQueue::Thread,
10050 kind: HeldKind::Harmless,
10051 })
10052 );
10053 assert!(
10054 !wait.interrupted().unwrap(),
10055 "an ignored signal does not end the wait"
10056 );
10057 wait.restore(&mut guest, None).await.unwrap();
10058 assert_eq!(kernel.lock().unwrap().blocked, guest_mask());
10059 }
10060
10061 #[tokio::test]
10065 async fn a_default_ignored_signal_that_stops_an_injection_is_held() {
10066 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGWINCH), 0);
10067 let _proc = scripted_proc(&kernel);
10068 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10069
10070 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10071
10072 assert!(matches!(result, Ok(Ok(0))), "{result:?}");
10073 assert_eq!(kernel.lock().unwrap().taken, vec![libc::SIGWINCH]);
10074 assert_eq!(kernel.lock().unwrap().requested.len(), 2);
10075 assert_eq!(wait.held.map(|held| held.kind), Some(HeldKind::Harmless));
10076 assert!(!wait.interrupted().unwrap());
10077 }
10078
10079 #[tokio::test]
10082 async fn a_pending_caught_signal_ends_the_wait_before_the_injection() {
10083 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10084 let (mut guest, kernel) = stopping_guest(usr1, 0);
10085 kernel.lock().unwrap().caught = usr1;
10086 let _proc = scripted_proc(&kernel);
10087 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10088
10089 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10090
10091 assert!(
10092 matches!(result, Err(Error::Errno(Errno::ERESTARTSYS))),
10093 "{result:?}"
10094 );
10095 let kernel = kernel.lock().unwrap();
10096 assert!(kernel.requested.is_empty(), "nothing is injected");
10097 assert_eq!(kernel.pending, usr1);
10098 assert_eq!(wait.held, None);
10099 }
10100
10101 #[tokio::test]
10108 async fn a_held_sigchld_is_never_replaced_by_another_stop() {
10109 let (mut guest, kernel) = stopping_guest(
10110 kernel_sigset_bit(libc::SIGCHLD) | kernel_sigset_bit(libc::SIGWINCH),
10111 0,
10112 );
10113 let _proc = scripted_proc(&kernel);
10114 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10115
10116 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10117
10118 assert_eq!(
10119 refusal(result),
10120 HeldSignalRefusal {
10121 wait: None,
10122 held: Some(libc::SIGCHLD),
10123 replacing: Some(libc::SIGWINCH),
10124 loss: HeldSignalLoss::Replaced,
10125 }
10126 );
10127 let kernel = kernel.lock().unwrap();
10128 assert_eq!(kernel.taken, vec![libc::SIGCHLD]);
10129 assert_eq!(
10130 kernel.requested.len(),
10131 1,
10132 "the second injection is not made"
10133 );
10134 assert_eq!(kernel.pending, kernel_sigset_bit(libc::SIGWINCH));
10135 assert_eq!(wait.held.map(|held| held.kind), Some(HeldKind::Precious));
10136 }
10137
10138 async fn wait_holding_sigchld(guest: &mut WaitGuest, kernel: &Kernel) -> KernelSignalWait {
10141 let mut wait = KernelSignalWait::new(&*guest, 0, false, Errno::ERESTARTSYS);
10142 wait.block(guest, None).await.unwrap();
10143 assert_eq!(
10144 wait.held.map(|held| (held.signal, held.kind)),
10145 Some((libc::SIGCHLD, HeldKind::Precious))
10146 );
10147 let kernel = kernel.lock().unwrap();
10148 assert_eq!(kernel.blocked, blocked_signal_mask() | guest_mask());
10149 assert_eq!(kernel.held_slot, Some(libc::SIGCHLD));
10150 wait
10151 }
10152
10153 #[tokio::test]
10161 async fn a_stop_of_the_mask_restoration_that_replaces_a_held_sigchld_is_refused() {
10162 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGCHLD), 0);
10163 let _proc = scripted_proc(&kernel);
10164 let mut wait = wait_holding_sigchld(&mut guest, &kernel).await;
10165 kernel
10166 .lock()
10167 .unwrap()
10168 .outcomes
10169 .push_back(MaskOutcome::StoppedBy(libc::SIGSTOP));
10170
10171 let result = wait.restore(&mut guest, None).await;
10172
10173 {
10174 let kernel = kernel.lock().unwrap();
10175 assert_eq!(kernel.blocked, guest_mask(), "the guest's mask is back");
10176 assert_eq!(
10177 kernel.requested[2..],
10178 [guest_mask(), guest_mask()],
10179 "one stopped restoration attempt and one that ran"
10180 );
10181 assert_eq!(kernel.held_slot, Some(libc::SIGSTOP));
10182 assert_eq!(kernel.lost, vec![libc::SIGCHLD]);
10183 }
10184 assert_eq!(
10185 refusal(result),
10186 HeldSignalRefusal {
10187 wait: None,
10188 held: Some(libc::SIGCHLD),
10189 replacing: None,
10190 loss: HeldSignalLoss::Restoration,
10191 }
10192 );
10193 }
10194
10195 #[tokio::test]
10200 async fn an_identified_stop_of_the_mask_restoration_that_replaces_a_held_sigchld_is_refused() {
10201 const RESERVED: i32 = 32;
10202 assert_eq!(blocked_signal_mask() & kernel_sigset_bit(RESERVED), 0);
10203 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGCHLD), 0);
10204 let _proc = scripted_proc(&kernel);
10205 let mut wait = wait_holding_sigchld(&mut guest, &kernel).await;
10206 kernel.lock().unwrap().pending = kernel_sigset_bit(RESERVED);
10207
10208 let result = wait.restore(&mut guest, None).await;
10209
10210 {
10211 let kernel = kernel.lock().unwrap();
10212 assert_eq!(kernel.blocked, guest_mask(), "the guest's mask is back");
10213 assert_eq!(kernel.taken, vec![libc::SIGCHLD, RESERVED]);
10214 assert_eq!(kernel.held_slot, Some(RESERVED));
10215 assert_eq!(kernel.lost, vec![libc::SIGCHLD]);
10216 }
10217 let refused = refusal(result);
10218 assert_eq!(
10219 refused,
10220 HeldSignalRefusal {
10221 wait: None,
10222 held: Some(libc::SIGCHLD),
10223 replacing: Some(RESERVED),
10224 loss: HeldSignalLoss::Restoration,
10225 }
10226 );
10227 let message = refused.to_string();
10228 assert!(
10229 message.starts_with(
10230 "unsupported: the a blocking wait cannot keep the held signal SIGCHLD: signal 32 \
10231 stopped the injection that puts the guest's signal mask back and replaced it"
10232 ),
10233 "{message}"
10234 );
10235 }
10236
10237 #[test]
10242 fn a_restoration_refusal_replaces_the_result_unless_the_wait_refused_first() {
10243 let tool = |refusal: HeldSignalRefusal| Error::Tool(anyhow::Error::new(refusal));
10244 let restoration = HeldSignalRefusal {
10245 wait: None,
10246 held: Some(libc::SIGCHLD),
10247 replacing: None,
10248 loss: HeldSignalLoss::Restoration,
10249 };
10250 let replaced = HeldSignalRefusal {
10251 wait: None,
10252 held: Some(libc::SIGCHLD),
10253 replacing: Some(libc::SIGUSR1),
10254 loss: HeldSignalLoss::Replaced,
10255 };
10256 let not_restored = BlockedWaitSignalError::MaskNotRestored {
10257 pid: Pid::from_raw(1),
10258 tid: Pid::from_raw(1),
10259 attempts: 16,
10260 last_error: Some(Errno::ERESTARTNOINTR),
10261 };
10262
10263 assert_eq!(result_after_restore(Ok(()), Ok(3)).unwrap().unwrap(), 3);
10264 assert_eq!(
10265 refusal(result_after_restore(Err(tool(restoration)), Ok(3)).unwrap()),
10266 restoration
10267 );
10268 assert_eq!(
10269 refusal(
10270 result_after_restore(Err(tool(restoration)), Err(Errno::ERESTARTSYS.into()))
10271 .unwrap()
10272 ),
10273 restoration
10274 );
10275 assert_eq!(
10276 refusal(result_after_restore(Err(tool(restoration)), Err(tool(replaced))).unwrap()),
10277 replaced
10278 );
10279 assert_eq!(
10280 diagnostic(result_after_restore(
10281 Err(Error::Tool(anyhow::Error::new(not_restored))),
10282 Ok(3)
10283 )),
10284 not_restored
10285 );
10286 }
10287
10288 fn unidentified_stops_guest(stops: usize) -> (WaitGuest, Kernel) {
10293 let (guest, kernel) = WaitGuest::live(guest_mask());
10294 kernel.lock().unwrap().outcomes =
10295 std::iter::repeat_n(MaskOutcome::StoppedBefore, stops).collect();
10296 (guest, kernel)
10297 }
10298
10299 #[tokio::test]
10308 async fn a_stop_that_cannot_be_identified_keeps_a_timed_wait_and_its_deadline() {
10309 for deadline in [true, false] {
10310 let (mut guest, kernel) = unidentified_stops_guest(1);
10311 let _proc = scripted_proc(&kernel);
10312 let mut wait = KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND)
10313 .with_deadline(deadline);
10314
10315 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10316
10317 assert!(
10318 matches!(result, Ok(Ok(0))),
10319 "deadline={deadline}: {result:?}"
10320 );
10321 assert_eq!(
10322 kernel.lock().unwrap().requested.len(),
10323 2,
10324 "deadline={deadline}"
10325 );
10326 assert_eq!(wait.held, None, "deadline={deadline}");
10327 }
10328 }
10329
10330 #[tokio::test]
10338 async fn only_a_wait_without_a_deadline_ends_after_the_absorbed_stop_bound() {
10339 let stops = 100;
10340 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10341 let (mut guest, kernel) = stopping_guest(usr1, usr1);
10342 kernel.lock().unwrap().repost_after_take = stops - 1;
10343 let _proc = scripted_proc(&kernel);
10344 let mut wait =
10345 KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND).with_deadline(true);
10346 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10347 assert!(matches!(result, Ok(Ok(0))), "{result:?}");
10348 assert_eq!(kernel.lock().unwrap().requested.len(), stops + 1);
10349 assert_eq!(wait.held.map(|held| held.kind), Some(HeldKind::Harmless));
10350
10351 let (mut guest, kernel) = stopping_guest(usr1, usr1);
10352 kernel.lock().unwrap().repost_after_take = stops - 1;
10353 let _proc = scripted_proc(&kernel);
10354 let mut wait = KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND);
10355 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10356 assert!(
10357 matches!(result, Err(Error::Errno(Errno::ERESTARTNOINTR))),
10358 "{result:?}"
10359 );
10360 assert_eq!(kernel.lock().unwrap().requested.len(), 65);
10361 }
10362
10363 #[tokio::test]
10369 async fn a_wait_that_reaches_the_stop_bound_holding_a_sigchld_is_refused() {
10370 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGCHLD), 0);
10371 kernel.lock().unwrap().repost_after_take = 100;
10372 let _proc = scripted_proc(&kernel);
10373 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10374
10375 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10376
10377 assert_eq!(
10378 refusal(result),
10379 HeldSignalRefusal {
10380 wait: None,
10381 held: Some(libc::SIGCHLD),
10382 replacing: None,
10383 loss: HeldSignalLoss::StopBound,
10384 }
10385 );
10386 assert_eq!(kernel.lock().unwrap().requested.len(), 65);
10387 }
10388
10389 #[tokio::test]
10397 async fn an_unidentified_stop_at_the_stop_bound_is_refused() {
10398 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10399 for deadline in [false, true] {
10400 let (mut guest, kernel) = stopping_guest(usr1, usr1);
10401 {
10402 let mut kernel = kernel.lock().unwrap();
10403 kernel.repost_after_take = 63;
10406 kernel.outcomes = VecDeque::from([MaskOutcome::StoppedBefore]);
10407 }
10408 let _proc = scripted_proc(&kernel);
10409 let mut wait = KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND)
10410 .with_deadline(deadline);
10411
10412 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10413
10414 if deadline {
10415 assert!(matches!(result, Ok(Ok(0))), "{result:?}");
10416 assert_eq!(kernel.lock().unwrap().requested.len(), 66);
10417 assert!(wait.held_unidentified);
10418 } else {
10419 assert_eq!(
10420 refusal(result),
10421 HeldSignalRefusal {
10422 wait: None,
10423 held: None,
10424 replacing: None,
10425 loss: HeldSignalLoss::StopBound,
10426 }
10427 );
10428 assert_eq!(kernel.lock().unwrap().requested.len(), 65);
10429 }
10430 assert_eq!(
10431 kernel.lock().unwrap().taken.len(),
10432 64,
10433 "deadline={deadline}"
10434 );
10435 }
10436 }
10437
10438 #[tokio::test]
10443 async fn an_unidentified_stop_over_a_held_sigchld_is_refused() {
10444 for deadline in [true, false] {
10445 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGCHLD), 0);
10446 kernel.lock().unwrap().outcomes = VecDeque::from([MaskOutcome::StoppedBefore]);
10447 let _proc = scripted_proc(&kernel);
10448 let mut wait =
10449 KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS).with_deadline(deadline);
10450
10451 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10452
10453 assert_eq!(
10454 refusal(result),
10455 HeldSignalRefusal {
10456 wait: None,
10457 held: Some(libc::SIGCHLD),
10458 replacing: None,
10459 loss: HeldSignalLoss::UnidentifiedStop,
10460 },
10461 "deadline={deadline}"
10462 );
10463 let kernel = kernel.lock().unwrap();
10464 assert_eq!(kernel.requested.len(), 2, "deadline={deadline}");
10465 assert_eq!(kernel.taken, vec![libc::SIGCHLD], "deadline={deadline}");
10466 }
10467 }
10468
10469 #[tokio::test]
10474 async fn a_second_unidentified_stop_is_refused() {
10475 for deadline in [true, false] {
10476 let (mut guest, kernel) = unidentified_stops_guest(2);
10477 let _proc = scripted_proc(&kernel);
10478 let mut wait = KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND)
10479 .with_deadline(deadline);
10480
10481 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10482
10483 assert_eq!(
10484 refusal(result),
10485 HeldSignalRefusal {
10486 wait: None,
10487 held: None,
10488 replacing: None,
10489 loss: HeldSignalLoss::UnidentifiedStop,
10490 },
10491 "deadline={deadline}"
10492 );
10493 assert_eq!(
10494 kernel.lock().unwrap().requested.len(),
10495 2,
10496 "deadline={deadline}"
10497 );
10498 }
10499 }
10500
10501 #[tokio::test]
10514 async fn a_sigchld_after_an_unidentified_stop_is_refused_before_it_replaces_it() {
10515 let chld = kernel_sigset_bit(libc::SIGCHLD);
10516 for deadline in [true, false] {
10517 let (mut guest, kernel) = stopping_guest(0, 0);
10518 {
10519 let mut kernel = kernel.lock().unwrap();
10520 kernel.pending_repost = chld;
10524 kernel.repost_after_take = 1;
10525 }
10526 let _proc = scripted_proc(&kernel);
10527 let mut wait = KernelSignalWait::new(&guest, 0, true, Errno::ERESTARTNOHAND)
10528 .with_deadline(deadline);
10529
10530 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10531
10532 let refused = refusal(result);
10533 assert_eq!(
10534 refused,
10535 HeldSignalRefusal {
10536 wait: None,
10537 held: None,
10538 replacing: Some(libc::SIGCHLD),
10539 loss: HeldSignalLoss::Replaced,
10540 },
10541 "deadline={deadline}"
10542 );
10543 let message = HeldSignalRefusal {
10544 wait: Some(Sysno::futex),
10545 ..refused
10546 }
10547 .to_string();
10548 assert!(
10549 message.starts_with(
10550 "unsupported: the futex wait cannot keep a held signal that could not be \
10551 identified: the pending SIGCHLD would stop the next injection and replace it"
10552 ),
10553 "deadline={deadline}: {message}"
10554 );
10555 assert!(wait.held_unidentified, "deadline={deadline}");
10556 let kernel = kernel.lock().unwrap();
10557 assert_eq!(
10558 kernel.requested.len(),
10559 1,
10560 "deadline={deadline}: the second injection is not made"
10561 );
10562 assert_eq!(kernel.taken, vec![libc::SIGCHLD], "deadline={deadline}");
10563 assert_eq!(kernel.held_slot, Some(libc::SIGCHLD), "deadline={deadline}");
10564 assert!(kernel.lost.is_empty(), "deadline={deadline}");
10565 assert_eq!(kernel.pending, chld, "deadline={deadline}");
10566 }
10567 }
10568
10569 #[tokio::test]
10577 async fn a_timed_wait_refuses_to_let_another_stop_replace_a_held_sigchld() {
10578 let (mut guest, kernel) = stopping_guest(
10579 kernel_sigset_bit(libc::SIGCHLD) | kernel_sigset_bit(libc::SIGWINCH),
10580 0,
10581 );
10582 let _proc = scripted_proc(&kernel);
10583 let mut wait =
10584 KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND).with_deadline(true);
10585
10586 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10587
10588 assert_eq!(
10589 refusal(result),
10590 HeldSignalRefusal {
10591 wait: None,
10592 held: Some(libc::SIGCHLD),
10593 replacing: Some(libc::SIGWINCH),
10594 loss: HeldSignalLoss::Replaced,
10595 }
10596 );
10597 let kernel = kernel.lock().unwrap();
10598 assert_eq!(kernel.taken, vec![libc::SIGCHLD]);
10599 assert_eq!(
10600 kernel.requested.len(),
10601 1,
10602 "the second injection is not made"
10603 );
10604 assert_eq!(kernel.pending, kernel_sigset_bit(libc::SIGWINCH));
10605 }
10606
10607 #[tokio::test]
10611 async fn a_timed_wait_ends_for_a_held_signal_that_would_end_it() {
10612 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10613 let (mut guest, kernel) = stopping_guest(usr1 | kernel_sigset_bit(libc::SIGWINCH), 0);
10614 let _proc = scripted_proc(&kernel);
10615 let mut wait =
10616 KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND).with_deadline(true);
10617 wait.held = Some(HeldSignal {
10618 signal: libc::SIGUSR1,
10619 queue: SignalQueue::Thread,
10620 kind: HeldKind::Precious,
10621 });
10622 kernel.lock().unwrap().pending = kernel_sigset_bit(libc::SIGWINCH);
10623 kernel.lock().unwrap().caught = usr1;
10624
10625 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10626
10627 assert!(
10628 matches!(result, Err(Error::Errno(Errno::ERESTARTNOHAND))),
10629 "{result:?}"
10630 );
10631 assert!(
10632 kernel.lock().unwrap().requested.is_empty(),
10633 "nothing is injected"
10634 );
10635 }
10636
10637 #[tokio::test]
10643 async fn a_wait_without_a_deadline_ends_for_a_held_signal_that_would_end_it() {
10644 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10645 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(libc::SIGWINCH), 0);
10646 kernel.lock().unwrap().caught = usr1;
10647 let _proc = scripted_proc(&kernel);
10648 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10649 wait.held = Some(HeldSignal {
10650 signal: libc::SIGUSR1,
10651 queue: SignalQueue::Thread,
10652 kind: HeldKind::Precious,
10653 });
10654
10655 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10656
10657 assert!(
10658 matches!(result, Err(Error::Errno(Errno::ERESTARTSYS))),
10659 "{result:?}"
10660 );
10661 assert!(
10662 kernel.lock().unwrap().requested.is_empty(),
10663 "nothing is injected"
10664 );
10665 }
10666
10667 #[tokio::test]
10674 async fn a_consumed_sigchld_that_stops_an_injection_is_refused() {
10675 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
10676 let (mut guest, kernel) = stopping_guest(usr1, 0);
10677 let _proc = scripted_proc(&kernel);
10678 let mut wait = KernelSignalWait::new(&guest, usr1, false, Errno::ERESTARTSYS);
10679 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10680 assert!(
10681 matches!(result, Err(Error::Errno(Errno::ERESTARTNOINTR))),
10682 "{result:?}"
10683 );
10684 assert_eq!(kernel.lock().unwrap().taken, vec![libc::SIGUSR1]);
10685 drop(_proc);
10686
10687 let chld = kernel_sigset_bit(libc::SIGCHLD);
10688 let (mut guest, kernel) = stopping_guest(chld, 0);
10689 let _proc = scripted_proc(&kernel);
10690 let mut wait = KernelSignalWait::new(&guest, chld, false, Errno::ERESTARTSYS);
10691 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10692 assert_eq!(
10693 refusal(result),
10694 HeldSignalRefusal {
10695 wait: None,
10696 held: None,
10697 replacing: Some(libc::SIGCHLD),
10698 loss: HeldSignalLoss::Consumed,
10699 }
10700 );
10701 assert_eq!(kernel.lock().unwrap().taken, vec![libc::SIGCHLD]);
10702 }
10703
10704 #[tokio::test]
10709 async fn a_stop_by_the_preemption_signal_still_restarts_a_wait_without_a_deadline() {
10710 let signal = reverie::PERF_EVENT_SIGNAL as i32;
10711 let (mut guest, kernel) = stopping_guest(kernel_sigset_bit(signal), 0);
10712 let _proc = scripted_proc(&kernel);
10713 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS);
10714
10715 let result = inject_mask_absorbing(&mut wait, &mut guest, guest_mask()).await;
10716
10717 assert!(
10718 matches!(result, Err(Error::Errno(Errno::ERESTARTNOINTR))),
10719 "{result:?}"
10720 );
10721 assert_eq!(kernel.lock().unwrap().taken, vec![signal]);
10722 }
10723
10724 #[tokio::test]
10732 async fn a_held_signal_refusal_follows_the_unsupported_operation_policy() {
10733 let refused = || {
10734 Err::<i64, Error>(Error::Tool(anyhow::Error::new(HeldSignalRefusal {
10735 wait: None,
10736 held: Some(libc::SIGCHLD),
10737 replacing: Some(libc::SIGWINCH),
10738 loss: HeldSignalLoss::Replaced,
10739 })))
10740 };
10741 let (mut guest, _kernel) = WaitGuest::live(guest_mask());
10742 guest.config.panic_on_unsupported_syscalls = false;
10743 let result = refuse_held_signal_loss(&mut guest, Sysno::poll, refused()).await;
10744 assert!(
10745 matches!(result, Err(Error::Errno(Errno::ERESTARTNOINTR))),
10746 "{result:?}"
10747 );
10748 let result = refuse_held_signal_loss(&mut guest, Sysno::poll, Ok(3)).await;
10749 assert!(matches!(result, Ok(3)), "{result:?}");
10750 let result =
10751 refuse_held_signal_loss(&mut guest, Sysno::poll, Err(Errno::ERESTARTSYS.into())).await;
10752 assert!(
10753 matches!(result, Err(Error::Errno(Errno::ERESTARTSYS))),
10754 "{result:?}"
10755 );
10756
10757 guest.config.panic_on_unsupported_syscalls = true;
10758 guest.config.exit_on_unsupported_syscall = true;
10759 guest.config.shutdown_on_unsupported_syscall = false;
10760 let result = refuse_held_signal_loss(&mut guest, Sysno::poll, refused()).await;
10761 match result {
10762 Err(Error::Tool(error)) => assert!(
10763 matches!(
10764 error.downcast_ref::<crate::UnsupportedSyscallError>(),
10765 Some(crate::UnsupportedSyscallError(Sysno::poll))
10766 ),
10767 "{error:#}"
10768 ),
10769 other => panic!("expected the typed refusal, got {other:?}"),
10770 }
10771
10772 let message = HeldSignalRefusal {
10773 wait: Some(Sysno::poll),
10774 held: Some(libc::SIGCHLD),
10775 replacing: Some(libc::SIGWINCH),
10776 loss: HeldSignalLoss::Replaced,
10777 }
10778 .to_string();
10779 for part in [
10780 "poll",
10781 "SIGCHLD",
10782 "SIGWINCH",
10783 "https://github.com/rrnewton/hermit/pull/3361",
10784 ] {
10785 assert!(message.contains(part), "{part} missing from: {message}");
10786 }
10787 }
10788
10789 fn caught_sigchld_guest(gated_backend: bool) -> (WaitGuest, Kernel) {
10794 let (mut guest, kernel) = WaitGuest::live(guest_mask());
10795 guest.config.sequentialize_threads = gated_backend;
10796 detcore_model::config::set_kernel_signal_facts(&mut guest.config.backend, gated_backend);
10797 {
10798 let mut kernel = kernel.lock().unwrap();
10799 kernel.pending = kernel_sigset_bit(libc::SIGCHLD);
10800 kernel.caught = kernel_sigset_bit(libc::SIGCHLD);
10801 }
10802 (guest, kernel)
10803 }
10804
10805 #[tokio::test]
10815 async fn a_pending_caught_sigchld_ends_a_select_wait_but_no_gated_wait() {
10816 for gated_backend in [false, true] {
10817 let (guest, kernel) = caught_sigchld_guest(gated_backend);
10818 let _proc = scripted_proc(&kernel);
10819
10820 let gated = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10821 assert!(
10822 !gated.interrupted().unwrap(),
10823 "a pending caught SIGCHLD does not end a gated wait \
10824 (gated_backend={gated_backend})"
10825 );
10826
10827 let select = KernelSignalWait::for_select(&guest);
10828 assert!(
10829 select.interrupted().unwrap(),
10830 "the pending caught SIGCHLD ends a select wait (gated_backend={gated_backend})"
10831 );
10832 assert_eq!(
10833 kernel.lock().unwrap().pending,
10834 kernel_sigset_bit(libc::SIGCHLD),
10835 "the signal stays queued for the guest's handler"
10836 );
10837 }
10838 }
10839
10840 #[tokio::test]
10846 async fn a_committed_sigchld_ends_a_gated_wait_unless_a_host_timed_source_can_post_it() {
10847 let chld = kernel_sigset_bit(libc::SIGCHLD);
10848 let (guest, kernel) = caught_sigchld_guest(true);
10849 let _proc = scripted_proc(&kernel);
10850
10851 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10852 assert!(
10853 !wait.interrupted().unwrap(),
10854 "an uncommitted SIGCHLD is held"
10855 );
10856 wait.admit(chld, 0);
10857 assert!(
10858 wait.interrupted().unwrap(),
10859 "a committed SIGCHLD ends the wait"
10860 );
10861 wait.admit(0, 0);
10862 assert!(
10863 !wait.interrupted().unwrap(),
10864 "the next turn's resume names nothing, so the SIGCHLD is held again"
10865 );
10866
10867 let mut host_timed = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10868 host_timed.hold_until_return(chld);
10869 host_timed.admit(chld, 0);
10870 assert!(
10871 !host_timed.interrupted().unwrap(),
10872 "a SIGCHLD a host-timed source can post stays held although committed"
10873 );
10874 }
10875
10876 #[tokio::test]
10884 async fn an_admitted_private_sigchld_ends_a_gated_wait_from_the_threads_own_queue_only() {
10885 let chld = kernel_sigset_bit(libc::SIGCHLD);
10886 let (guest, kernel) = caught_sigchld_guest(true);
10887 let _proc = scripted_proc(&kernel);
10888
10889 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10890 wait.admit(0, chld);
10891 assert!(
10892 wait.interrupted().unwrap(),
10893 "the thread's own copy ends the wait"
10894 );
10895 assert!(
10896 wait.interrupts_only_by_committed(
10897 &read_wait_signal_state(guest.pid(), guest.tid()).unwrap()
10898 ),
10899 "only the admitted copy ends it, so the turn's probe runs first"
10900 );
10901 kernel.lock().unwrap().shared = chld;
10902 assert!(
10903 !wait.interrupted().unwrap(),
10904 "a SIGCHLD pending only on the shared queue is held"
10905 );
10906 kernel.lock().unwrap().shared = 0;
10907 wait.admit(0, 0);
10908 assert!(!wait.interrupted().unwrap(), "the next turn holds it again");
10909
10910 let mut host_timed = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10911 host_timed.hold_until_return(chld);
10912 host_timed.admit(0, chld);
10913 assert!(
10914 !host_timed.interrupted().unwrap(),
10915 "a SIGCHLD a host-timed source can post stays held"
10916 );
10917 }
10918
10919 #[tokio::test]
10924 async fn an_admitted_private_sigchld_that_only_the_wait_mask_lets_in_is_delivered_under_it() {
10925 let chld = kernel_sigset_bit(libc::SIGCHLD);
10926 let (guest, kernel) = caught_sigchld_guest(true);
10927 let _proc = scripted_proc(&kernel);
10928 kernel.lock().unwrap().blocked |= chld;
10929 let mut wait =
10930 KernelSignalWait::new(&guest, 0, false, Errno::EINTR).with_wait_mask(Some(0));
10931 let state = read_wait_signal_state(guest.pid(), guest.tid()).unwrap();
10932 assert!(
10933 !wait.interrupts_only_under_wait_mask(&state),
10934 "held while not admitted"
10935 );
10936 wait.admit(0, chld);
10937 assert!(wait.interrupts_only_under_wait_mask(&state));
10938 }
10939
10940 #[tokio::test]
10948 async fn a_recorded_private_sigchld_is_kept_only_under_a_block_in_place() {
10949 let chld = kernel_sigset_bit(libc::SIGCHLD);
10950 let mut published = PublishedWaitMask(Some(guest_mask()));
10953
10954 let (mut guest, kernel) = caught_sigchld_guest(true);
10955 let _proc = scripted_proc(&kernel);
10956 kernel.lock().unwrap().setter.push_back(None);
10957 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10958 assert_eq!(
10959 wait.admit_under_block(&mut guest, 0, chld, 0, &mut published)
10960 .await
10961 .unwrap(),
10962 chld,
10963 "blocked in place, and the thread's own queue holds it"
10964 );
10965 assert!(wait.blocked_in_place);
10966 wait.restore(&mut guest, None).await.unwrap();
10967 drop(_proc);
10968
10969 let (mut guest, kernel) = caught_sigchld_guest(true);
10970 let _proc = scripted_proc(&kernel);
10971 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10972 assert_eq!(
10973 wait.admit_under_block(&mut guest, 0, 0, 0, &mut published)
10974 .await
10975 .unwrap(),
10976 0
10977 );
10978 assert!(
10979 kernel.lock().unwrap().set_calls.is_empty(),
10980 "nothing admitted, no request"
10981 );
10982 assert_eq!(
10983 wait.admit_under_block(&mut guest, 0, chld, chld, &mut published)
10984 .await
10985 .unwrap(),
10986 0,
10987 "a SIGCHLD a host-timed source can post is not admitted"
10988 );
10989 assert!(kernel.lock().unwrap().set_calls.is_empty());
10990 drop(_proc);
10991
10992 let (mut guest, kernel) = caught_sigchld_guest(true);
10993 let _proc = scripted_proc(&kernel);
10994 kernel.lock().unwrap().shared = chld;
10995 kernel.lock().unwrap().setter.push_back(None);
10996 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
10997 assert_eq!(
10998 wait.admit_under_block(&mut guest, 0, chld, 0, &mut published)
10999 .await
11000 .unwrap(),
11001 0,
11002 "only on the shared queue: not the recorded copy"
11003 );
11004 wait.restore(&mut guest, None).await.unwrap();
11005 drop(_proc);
11006
11007 let (mut guest, kernel) = caught_sigchld_guest(true);
11008 let _proc = scripted_proc(&kernel);
11009 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
11010 assert_eq!(
11011 wait.admit_under_block(&mut guest, 0, chld, 0, &mut published)
11012 .await
11013 .unwrap(),
11014 0,
11015 "the setter refuses (no answer queued)"
11016 );
11017 assert!(
11018 wait.needs_block(),
11019 "the injected block after the first probe stays"
11020 );
11021 drop(_proc);
11022
11023 let (mut guest, kernel) = caught_sigchld_guest(true);
11024 let _proc = scripted_proc(&kernel);
11025 let mut wait = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
11026 wait.unblockable = true;
11027 assert_eq!(
11028 wait.admit_under_block(&mut guest, 0, chld, 0, &mut published)
11029 .await
11030 .unwrap(),
11031 0,
11032 "an earlier injected block failed: no block, no private copy"
11033 );
11034 assert!(kernel.lock().unwrap().set_calls.is_empty());
11035 }
11036
11037 #[tokio::test]
11045 async fn a_signal_after_an_empty_pipe_turn_snapshot_still_interrupts() {
11046 let usr1 = kernel_sigset_bit(libc::SIGUSR1);
11047 for (sent, blocked) in [(true, false), (false, false), (true, true)] {
11048 let own_mask = guest_mask() | if blocked { usr1 } else { 0 };
11049 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(1), own_mask);
11050 {
11051 let mut kernel = kernel.lock().unwrap();
11052 kernel.caught = usr1;
11053 kernel.pending_repost = if sent { usr1 } else { 0 };
11054 kernel.stops_for_pending = true;
11055 kernel.empty_read_probes = Some(0);
11056 kernel.private_record = Some((0, 0));
11057 kernel.setter.extend([None, None]);
11058 }
11059 let _proc = scripted_proc(&kernel);
11060 let mut wait = Some(KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS));
11061 let mut published = PublishedWaitMask(Some(own_mask));
11062 let read = syscalls::Read::new()
11063 .with_fd(3)
11064 .with_buf(AddrMut::from_raw(guest.arena.as_mut_ptr() as usize))
11065 .with_len(1);
11066 let first = turn_signals_with_block(
11067 &mut guest,
11068 &mut wait,
11069 ProbeMaskPolicy::PrivateProbe,
11070 0,
11071 false,
11072 &mut published,
11073 )
11074 .await
11075 .unwrap();
11076 assert!(!first.ends(false), "the snapshot preceded the signal");
11077 assert_eq!(
11078 guest.inject_with_retry(read).await,
11079 Err(Errno::EAGAIN),
11080 "an empty nonblocking read transfers no bytes"
11081 );
11082 let next = turn_signals_with_block(
11083 &mut guest,
11084 &mut wait,
11085 ProbeMaskPolicy::PrivateProbe,
11086 0,
11087 false,
11088 &mut published,
11089 )
11090 .await
11091 .unwrap();
11092 wait.as_mut()
11093 .unwrap()
11094 .restore(&mut guest, None)
11095 .await
11096 .unwrap();
11097 let kernel = kernel.lock().unwrap();
11098 assert_eq!(kernel.blocked, own_mask, "the guest's mask is restored");
11099 assert_eq!(kernel.empty_read_probes, Some(1));
11100 assert_eq!(
11101 next.ends(false),
11102 sent && !blocked,
11103 "sent={sent}, blocked={blocked}, pending={:#x}, held={:?}, taken={:?}",
11104 kernel.pending,
11105 kernel.held_slot,
11106 kernel.taken
11107 );
11108 assert_eq!(kernel.pending, if sent { usr1 } else { 0 });
11109 assert_eq!(kernel.held_slot, None, "the signal stays kernel-pending");
11110 }
11111 }
11112
11113 #[tokio::test]
11121 async fn a_pipe_turn_counts_a_recorded_private_sigchld_only_under_a_block_in_place() {
11122 let chld = kernel_sigset_bit(libc::SIGCHLD);
11123 let turn = |may_block: bool, gate: bool, shared: bool| async move {
11124 let (mut guest, kernel) = WaitGuest::new(Pid::from_raw(1), guest_mask());
11126 {
11127 let mut kernel = kernel.lock().unwrap();
11128 kernel.pending = chld;
11129 kernel.caught = chld;
11130 kernel.shared = if shared { chld } else { 0 };
11131 kernel.setter.push_back(None);
11132 kernel.private_record = Some((0, chld));
11133 }
11134 let _proc = scripted_proc(&kernel);
11135 let mut wait = Some(KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTSYS));
11136 let mut published = PublishedWaitMask(Some(guest_mask()));
11137 let pending = turn_signals_with_block(
11138 &mut guest,
11139 &mut wait,
11140 if may_block {
11141 ProbeMaskPolicy::PendingSignals
11142 } else {
11143 ProbeMaskPolicy::Guest
11144 },
11145 0,
11146 gate,
11147 &mut published,
11148 )
11149 .await
11150 .unwrap()
11151 .pending;
11152 if let Some(wait) = wait.as_mut() {
11153 wait.restore(&mut guest, None).await.unwrap();
11154 }
11155 let blocked_in_place = !kernel.lock().unwrap().set_calls.is_empty();
11156 (pending & chld, blocked_in_place)
11157 };
11158 assert_eq!(
11159 turn(true, true, false).await,
11160 (chld, true),
11161 "blocked in place: admitted"
11162 );
11163 assert_eq!(
11164 turn(false, true, false).await,
11165 (0, false),
11166 "the writer's first attempt blocks nothing: held"
11167 );
11168 assert_eq!(turn(true, false, false).await, (0, true), "no gate: held");
11169 assert_eq!(
11170 turn(true, true, true).await,
11171 (0, true),
11172 "the shared queue alone: held"
11173 );
11174 }
11175
11176 #[test]
11180 fn a_pipe_turn_counts_an_admitted_private_sigchld() {
11181 let chld = kernel_sigset_bit(libc::SIGCHLD);
11182 let state = KernelSignalState {
11183 pending: chld,
11184 thread_pending: chld,
11185 caught: chld,
11186 ..KernelSignalState::default()
11187 };
11188 assert_eq!(
11189 turn_signals_before_discard(&state, 0, 0, 0, NamedRealTime::Woken, 0).pending,
11190 0,
11191 "held"
11192 );
11193 assert_eq!(
11194 turn_signals_before_discard(&state, 0, 0, 0, NamedRealTime::Woken, chld).pending,
11195 chld,
11196 "admitted"
11197 );
11198 }
11199
11200 #[tokio::test]
11207 async fn a_pending_caught_sigchld_ends_a_select_wait_before_the_injection() {
11208 let (mut guest, kernel) = caught_sigchld_guest(true);
11209 kernel.lock().unwrap().stops_for_pending = true;
11210 let _proc = scripted_proc(&kernel);
11211
11212 let mut select = KernelSignalWait::for_select(&guest);
11213 let result = inject_mask_absorbing(&mut select, &mut guest, guest_mask()).await;
11214 assert!(
11215 matches!(result, Err(Error::Errno(Errno::ERESTARTNOHAND))),
11216 "{result:?}"
11217 );
11218 {
11219 let kernel = kernel.lock().unwrap();
11220 assert!(kernel.requested.is_empty(), "nothing is injected");
11221 assert_eq!(kernel.pending, kernel_sigset_bit(libc::SIGCHLD));
11222 }
11223 assert_eq!(select.held, None);
11224
11225 let mut gated = KernelSignalWait::new(&guest, 0, false, Errno::ERESTARTNOHAND);
11226 let result = inject_mask_absorbing(&mut gated, &mut guest, guest_mask()).await;
11227 assert!(matches!(result, Ok(Ok(0))), "{result:?}");
11228 {
11229 let kernel = kernel.lock().unwrap();
11230 assert_eq!(kernel.taken, vec![libc::SIGCHLD]);
11231 assert_eq!(kernel.requested.len(), 2, "stopped once, then ran");
11232 }
11233 assert_eq!(gated.held.map(|held| held.kind), Some(HeldKind::Precious));
11234 }
11235
11236 #[test]
11239 fn next_dequeued_follows_the_kernel_order() {
11240 let bit = kernel_sigset_bit;
11241 let state = KernelSignalState {
11242 thread_pending: bit(libc::SIGUSR2) | bit(libc::SIGSEGV),
11243 shared_pending: bit(libc::SIGHUP),
11244 ..KernelSignalState::default()
11245 };
11246 assert_eq!(
11247 state.next_dequeued(0),
11248 Some((libc::SIGSEGV, SignalQueue::Thread))
11249 );
11250 assert_eq!(
11251 state.next_dequeued(bit(libc::SIGSEGV)),
11252 Some((libc::SIGUSR2, SignalQueue::Thread))
11253 );
11254 assert_eq!(
11255 state.next_dequeued(bit(libc::SIGSEGV) | bit(libc::SIGUSR2)),
11256 Some((libc::SIGHUP, SignalQueue::Shared))
11257 );
11258 assert_eq!(state.next_dequeued(!0), None);
11259 }
11260
11261 #[test]
11264 fn a_stop_is_identified_only_by_exactly_its_signal() {
11265 let bit = kernel_sigset_bit;
11266 let before = KernelSignalState {
11267 thread_pending: bit(libc::SIGUSR1) | bit(libc::SIGWINCH),
11268 shared_pending: bit(libc::SIGHUP),
11269 ..KernelSignalState::default()
11270 };
11271 let took = |after: KernelSignalState, signal, queue| {
11272 stop_took_only(&before, &after, signal, queue)
11273 };
11274 let after = KernelSignalState {
11275 thread_pending: bit(libc::SIGWINCH) | bit(libc::SIGUSR2),
11276 ..before
11277 };
11278 assert!(took(after, libc::SIGUSR1, SignalQueue::Thread));
11279 assert!(
11280 !took(after, libc::SIGWINCH, SignalQueue::Thread),
11281 "SIGWINCH is still queued"
11282 );
11283 let two_left = KernelSignalState {
11284 thread_pending: 0,
11285 ..before
11286 };
11287 assert!(!took(two_left, libc::SIGUSR1, SignalQueue::Thread));
11288 let shared_left = KernelSignalState {
11289 thread_pending: bit(libc::SIGWINCH),
11290 shared_pending: 0,
11291 ..before
11292 };
11293 assert!(!took(shared_left, libc::SIGUSR1, SignalQueue::Thread));
11294 let mask_changed = KernelSignalState {
11295 blocked: bit(libc::SIGUSR2),
11296 ..after
11297 };
11298 assert!(!took(mask_changed, libc::SIGUSR1, SignalQueue::Thread));
11299 let disposition_changed = KernelSignalState {
11300 ignored: bit(libc::SIGWINCH),
11301 ..after
11302 };
11303 assert!(!took(
11304 disposition_changed,
11305 libc::SIGUSR1,
11306 SignalQueue::Thread
11307 ));
11308 }
11309
11310 #[test]
11318 fn a_signal_the_temporary_mask_blocks_is_held_not_ended() {
11319 let bit = kernel_sigset_bit;
11320 let (guest, _kernel) = WaitGuest::live(guest_mask());
11321 let before = KernelSignalState {
11322 pending: bit(libc::SIGALRM),
11323 thread_pending: bit(libc::SIGALRM),
11324 blocked: guest_mask(),
11325 caught: bit(libc::SIGALRM) | bit(libc::SIGUSR2),
11326 ..KernelSignalState::default()
11327 };
11328 let plain = KernelSignalWait::for_select(&guest);
11329 assert_eq!(
11330 plain.stop_verdict(&before, libc::SIGALRM),
11331 StopVerdict::End(Errno::ERESTARTNOHAND)
11332 );
11333 let masked = KernelSignalWait::for_select(&guest).with_wait_mask(Some(bit(libc::SIGALRM)));
11334 assert_eq!(
11335 masked.stop_verdict(&before, libc::SIGALRM),
11336 StopVerdict::Absorb(HeldKind::Precious)
11337 );
11338 let unblocking = KernelSignalWait::for_select(&guest).with_wait_mask(Some(0));
11339 assert_eq!(
11340 unblocking.stop_verdict(&before, libc::SIGUSR2),
11341 StopVerdict::End(Errno::ERESTARTNOHAND)
11342 );
11343 }
11344}
11345
11346#[cfg(test)]
11347mod turn_signal_tests {
11348 use super::*;
11349
11350 fn bit(signal: libc::c_int) -> KernelSigset {
11351 kernel_sigset_bit(signal)
11352 }
11353
11354 fn state(
11355 pending: KernelSigset,
11356 caught: KernelSigset,
11357 blocked: KernelSigset,
11358 ) -> KernelSignalState {
11359 KernelSignalState {
11360 pending,
11361 thread_pending: pending,
11362 caught,
11363 blocked,
11364 ..Default::default()
11365 }
11366 }
11367
11368 #[test]
11372 fn a_named_wake_ends_a_call_only_after_progress() {
11373 let named = TurnSignals {
11374 named: bit(libc::SIGUSR1),
11375 pending: 0,
11376 };
11377 assert!(!named.ends(false));
11378 assert!(named.ends(true));
11379 let pending = TurnSignals {
11380 named: 0,
11381 pending: bit(libc::SIGUSR1),
11382 };
11383 assert!(pending.ends(false));
11384 assert!(pending.ends(true));
11385 assert!(!TurnSignals::default().ends(true));
11386 }
11387
11388 #[test]
11391 fn the_hold_leaves_out_an_uncommitted_sigchld_and_every_host_timed_signal() {
11392 let chld = bit(libc::SIGCHLD);
11393 let hup = bit(libc::SIGHUP);
11394 let s = state(chld | hup, chld | hup, 0);
11395 assert_eq!(
11396 turn_signals_before_discard(&s, 0, hup, 0, NamedRealTime::Woken, 0).pending,
11397 0
11398 );
11399 assert_eq!(
11400 turn_signals_before_discard(&s, 0, hup, chld, NamedRealTime::Woken, 0).pending,
11401 chld
11402 );
11403 assert_eq!(
11404 turn_signals_before_discard(&s, 0, hup, chld | hup, NamedRealTime::Woken, 0).pending,
11405 chld
11406 );
11407 }
11408
11409 #[test]
11416 fn a_named_signal_counts_only_unblocked_and_a_real_time_one_by_its_call() {
11417 use NamedRealTime::ByDisposition;
11418 use NamedRealTime::Woken;
11419 let usr1 = bit(libc::SIGUSR1);
11420 let rt = bit(libc::SIGRTMIN());
11421 let s = state(0, usr1, usr1);
11422 for real_time in [Woken, ByDisposition] {
11423 assert_eq!(
11424 turn_signals_before_discard(&s, usr1, 0, usr1, real_time, 0).named,
11425 0
11426 );
11427 assert_eq!(
11428 turn_signals_before_discard(&s, 0, 0, usr1, real_time, 0).named,
11429 usr1
11430 );
11431 }
11432 let ignored_rt = KernelSignalState {
11433 ignored: rt,
11434 ..state(0, 0, 0)
11435 };
11436 assert_eq!(
11437 turn_signals_before_discard(&ignored_rt, 0, 0, rt, ByDisposition, 0).named,
11438 0
11439 );
11440 assert_eq!(
11441 turn_signals_before_discard(&ignored_rt, 0, 0, rt, Woken, 0).named,
11442 rt
11443 );
11444 assert_eq!(
11445 turn_signals_before_discard(&ignored_rt, rt, 0, rt, Woken, 0).named,
11446 0
11447 );
11448 let caught_rt = state(0, rt, 0);
11449 assert_eq!(
11450 turn_signals_before_discard(&caught_rt, 0, 0, rt, ByDisposition, 0).named,
11451 rt
11452 );
11453 }
11454}
11455
11456#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11459pub(crate) enum ContinuationMask {
11460 Temporary,
11462 Unknown,
11464 Restored,
11469}
11470
11471pub(crate) fn continuation_mask(call: Syscall, result: &Result<i64, Error>) -> ContinuationMask {
11495 let ended_by = |errno: Errno| matches!(result, Err(Error::Errno(error)) if *error == errno);
11496 let kept = match call {
11497 Syscall::RtSigsuspend(_) => return ContinuationMask::Unknown,
11498 Syscall::Pselect6(_) | Syscall::Ppoll(_) => {
11499 ended_by(Errno::ERESTARTNOHAND) || ended_by(Errno::EINTR)
11500 }
11501 Syscall::EpollPwait(_) | Syscall::Other(Sysno::epoll_pwait2, _) => ended_by(Errno::EINTR),
11502 _ => false,
11503 };
11504 if kept {
11505 ContinuationMask::Temporary
11506 } else {
11507 ContinuationMask::Restored
11508 }
11509}
11510
11511#[cfg(test)]
11512mod temporary_mask_tests {
11513 use super::*;
11514
11515 #[test]
11524 fn the_continuation_mask_follows_what_the_kernel_did() {
11525 let restart = Err(Error::Errno(Errno::ERESTARTNOHAND));
11526 let eintr = Err(Error::Errno(Errno::EINTR));
11527 let pselect = Syscall::Pselect6(syscalls::Pselect6::new());
11528 let ppoll = Syscall::Ppoll(syscalls::Ppoll::new());
11529 let epoll = Syscall::EpollPwait(syscalls::EpollPwait::new());
11530 let epoll2 = Syscall::Other(
11531 Sysno::epoll_pwait2,
11532 reverie::syscalls::SyscallArgs::new(0, 0, 0, 0, 0, 0),
11533 );
11534 let before_entry = Err(Error::Errno(Errno::ERESTARTSYS));
11535 let sigsuspend = Syscall::RtSigsuspend(syscalls::RtSigsuspend::new());
11536 let read = Syscall::Read(syscalls::Read::new());
11537 let ebadf = Err(Error::Errno(Errno::EBADF));
11538 use ContinuationMask::*;
11539 for call in [pselect, ppoll] {
11540 assert_eq!(continuation_mask(call, &restart), Temporary, "{call:?}");
11541 assert_eq!(continuation_mask(call, &eintr), Temporary, "{call:?}");
11542 assert_eq!(continuation_mask(call, &before_entry), Restored, "{call:?}");
11543 assert_eq!(continuation_mask(call, &ebadf), Restored, "{call:?}");
11544 assert_eq!(continuation_mask(call, &Ok(1)), Restored, "{call:?}");
11545 }
11546 for result in [&restart, &eintr, &before_entry, &Ok(0)] {
11547 assert_eq!(continuation_mask(sigsuspend, result), Unknown, "{result:?}");
11548 }
11549 for call in [epoll, epoll2] {
11550 assert_eq!(continuation_mask(call, &eintr), Temporary, "{call:?}");
11551 assert_eq!(continuation_mask(call, &restart), Restored, "{call:?}");
11552 assert_eq!(continuation_mask(call, &before_entry), Restored, "{call:?}");
11553 assert_eq!(continuation_mask(call, &Ok(0)), Restored, "{call:?}");
11554 }
11555 assert_eq!(continuation_mask(read, &restart), Restored);
11556 }
11557}
11558
11559#[cfg(test)]
11560mod tool_decided_injection_tests {
11561 use super::*;
11562
11563 fn refusal(operation: reverie::UnsupportedOperation) -> Option<reverie::UnsupportedRefusal> {
11564 Some(reverie::UnsupportedRefusal::new(
11565 operation,
11566 Errno::ENOSYS,
11567 "KVM leaves this call to the Tool",
11568 ))
11569 }
11570
11571 #[test]
11575 fn only_a_tool_decided_refusal_is_an_internal_failure() {
11576 let dettid = DetTid::from_raw(7);
11577 let failure = tool_decided_injection_failure(
11578 refusal(reverie::UnsupportedOperation::ToolDecided),
11579 dettid,
11580 Sysno::getpgrp,
11581 )
11582 .expect("a ToolDecided refusal is an internal failure");
11583 assert!(
11584 failure.starts_with(
11585 "HERMIT_INTERNAL_FAILURE class=tool-decided-injection dtid=7 syscall=getpgrp"
11586 ),
11587 "{failure}"
11588 );
11589 assert!(
11590 failure.contains("KVM leaves this call to the Tool"),
11591 "{failure}"
11592 );
11593 assert_eq!(
11594 tool_decided_injection_failure(None, dettid, Sysno::getpgrp),
11595 None
11596 );
11597 for operation in [
11598 reverie::UnsupportedOperation::Flock,
11599 reverie::UnsupportedOperation::ProcessGroups,
11600 reverie::UnsupportedOperation::Signal,
11601 reverie::UnsupportedOperation::NonLeaderExec,
11602 ] {
11603 assert_eq!(
11604 tool_decided_injection_failure(refusal(operation), dettid, Sysno::getpgrp),
11605 None,
11606 "{operation:?}"
11607 );
11608 }
11609 }
11610}
11611
11612#[cfg(test)]
11613mod backend_refusal_tests {
11614 use reverie::Pid;
11615 use reverie::Tool;
11616
11617 use super::*;
11618 use crate::config::Config;
11619 use crate::record_or_replay::NoopTool;
11620 use crate::syscall_hot_path_tests::InjectGuest;
11621
11622 const DIAGNOSTIC: &str = "this backend does not implement the call";
11623
11624 fn fail_closed(
11628 refusal: Option<reverie::UnsupportedRefusal>,
11629 ) -> (Detcore<NoopTool>, InjectGuest<NoopTool>) {
11630 let config = Config {
11631 panic_on_unsupported_syscalls: true,
11632 exit_on_unsupported_syscall: true,
11633 shutdown_on_unsupported_syscall: false,
11634 ..Config::default()
11635 };
11636 let tool = <Detcore<NoopTool> as Tool>::new(Pid::from_raw(4242), &config);
11637 let mut guest = InjectGuest::<NoopTool>::new(Ok(0));
11638 guest.config = config;
11639 if let Some(refusal) = refusal {
11640 guest = guest.with_refusal(refusal);
11641 }
11642 (tool, guest)
11643 }
11644
11645 fn refusal(operation: reverie::UnsupportedOperation) -> reverie::UnsupportedRefusal {
11646 reverie::UnsupportedRefusal::new(operation, Errno::ENOSYS, DIAGNOSTIC)
11647 }
11648
11649 const CALLS: [(Sysno, reverie::UnsupportedOperation); 4] = [
11650 (
11651 Sysno::pkey_alloc,
11652 reverie::UnsupportedOperation::ProtectionKeys,
11653 ),
11654 (
11655 Sysno::pkey_mprotect,
11656 reverie::UnsupportedOperation::ProtectionKeys,
11657 ),
11658 (
11659 Sysno::pkey_free,
11660 reverie::UnsupportedOperation::ProtectionKeys,
11661 ),
11662 (
11663 Sysno::userfaultfd,
11664 reverie::UnsupportedOperation::Userfaultfd,
11665 ),
11666 ];
11667
11668 #[test]
11672 fn the_calls_own_refusal_is_refused_by_policy() {
11673 futures::executor::block_on(async {
11674 for (sysno, operation) in CALLS {
11675 let (tool, mut guest) = fail_closed(Some(refusal(operation)));
11676 let result = tool
11677 .report_backend_refusal(&mut guest, sysno, operation, Err(Errno::ENOSYS.into()))
11678 .await;
11679 match result {
11680 Err(Error::Tool(error)) => assert_eq!(
11681 error
11682 .downcast_ref::<crate::UnsupportedSyscallError>()
11683 .expect("the policy's named refusal")
11684 .0,
11685 sysno
11686 ),
11687 other => panic!("{sysno}: expected the typed refusal, got {other:?}"),
11688 }
11689 assert!(guest.thread_state().stats.syscall_refused, "{sysno}");
11690 assert!(
11691 guest.injected().is_empty(),
11692 "{sysno}: nothing more is injected"
11693 );
11694 }
11695 })
11696 }
11697
11698 #[test]
11702 fn anything_else_keeps_the_calls_result() {
11703 futures::executor::block_on(async {
11704 for (sysno, operation) in CALLS {
11705 let (tool, mut guest) = fail_closed(Some(refusal(operation)));
11706 let result = tool
11707 .report_backend_refusal(&mut guest, sysno, operation, Ok(3))
11708 .await;
11709 assert!(matches!(result, Ok(3)), "{sysno}: {result:?}");
11710 assert!(
11711 guest.take_unsupported_refusal().is_some(),
11712 "{sysno}: a success leaves the refusal in place"
11713 );
11714
11715 let (tool, mut guest) = fail_closed(None);
11716 let result = tool
11717 .report_backend_refusal(&mut guest, sysno, operation, Err(Errno::ENOSYS.into()))
11718 .await;
11719 assert!(
11720 matches!(result, Err(Error::Errno(Errno::ENOSYS))),
11721 "{sysno}: {result:?}"
11722 );
11723 assert!(!guest.thread_state().stats.syscall_refused, "{sysno}");
11724
11725 let other = if operation == reverie::UnsupportedOperation::Userfaultfd {
11726 reverie::UnsupportedOperation::ProtectionKeys
11727 } else {
11728 reverie::UnsupportedOperation::Userfaultfd
11729 };
11730 for (backend, errno) in [
11731 (refusal(other), Errno::ENOSYS),
11732 (
11733 reverie::UnsupportedRefusal::new(operation, Errno::EINVAL, DIAGNOSTIC),
11734 Errno::ENOSYS,
11735 ),
11736 ] {
11737 let (tool, mut guest) = fail_closed(Some(backend));
11738 let result = tool
11739 .report_backend_refusal(&mut guest, sysno, operation, Err(errno.into()))
11740 .await;
11741 assert!(
11742 matches!(result, Err(Error::Errno(Errno::ENOSYS))),
11743 "{sysno}: {result:?}"
11744 );
11745 assert!(guest.thread_state().stats.syscall_refused, "{sysno}");
11747 }
11748 }
11749 })
11750 }
11751}