Skip to content

Commit 10527f0

Browse files
alexandruagandreeaflorescu
authored andcommitted
fix bad syscall handling
This commit brings the following changes to our bad syscall handling: - It whitelists fcntl(*, FCNTL_F_SETFD, FCNTL_FD_CLOEXEC), which appears to be invoked by the Rust File::open() method. - We now terminate the Firecracker process whenever a bad syscall is intercepted (we still attempt to flush metrics and log an error message). - To avoid getting some unnecessary SIGSYS signals (or whitelisting additional syscalls), the vmm::stop() method calls process::exit() immediately after flushing the metrics one last time. We no longer explicitly bring down the other Firecracker threads. Signed-off-by: Alexandru Agache <[email protected]>
1 parent 4d5f0bd commit 10527f0

File tree

6 files changed

+155
-146
lines changed

6 files changed

+155
-146
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
### Changed
1111
- Log the app version when the `Logger` is initialized.
12-
1312
- Pretty print panic information.
13+
- Firecracker terminates with exit code 148 when a non-whitelisted syscall
14+
is intercepted.
1415

1516
### Fixed
1617

logger/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ authors = ["Amazon firecracker team <[email protected]>"]
55

66
[dependencies]
77
chrono = ">=0.4"
8-
lazy_static = ">=0.2"
8+
lazy_static = ">=1.2"
99
libc = ">=0.2.39"
1010
log = { version = "0.4", features = ["std"] }
1111
serde = ">=1.0.27"

src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use clap::{App, Arg};
2121
use std::io::ErrorKind;
2222
use std::panic;
2323
use std::path::PathBuf;
24+
use std::process;
2425
use std::sync::mpsc::channel;
2526
use std::sync::{Arc, RwLock};
2627

