Skip to content

Commit 7b4802d

Browse files
committed
fix EPOLL_CTL_MOD
1 parent 4412a0a commit 7b4802d

File tree

2 files changed

+46
-27
lines changed

2 files changed

+46
-27
lines changed

src/tools/miri/src/shims/unix/linux_like/epoll.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn range_for_id(id: FdId) -> std::ops::RangeInclusive<EpollEventKey> {
4444
}
4545

4646
/// EpollEventInstance contains information that will be returned by epoll_wait.
47-
#[derive(Debug)]
47+
#[derive(Debug, Default)]
4848
pub struct EpollEventInstance {
4949
/// Bitmask of event types that happened to the file description.
5050
events: u32,
@@ -54,12 +54,6 @@ pub struct EpollEventInstance {
5454
clock: VClock,
5555
}
5656

57-
impl EpollEventInstance {
58-
pub fn new(data: u64) -> EpollEventInstance {
59-
EpollEventInstance { events: 0, data, clock: Default::default() }
60-
}
61-
}
62-
6357
/// EpollEventInterest registers the file description information to an epoll
6458
/// instance during a successful `epoll_ctl` call. It also stores additional
6559
/// information needed to check and update readiness state for `epoll_wait`.
@@ -345,8 +339,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
345339
);
346340
}
347341

348-
// Add new interest to list.
342+
// Add new interest to list. Experiments show that we need to reset all state
343+
// on `EPOLL_CTL_MOD`, including the edge tracking.
349344
let epoll_key = (id, fd);
345+
let new_interest = EpollEventInterest { events, data, prev_events: 0 };
350346
let new_interest = if op == epoll_ctl_add {
351347
if interest_list.range(range_for_id(id)).next().is_none() {
352348
// This is the first time this FD got added to this epoll.
@@ -358,25 +354,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
358354
// We already had interest in this.
359355
return this.set_last_error_and_return_i32(LibcError("EEXIST"));
360356
}
361-
btree_map::Entry::Vacant(e) =>
362-
e.insert(EpollEventInterest { events, data, prev_events: 0 }),
357+
btree_map::Entry::Vacant(e) => e.insert(new_interest),
363358
}
364359
} else {
365360
// Modify the existing interest.
366361
let Some(interest) = interest_list.get_mut(&epoll_key) else {
367362
return this.set_last_error_and_return_i32(LibcError("ENOENT"));
368363
};
369-
interest.events = events;
370-
// FIXME what about the data?
364+
*interest = new_interest;
371365
interest
372366
};
373367

374368
// Deliver events for the new interest.
369+
let force_edge = true; // makes no difference since we reset `prev_events`
375370
send_ready_events_to_interests(
376371
this,
377372
&epfd,
378373
fd_ref.as_unix(this).get_epoll_ready_events()?.get_event_bitmask(this),
379-
/* force_edge */ false,
374+
force_edge,
380375
std::iter::once((&epoll_key, new_interest)),
381376
)?;
382377

@@ -588,7 +583,8 @@ fn send_ready_events_to_interests<'tcx, 'a>(
588583
ready_list.remove(&event_key);
589584
continue;
590585
}
591-
// Generate new instance, or update existing one.
586+
// Generate new instance, or update existing one. It is crucial that whe we are done,
587+
// if an interest exists in the ready list, then it matches the latest events and data!
592588
let instance = match ready_list.entry(event_key) {
593589
btree_map::Entry::Occupied(e) => e.into_mut(),
594590
btree_map::Entry::Vacant(e) => {
@@ -598,10 +594,11 @@ fn send_ready_events_to_interests<'tcx, 'a>(
598594
// prior entry to update; just skip it.
599595
continue;
600596
}
601-
e.insert(EpollEventInstance::new(interest.data))
597+
e.insert(EpollEventInstance::default())
602598
}
603599
};
604600
instance.events = flags;
601+
instance.data = interest.data;
605602
ecx.release_clock(|clock| {
606603
instance.clock.join(clock);
607604
})?;

src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ fn test_epoll_socketpair() {
105105

106106
// This test first registers a file description with a flag that does not lead to notification,
107107
// then EPOLL_CTL_MOD to add another flag that will lead to notification.
108+
// Also check that the new data value set via MOD is applied properly.
108109
fn test_epoll_ctl_mod() {
109110
// Create an epoll instance.
110111
let epfd = unsafe { libc::epoll_create1(0) };
@@ -115,28 +116,49 @@ fn test_epoll_ctl_mod() {
115116
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
116117
assert_eq!(res, 0);
117118

118-
// Register fd[1] with EPOLLIN|EPOLLET.
119-
let mut ev = libc::epoll_event {
120-
events: (libc::EPOLLIN | libc::EPOLLET) as _,
121-
u64: u64::try_from(fds[1]).unwrap(),
122-
};
119+
// Register fd[1] with EPOLLIN|EPOLLET, and data of "0".
120+
let mut ev = libc::epoll_event { events: (libc::EPOLLIN | libc::EPOLLET) as _, u64: 0 };
123121
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
124122
assert_eq!(res, 0);
125123

126124
// Check result from epoll_wait. No notification would be returned.
127125
check_epoll_wait::<8>(epfd, &[]);
128126

129-
// Use EPOLL_CTL_MOD to change to EPOLLOUT flag.
130-
let mut ev = libc::epoll_event {
131-
events: (libc::EPOLLOUT | libc::EPOLLET) as _,
132-
u64: u64::try_from(fds[1]).unwrap(),
133-
};
127+
// Use EPOLL_CTL_MOD to change to EPOLLOUT flag and data.
128+
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 1 };
134129
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
135130
assert_eq!(res, 0);
136131

137-
// Check result from epoll_wait. EPOLLOUT notification is expected.
132+
// Check result from epoll_wait. EPOLLOUT notification and new data is expected.
138133
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
139-
let expected_value = u64::try_from(fds[1]).unwrap();
134+
let expected_value = 1;
135+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
136+
137+
// Write to fds[1] and read from fds[0] to make the notification ready again
138+
// (relying on there always being an event when the buffer gets emptied).
139+
let data = "abc".as_bytes();
140+
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr().cast(), data.len()) };
141+
assert_eq!(res, 3);
142+
let mut buf = [0u8; 3];
143+
let res = unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len()) };
144+
assert_eq!(res, 3);
145+
146+
// Now that the event is already ready, change the "data" value.
147+
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 2 };
148+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
149+
assert_eq!(res, 0);
150+
151+
// Receive event, with latest data value.
152+
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
153+
let expected_value = 2;
154+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
155+
156+
// Do another update that changes nothing.
157+
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 2 };
158+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
159+
assert_eq!(res, 0);
160+
161+
// This re-triggers the event, even if it's the same flags as before.
140162
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
141163
}
142164

0 commit comments

Comments
 (0)