Skip to content

Commit 663506a

Browse files
authored
fix: only close fanotify events with a valid fd (#2399)
* fix: only close fanotify events with a valid fd * add changelog entry * add a test * make the test pass clippy
1 parent 1604723 commit 663506a

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

changelog/2399.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
No longer panics when the `fanotify` queue overflows.

src/sys/fanotify.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ impl FanotifyEvent {
249249

250250
impl Drop for FanotifyEvent {
251251
fn drop(&mut self) {
252+
if self.0.fd == libc::FAN_NOFD {
253+
return;
254+
}
252255
let e = close(self.0.fd);
253256
if !std::thread::panicking() && e == Err(Errno::EBADF) {
254257
panic!("Closing an invalid file descriptor!");

test/sys/test_fanotify.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::*;
2+
use nix::errno::Errno;
23
use nix::sys::fanotify::{
34
EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
45
Response,
56
};
6-
use std::fs::{read_link, File, OpenOptions};
7+
use std::fs::{read_link, read_to_string, File, OpenOptions};
78
use std::io::ErrorKind;
89
use std::io::{Read, Write};
910
use std::os::fd::AsRawFd;
@@ -16,6 +17,7 @@ pub fn test_fanotify() {
1617

1718
test_fanotify_notifications();
1819
test_fanotify_responses();
20+
test_fanotify_overflow();
1921
}
2022

2123
fn test_fanotify_notifications() {
@@ -147,3 +149,71 @@ fn test_fanotify_responses() {
147149

148150
file_thread.join().unwrap();
149151
}
152+
153+
fn test_fanotify_overflow() {
154+
let max_events: usize =
155+
read_to_string("/proc/sys/fs/fanotify/max_queued_events")
156+
.unwrap()
157+
.trim()
158+
.parse()
159+
.unwrap();
160+
161+
// make sure the kernel is configured with the default value,
162+
// just so this test doesn't run forever
163+
assert_eq!(max_events, 16384);
164+
165+
let group = Fanotify::init(
166+
InitFlags::FAN_CLASS_NOTIF
167+
| InitFlags::FAN_REPORT_TID
168+
| InitFlags::FAN_NONBLOCK,
169+
EventFFlags::O_RDONLY,
170+
)
171+
.unwrap();
172+
let tempdir = tempfile::tempdir().unwrap();
173+
let tempfile = tempdir.path().join("test");
174+
175+
OpenOptions::new()
176+
.write(true)
177+
.create_new(true)
178+
.open(&tempfile)
179+
.unwrap();
180+
181+
group
182+
.mark(
183+
MarkFlags::FAN_MARK_ADD,
184+
MaskFlags::FAN_OPEN,
185+
None,
186+
Some(&tempfile),
187+
)
188+
.unwrap();
189+
190+
thread::scope(|s| {
191+
// perform 10 more events to demonstrate some will be dropped
192+
for _ in 0..(max_events + 10) {
193+
s.spawn(|| {
194+
File::open(&tempfile).unwrap();
195+
});
196+
}
197+
});
198+
199+
// flush the queue until it's empty
200+
let mut n = 0;
201+
let mut last_event = None;
202+
loop {
203+
match group.read_events() {
204+
Ok(events) => {
205+
n += events.len();
206+
if let Some(event) = events.last() {
207+
last_event = Some(event.mask());
208+
}
209+
}
210+
Err(e) if e == Errno::EWOULDBLOCK => break,
211+
Err(e) => panic!("{e:?}"),
212+
}
213+
}
214+
215+
// make sure we read all we expected.
216+
// the +1 is for the overflow event.
217+
assert_eq!(n, max_events + 1);
218+
assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW));
219+
}

0 commit comments

Comments
 (0)