@@ -38,8 +39,11 @@ fn main() {
3839
.preinit(Some(DEFAULT_INSTANCE_ID.to_string()))
3940
.expect("Failed to register logger");
4041

41-
// If the signal handler can't be set, it's OK to panic.
42-
vmm::setup_sigsys_handler().expect("Failed to register signal handler");
42+
if let Err(e) = vmm::setup_sigsys_handler() {
43+
error!("Failed to register signal handler: {}", e);
44+
process::exit(vmm::FC_EXIT_CODE_GENERIC_ERROR as i32);
45+
}
46+
4347
// Start firecracker by setting up a panic hook, which will be called before
4448
// terminating as we're building with panic = "abort".
4549
// It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
@@ -83,7 +87,15 @@ fn main() {
8387
.expect("Missing argument: api_sock");
8488

8589
let mut instance_id = String::from(DEFAULT_INSTANCE_ID);
90+
91+
// We disable seccomp filtering when testing, because when running the test_gnutests
92+
// integration test from test_unittests.py, an invalid syscall is issued, and we crash
93+
// otherwise.
94+
#[cfg(not(test))]
8695
let mut seccomp_level = seccomp::SECCOMP_LEVEL_ADVANCED;
96+
#[cfg(test)]
97+
let mut seccomp_level = seccomp::SECCOMP_LEVEL_NONE;
98+
8799
let mut start_time_us = None;
88100
let mut start_time_cpu_us = None;
89101
let mut is_jailed = false;

vmm/src/default_syscalls/x86_64.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub const ALLOWED_SYSCALLS: &[i64] = &[
1717
libc::SYS_epoll_pwait,
1818
libc::SYS_exit,
1919
libc::SYS_exit_group,
20+
libc::SYS_fcntl,
2021
libc::SYS_fstat,
2122
libc::SYS_futex,
2223
libc::SYS_ioctl,
@@ -41,10 +42,12 @@ const EPOLL_CTL_ADD: u64 = 1;
4142
const EPOLL_CTL_DEL: u64 = 2;
4243

4344
// See include/uapi/asm-generic/fcntl.h in the kernel code.
45+
const FCNTL_FD_CLOEXEC: u64 = 1;
46+
const FCNTL_F_SETFD: u64 = 2;
47+
const O_CLOEXEC: u64 = 0x02000000;
48+
const O_NONBLOCK: u64 = 0x00004000;
4449
const O_RDONLY: u64 = 0x00000000;
4550
const O_RDWR: u64 = 0x00000002;
46-
const O_NONBLOCK: u64 = 0x00004000;
47-
const O_CLOEXEC: u64 = 0x02000000;
4851

4952
// See include/uapi/linux/futex.h in the kernel code.
5053
const FUTEX_WAIT: u64 = 0;
@@ -163,6 +166,19 @@ pub fn default_context() -> Result<SeccompFilterContext, Error> {
163166
libc::SYS_exit_group,
164167
(0, vec![SeccompRule::new(vec![], SeccompAction::Allow)]),
165168
),
169+
(
170+
libc::SYS_fcntl,
171+
(
172+
0,
173+
vec![SeccompRule::new(
174+
vec![
175+
SeccompCondition::new(1, SeccompCmpOp::Eq, FCNTL_F_SETFD)?,
176+
SeccompCondition::new(2, SeccompCmpOp::Eq, FCNTL_FD_CLOEXEC)?,
177+
],
178+
SeccompAction::Allow,
179+
)],
180+
),
181+
),
166182
(
167183
libc::SYS_fstat,
168184
(0, vec![SeccompRule::new(vec![], SeccompAction::Allow)]),

vmm/src/lib.rs

Lines changed: 16 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use std::fs::{metadata, File, OpenOptions};
5353
use std::os::unix::io::{AsRawFd, RawFd};
5454
use std::path::PathBuf;
5555
use std::result;
56-
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
56+
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
5757
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
5858
use std::sync::{Arc, Barrier, RwLock};
5959
use std::thread;
@@ -75,7 +75,7 @@ use logger::{AppInfo, Level, LogOption, Metric, LOGGER, METRICS};
7575
use memory_model::{GuestAddress, GuestMemory};
7676
use serde_json::Value;
7777
pub use sigsys_handler::setup_sigsys_handler;
78-
use sys_util::{register_signal_handler, EventFd, Killable, Terminal};
78+
use sys_util::{register_signal_handler, EventFd, Terminal};
7979
use vm_control::VmResponse;
8080
use vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError};
8181
use vmm_config::drive::{BlockDeviceConfig, BlockDeviceConfigs, DriveError};
@@ -96,6 +96,15 @@ const WRITE_METRICS_PERIOD_SECONDS: u64 = 60;
9696
static START_INSTANCE_REQUEST_TS: AtomicUsize = ATOMIC_USIZE_INIT;
9797
static START_INSTANCE_REQUEST_CPU_TS: AtomicUsize = ATOMIC_USIZE_INIT;
9898

99+
/// Success exit code.
100+
pub const FC_EXIT_CODE_OK: u8 = 0;
101+
/// Generic error exit code.
102+
pub const FC_EXIT_CODE_GENERIC_ERROR: u8 = 1;
103+
/// Generic exit code for an error considered not possible to occur if the program logic is sound.
104+
pub const FC_EXIT_CODE_UNEXPECTED_ERROR: u8 = 2;
105+
/// Firecracker was shut down after intercepting a restricted system call.
106+
pub const FC_EXIT_CODE_BAD_SYSCALL: u8 = 148;
107+
99108
/// Errors associated with the VMM internal logic. These errors cannot be generated by direct user
100109
/// input, but can result from bad configuration of the host (for example if Firecracker doesn't
101110
/// have permissions to open the KVM fd).
@@ -340,7 +349,6 @@ impl MaybeHandler {
340349
}
341350

342351
struct EpollEvent<T: AsRawFd> {
343-
dispatch_index: u64,
344352
fd: T,
345353
}
346354

@@ -428,23 +436,7 @@ impl EpollContext {
428436
.map_err(Error::EpollFd)?;
429437
self.dispatch_table.push(Some(token));
430438

431-
Ok(EpollEvent { dispatch_index, fd })
432-
}
433-
434-
fn remove_event<T>(&mut self, epoll_event: EpollEvent<T>) -> Result<()>
435-
where
436-
T: AsRawFd,
437-
{
438-
epoll::ctl(
439-
self.epoll_raw_fd,
440-
epoll::ControlOptions::EPOLL_CTL_DEL,
441-
epoll_event.fd.as_raw_fd(),
442-
epoll::Event::new(epoll::Events::EPOLLIN, epoll_event.dispatch_index),
443-
)
444-
.map_err(Error::EpollFd)?;
445-
self.dispatch_table[epoll_event.dispatch_index as usize] = None;
446-
447-
Ok(())
439+
Ok(EpollEvent { fd })
448440
}
449441

450442
fn allocate_tokens(&mut self, count: usize) -> (u64, Sender<Box<EpollHandler>>) {
@@ -528,7 +520,6 @@ struct Vmm {
528520
// Guest VM core resources.
529521
guest_memory: Option<GuestMemory>,
530522
kernel_config: Option<KernelConfig>,
531-
kill_signaled: Option<Arc<AtomicBool>>,
532523
vcpu_handles: Option<Vec<thread::JoinHandle<()>>>,
533524
exit_evt: Option<EpollEvent<EventFd>>,
534525
vm: Vm,
@@ -591,7 +582,6 @@ impl Vmm {
591582
shared_info: api_shared_info,
592583
guest_memory: None,
593584
kernel_config: None,
594-
kill_signaled: None,
595585
vcpu_handles: None,
596586
exit_evt: None,
597587
vm,
@@ -910,9 +900,6 @@ impl Vmm {
910900
self.vcpu_handles = Some(Vec::with_capacity(vcpu_count as usize));
911901
// It is safe to unwrap since it's set just above.
912902
let vcpu_handles = self.vcpu_handles.as_mut().unwrap();
913-
self.kill_signaled = Some(Arc::new(AtomicBool::new(false)));
914-
// It is safe to unwrap since it's set just above.
915-
let kill_signaled = self.kill_signaled.as_mut().unwrap();
916903

917904
let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize));
918905

@@ -925,7 +912,6 @@ impl Vmm {
925912
.as_ref()
926913
.ok_or(StartMicrovmError::DeviceManager)?;
927914
let mmio_bus = device_manager.bus.clone();
928-
let kill_signaled = kill_signaled.clone();
929915
let vcpu_thread_barrier = vcpu_thread_barrier.clone();
930916
// If the lock is poisoned, it's OK to panic.
931917
let vcpu_exit_evt = self
@@ -1050,10 +1036,6 @@ impl Vmm {
10501036
},
10511037
_ => (),
10521038
}
1053-
1054-
if kill_signaled.load(Ordering::SeqCst) {
1055-
break;
1056-
}
10571039
}
10581040

10591041
// Nothing we need do for the success case.
@@ -1206,36 +1188,6 @@ impl Vmm {
12061188
fn stop(&mut self, exit_code: i32) {
12071189
info!("Vmm is stopping.");
12081190

1209-
if let Some(v) = self.kill_signaled.take() {
1210-
v.store(true, Ordering::SeqCst);
1211-
};
1212-
1213-
if let Some(handles) = self.vcpu_handles.take() {
1214-
for handle in handles {
1215-
match handle.kill(VCPU_RTSIG_OFFSET) {
1216-
Ok(_) => {
1217-
if let Err(e) = handle.join() {
1218-
warn!("Failed to join vcpu thread: {:?}", e);
1219-
METRICS.vcpu.failures.inc();
1220-
}
1221-
}
1222-
Err(e) => {
1223-
METRICS.vcpu.failures.inc();
1224-
warn!("Failed to kill vcpu thread: {:?}", e)
1225-
}
1226-
}
1227-
}
1228-
};
1229-
1230-
if let Some(evt) = self.exit_evt.take() {
1231-
if let Err(e) = self.epoll_context.remove_event(evt) {
1232-
warn!(
1233-
"Cannot remove the exit event from the Epoll Context. {:?}",
1234-
e
1235-
);
1236-
}
1237-
}
1238-
12391191
if let Err(e) = self.epoll_context.disable_stdin_event() {
12401192
warn!("Cannot disable the STDIN event. {:?}", e);
12411193
}
@@ -1294,7 +1246,7 @@ impl Vmm {
12941246
}
12951247
None => warn!("leftover exit-evt in epollcontext!"),
12961248
}
1297-
self.stop(0);
1249+
self.stop(FC_EXIT_CODE_OK as i32);
12981250
}
12991251
EpollDispatch::Stdin => {
13001252
let mut out = [0u8; 64];
@@ -1834,11 +1786,11 @@ pub fn start_vmm_thread(
18341786
match vmm.run_control() {
18351787
Ok(()) => {
18361788
info!("Gracefully terminated VMM control loop");
1837-
vmm.stop(0)
1789+
vmm.stop(FC_EXIT_CODE_OK as i32)
18381790
}
18391791
Err(e) => {
18401792
error!("Abruptly exited VMM control loop: {:?}", e);
1841-
vmm.stop(1)
1793+
vmm.stop(FC_EXIT_CODE_GENERIC_ERROR as i32);
18421794
}
18431795
}
18441796
})
@@ -2278,16 +2230,13 @@ mod tests {
22782230
}
22792231

22802232
#[test]
2281-
fn add_remove_event_test() {
2233+
fn add_event_test() {
22822234
let mut ep = EpollContext::new().unwrap();
22832235
let evfd = EventFd::new().unwrap();
22842236

22852237
// adding new event should work
22862238
let epev = ep.add_event(evfd, EpollDispatch::Exit);
22872239
assert!(epev.is_ok());
2288-
2289-
// removing event should work
2290-
assert!(ep.remove_event(epev.unwrap()).is_ok());
22912240
}
22922241

22932242
#[test]
@@ -2323,60 +2272,6 @@ mod tests {
23232272
*ep.dispatch_table[idx].as_ref().unwrap(),
23242273
EpollDispatch::Exit
23252274
);
2326-
2327-
// removing event should work
2328-
assert!(ep.remove_event(epev).is_ok());
2329-
}
2330-
2331-
#[test]
2332-
fn epoll_event_try_get_after_remove_test() {
2333-
let mut ep = EpollContext::new().unwrap();
2334-
let evfd = EventFd::new().unwrap();
2335-
2336-
// adding new event should work
2337-
let epev = ep.add_event(evfd, EpollDispatch::Exit).unwrap();
2338-
2339-
let evpoll_events_len = 10;
2340-
2341-
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); evpoll_events_len];
2342-
2343-
// raise the event
2344-
assert!(epev.fd.write(1).is_ok());
2345-
2346-
// removing event should work
2347-
assert!(ep.remove_event(epev).is_ok());
2348-
2349-
// epoll should have no pending events
2350-
let epollret = epoll::wait(ep.epoll_raw_fd, 0, &mut events[..]);
2351-
let num_events = epollret.unwrap();
2352-
assert_eq!(num_events, 0);
2353-
}
2354-
2355-
#[test]
2356-
fn epoll_event_try_use_after_remove_test() {
2357-
let mut ep = EpollContext::new().unwrap();
2358-
let evfd = EventFd::new().unwrap();
2359-
2360-
// adding new event should work
2361-
let epev = ep.add_event(evfd, EpollDispatch::Exit).unwrap();
2362-
2363-
let evpoll_events_len = 10;
2364-
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); evpoll_events_len];
2365-
2366-
// raise the event
2367-
assert!(epev.fd.write(1).is_ok());
2368-
2369-
// epoll should report one event
2370-
let epollret = epoll::wait(ep.epoll_raw_fd, 0, &mut events[..]);
2371-
let num_events = epollret.unwrap();
2372-
assert_eq!(num_events, 1);
2373-
2374-
// removing event should work
2375-
assert!(ep.remove_event(epev).is_ok());
2376-
2377-
// reported event should no longer be available
2378-
let idx = events[0].data as usize;
2379-
assert!(ep.dispatch_table[idx].is_none());
23802275
}
23812276

23822277
#[test]

0 commit comments

Comments
 (0)