Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/src/devices/tests/integration_tests.rs
1958 views
1
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
// SPDX-License-Identifier: Apache-2.0
3
4
mod serial_utils;
5
6
use std::io;
7
use std::os::raw::{c_int, c_void};
8
use std::sync::{Arc, Mutex};
9
10
use event_manager::{EventManager, SubscriberOps};
11
12
use devices::legacy::Serial;
13
use devices::BusDevice;
14
use serial_utils::MockSerialInput;
15
use utils::eventfd::EventFd;
16
17
#[test]
18
fn test_issue_serial_hangup_anon_pipe_while_registered_stdin() {
19
let mut fds: [c_int; 2] = [0; 2];
20
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
21
assert!(rc == 0);
22
23
// Serial input is the reading end of the pipe.
24
let serial_in = MockSerialInput(fds[0]);
25
let kick_stdin_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
26
let serial = Arc::new(Mutex::new(Serial::new_in_out(
27
EventFd::new(libc::EFD_NONBLOCK).unwrap(),
28
Box::new(serial_in),
29
Box::new(io::stdout()),
30
Some(kick_stdin_evt.try_clone().unwrap()),
31
)));
32
33
// Make reading fd non blocking to read just what is inflight.
34
let flags = unsafe { libc::fcntl(fds[0], libc::F_GETFL, 0) };
35
let mut rc = unsafe { libc::fcntl(fds[0], libc::F_SETFL, flags | libc::O_NONBLOCK) };
36
assert!(rc == 0);
37
38
const BYTES_COUNT: usize = 65; // Serial FIFO_SIZE + 1.
39
let mut dummy_data = [1u8; BYTES_COUNT];
40
rc = unsafe {
41
libc::write(
42
fds[1],
43
dummy_data.as_mut_ptr() as *const c_void,
44
dummy_data.len(),
45
) as i32
46
};
47
assert!(dummy_data.len() == rc as usize);
48
49
// Register the reading end of the pipe to the event manager, to be processed later on.
50
let mut event_manager = EventManager::new().unwrap();
51
let _id = event_manager.add_subscriber(serial.clone());
52
53
// `EventSet::IN` was received on stdin. The event handling will consume
54
// 64 bytes from stdin. The stdin monitoring is still armed.
55
let mut ev_count = event_manager.run().unwrap();
56
assert_eq!(ev_count, 1);
57
58
let mut data = [0u8; BYTES_COUNT];
59
60
// On the main thread, we will simulate guest "vCPU" thread serial reads.
61
let data_bus_offset = 0;
62
for i in 0..BYTES_COUNT - 1 {
63
serial
64
.lock()
65
.unwrap()
66
.read(data_bus_offset, &mut data[i..=i]);
67
}
68
69
assert!(data[..31] == dummy_data[..31]);
70
assert!(data[32..64] == dummy_data[32..64]);
71
72
// The avail capacity of the serial FIFO is 64.
73
// Read the 65th from the stdin through the kick stdin event triggered by 64th of the serial
74
// FIFO read, or by the armed level-triggered stdin monitoring. Either one of the events might
75
// be handled first. The handling of the second event will find the stdin without any pending
76
// bytes and will result in EWOULDBLOCK. Usually, EWOULDBLOCK will reregister the stdin, but
77
// since it was not unregistered before, it will do a noop.
78
ev_count = event_manager.run().unwrap();
79
assert_eq!(ev_count, 2);
80
81
// The avail capacity of the serial FIFO is 63.
82
rc = unsafe {
83
libc::write(
84
fds[1],
85
dummy_data.as_mut_ptr() as *const c_void,
86
dummy_data.len(),
87
) as i32
88
};
89
assert!(dummy_data.len() == rc as usize);
90
91
// Writing to the other end of the pipe triggers handling a stdin event.
92
// Now, 63 bytes will be read from stdin, filling up the buffer.
93
ev_count = event_manager.run().unwrap();
94
assert_eq!(ev_count, 1);
95
96
// Close the writing end (this sends an HANG_UP to the reading end).
97
// While the stdin is registered, this event is caught by the event manager.
98
rc = unsafe { libc::close(fds[1]) };
99
assert!(rc == 0);
100
101
// This cycle of epoll has two important events. First, the received HANGUP and second
102
// the fact that the FIFO is full, so even if the stdin reached EOF, there are still
103
// pending bytes to be read. We still unregister the stdin and keep reading from it until
104
// we get all pending bytes.
105
ev_count = event_manager.run().unwrap();
106
assert_eq!(ev_count, 1);
107
108
// Free up 64 bytes from the serial FIFO.
109
for i in 0..BYTES_COUNT - 1 {
110
serial
111
.lock()
112
.unwrap()
113
.read(data_bus_offset, &mut data[i..=i]);
114
}
115
116
// Process the kick stdin event generated by the reading of the 64th byte of the serial FIFO.
117
// This will consume some more bytes from the stdin while the stdin is unregistered.
118
ev_count = event_manager.run().unwrap();
119
assert_eq!(ev_count, 1);
120
121
// Two more bytes left. At the 2nd byte, another kick read stdin event is generated,
122
// trying to fill again the serial FIFO with more bytes.
123
for i in 0..2 {
124
serial
125
.lock()
126
.unwrap()
127
.read(data_bus_offset, &mut data[i..=i]);
128
}
129
130
// We try to read again, but we detect that stdin received previously EOF.
131
// This can be deduced by reading from a non-blocking fd and getting 0 bytes as a result,
132
// instead of EWOUDBLOCK. We unregister the stdin and the kick stdin read evt.
133
ev_count = event_manager.run().unwrap();
134
assert_eq!(ev_count, 1);
135
136
// Nothing can interrupt us.
137
ev_count = event_manager.run_with_timeout(1).unwrap();
138
assert_eq!(ev_count, 0);
139
}
140
141
#[test]
142
fn test_issue_hangup() {
143
let mut fds: [c_int; 2] = [0; 2];
144
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
145
assert!(rc == 0);
146
147
// Serial input is the reading end of the pipe.
148
let serial_in = MockSerialInput(fds[0]);
149
let kick_stdin_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
150
let serial = Arc::new(Mutex::new(Serial::new_in_out(
151
EventFd::new(libc::EFD_NONBLOCK).unwrap(),
152
Box::new(serial_in),
153
Box::new(io::stdout()),
154
Some(kick_stdin_evt.try_clone().unwrap()),
155
)));
156
157
// Make reading fd non blocking to read just what is inflight.
158
let flags = unsafe { libc::fcntl(fds[0], libc::F_GETFL, 0) };
159
let mut rc = unsafe { libc::fcntl(fds[0], libc::F_SETFL, flags | libc::O_NONBLOCK) };
160
assert!(rc == 0);
161
162
// Close the writing end (this sends an HANG_UP to the reading end).
163
// While the stdin is registered, this event is caught by the event manager.
164
rc = unsafe { libc::close(fds[1]) };
165
assert!(rc == 0);
166
167
// Register the reading end of the pipe to the event manager, to be processed later on.
168
let mut event_manager = EventManager::new().unwrap();
169
let _id = event_manager.add_subscriber(serial.clone());
170
171
let mut ev_count = event_manager.run().unwrap();
172
assert_eq!(ev_count, 1);
173
174
// Nothing can interrupt us.
175
ev_count = event_manager.run_with_timeout(1).unwrap();
176
assert_eq!(ev_count, 0);
177
}
178
179
#[test]
180
fn test_issue_serial_hangup_anon_pipe_while_unregistered_stdin() {
181
let mut fds: [c_int; 2] = [0; 2];
182
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
183
assert!(rc == 0);
184
185
// Serial input is the reading end of the pipe.
186
let serial_in = MockSerialInput(fds[0]);
187
let kick_stdin_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
188
let serial = Arc::new(Mutex::new(Serial::new_in_out(
189
EventFd::new(libc::EFD_NONBLOCK).unwrap(),
190
Box::new(serial_in),
191
Box::new(io::stdout()),
192
Some(kick_stdin_evt.try_clone().unwrap()),
193
)));
194
195
// Make reading fd non blocking to read just what is inflight.
196
let flags = unsafe { libc::fcntl(fds[0], libc::F_GETFL, 0) };
197
let mut rc = unsafe { libc::fcntl(fds[0], libc::F_SETFL, flags | libc::O_NONBLOCK) };
198
assert!(rc == 0);
199
200
const BYTES_COUNT: usize = 65; // Serial FIFO_SIZE + 1.
201
let mut dummy_data = [1u8; BYTES_COUNT];
202
rc = unsafe {
203
libc::write(
204
fds[1],
205
dummy_data.as_mut_ptr() as *const c_void,
206
dummy_data.len(),
207
) as i32
208
};
209
assert!(dummy_data.len() == rc as usize);
210
211
// Register the reading end of the pipe to the event manager, to be processed later on.
212
let mut event_manager = EventManager::new().unwrap();
213
let _id = event_manager.add_subscriber(serial.clone());
214
215
// `EventSet::IN` was received on stdin. The event handling will consume
216
// 64 bytes from stdin. The stdin monitoring is still armed.
217
let mut ev_count = event_manager.run_with_timeout(0).unwrap();
218
assert_eq!(ev_count, 1);
219
220
let mut data = [0u8; BYTES_COUNT];
221
222
// On the main thread, we will simulate guest "vCPU" thread serial reads.
223
let data_bus_offset = 0;
224
for i in 0..BYTES_COUNT - 1 {
225
serial
226
.lock()
227
.unwrap()
228
.read(data_bus_offset, &mut data[i..=i]);
229
}
230
231
assert!(data[..31] == dummy_data[..31]);
232
assert!(data[32..64] == dummy_data[32..64]);
233
234
// The avail capacity of the serial FIFO is 64.
235
// Read the 65th from the stdin through the kick stdin event triggered by 64th of the serial
236
// FIFO read, or by the armed level-triggered stdin monitoring. Either one of the events might
237
// be handled first. The handling of the second event will find the stdin without any pending
238
// bytes and will result in EWOULDBLOCK. Usually, EWOULDBLOCK will reregister the stdin, but
239
// since it was not unregistered before, it will do a noop.
240
ev_count = event_manager.run().unwrap();
241
assert_eq!(ev_count, 2);
242
243
// The avail capacity of the serial FIFO is 63.
244
rc = unsafe {
245
libc::write(
246
fds[1],
247
dummy_data.as_mut_ptr() as *const c_void,
248
dummy_data.len(),
249
) as i32
250
};
251
assert!(dummy_data.len() == rc as usize);
252
253
// Writing to the other end of the pipe triggers handling an stdin event.
254
// Now, 63 bytes will be read from stdin, filling up the buffer.
255
ev_count = event_manager.run().unwrap();
256
assert_eq!(ev_count, 1);
257
258
// Serial FIFO is full, so silence the stdin. We do not need any other interruptions
259
// until the serial FIFO is freed.
260
ev_count = event_manager.run().unwrap();
261
assert_eq!(ev_count, 1);
262
263
// Close the writing end (this sends an HANG_UP to the reading end).
264
// While the stdin is unregistered, this event is not caught by the event manager.
265
rc = unsafe { libc::close(fds[1]) };
266
assert!(rc == 0);
267
268
// This would be a blocking epoll_wait, since the buffer is full and stdin is unregistered.
269
// There is no event that can break the epoll wait loop.
270
ev_count = event_manager.run_with_timeout(0).unwrap();
271
assert_eq!(ev_count, 0);
272
273
// Free up 64 bytes from the serial FIFO.
274
for i in 0..BYTES_COUNT - 1 {
275
serial
276
.lock()
277
.unwrap()
278
.read(data_bus_offset, &mut data[i..=i]);
279
}
280
281
// Process the kick stdin event generated by the reading of the 64th byte of the serial FIFO.
282
// This will consume some more bytes from the stdin. Keep in mind that the HANGUP event was
283
// lost and we do not know that the stdin reached EOF.
284
ev_count = event_manager.run().unwrap();
285
assert_eq!(ev_count, 1);
286
287
// Two more bytes left. At the 2nd byte, another kick read stdin event is generated,
288
// trying to fill again the serial FIFO with more bytes. Keep in mind that the HANGUP event was
289
// lost and we do not know that the stdin reached EOF.
290
for i in 0..2 {
291
serial
292
.lock()
293
.unwrap()
294
.read(data_bus_offset, &mut data[i..=i]);
295
}
296
297
// We try to read again, but we detect that stdin received previously EOF.
298
// This can be deduced by reading from a non-blocking fd and getting 0 bytes as a result,
299
// instead of EWOUDBLOCK. We unregister the stdin and the kick stdin read evt.
300
ev_count = event_manager.run().unwrap();
301
assert_eq!(ev_count, 1);
302
303
// Nothing can interrupt us.
304
ev_count = event_manager.run_with_timeout(0).unwrap();
305
assert_eq!(ev_count, 0);
306
}
307
308