Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/integration_tests/functional/test_signals.py
1958 views
1
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Tests scenarios for Firecracker signal handling."""
4
5
import json
6
import os
7
from signal import \
8
(SIGBUS, SIGSEGV, SIGXFSZ,
9
SIGXCPU, SIGPIPE, SIGHUP, SIGILL, SIGSYS)
10
from time import sleep
11
import resource as res
12
import pytest
13
14
import host_tools.network as net_tools
15
import framework.utils as utils
16
17
signum_str = {
18
SIGBUS: "sigbus",
19
SIGSEGV: "sigsegv",
20
SIGXFSZ: "sigxfsz",
21
SIGXCPU: "sigxcpu",
22
SIGPIPE: "sigpipe",
23
SIGHUP: "sighup",
24
SIGILL: "sigill",
25
SIGSYS: "sigsys"
26
}
27
28
29
@pytest.mark.parametrize(
30
"signum",
31
[SIGBUS, SIGSEGV, SIGXFSZ, SIGXCPU, SIGPIPE, SIGHUP, SIGILL, SIGSYS]
32
)
33
def test_generic_signal_handler(test_microvm_with_api, signum):
34
"""Test signal handling for all handled signals."""
35
microvm = test_microvm_with_api
36
microvm.spawn()
37
38
# We don't need to monitor the memory for this test.
39
microvm.memory_monitor = None
40
41
microvm.basic_config()
42
43
# Configure metrics based on a file.
44
metrics_path = os.path.join(microvm.path, 'metrics_fifo')
45
utils.run_cmd("touch {}".format(metrics_path))
46
response = microvm.metrics.put(
47
metrics_path=microvm.create_jailed_resource(metrics_path)
48
)
49
assert microvm.api_session.is_status_no_content(response.status_code)
50
51
microvm.start()
52
firecracker_pid = int(microvm.jailer_clone_pid)
53
sleep(0.5)
54
55
metrics_jail_path = os.path.join(microvm.chroot(), metrics_path)
56
metrics_fd = open(metrics_jail_path)
57
58
line_metrics = metrics_fd.readlines()
59
assert len(line_metrics) == 1
60
61
os.kill(firecracker_pid, signum)
62
# Firecracker gracefully handles SIGPIPE (doesn't terminate).
63
if signum == int(SIGPIPE):
64
msg = 'Received signal 13'
65
# Flush metrics to file, so we can see the SIGPIPE at bottom assert.
66
# This is going to fail if process has exited.
67
response = microvm.actions.put(action_type='FlushMetrics')
68
assert microvm.api_session.is_status_no_content(response.status_code)
69
else:
70
microvm.expect_kill_by_signal = True
71
# Ensure that the process was terminated.
72
utils.wait_process_termination(firecracker_pid)
73
msg = 'Shutting down VM after intercepting signal {}'.format(signum)
74
75
microvm.check_log_message(msg)
76
77
if signum != SIGSYS:
78
metric_line = json.loads(metrics_fd.readlines()[0])
79
assert metric_line["signals"][signum_str[signum]] == 1
80
81
82
def test_sigxfsz_handler(test_microvm_with_api):
83
"""Test intercepting and handling SIGXFSZ."""
84
microvm = test_microvm_with_api
85
microvm.spawn()
86
87
# We don't need to monitor the memory for this test.
88
microvm.memory_monitor = None
89
90
microvm.basic_config()
91
92
# Configure metrics based on a file.
93
metrics_path = os.path.join(microvm.path, 'metrics_fifo')
94
utils.run_cmd("touch {}".format(metrics_path))
95
response = microvm.metrics.put(
96
metrics_path=microvm.create_jailed_resource(metrics_path)
97
)
98
assert microvm.api_session.is_status_no_content(response.status_code)
99
100
microvm.start()
101
102
metrics_jail_path = os.path.join(microvm.jailer.chroot_path(),
103
metrics_path)
104
metrics_fd = open(metrics_jail_path)
105
line_metrics = metrics_fd.readlines()
106
assert len(line_metrics) == 1
107
108
firecracker_pid = int(microvm.jailer_clone_pid)
109
size = os.path.getsize(metrics_jail_path)
110
# The SIGXFSZ is triggered because the size of rootfs is bigger than
111
# the size of metrics file times 3. Since the metrics file is flushed
112
# twice we have to make sure that the limit is bigger than that
113
# in order to make sure the SIGXFSZ metric is logged
114
res.prlimit(firecracker_pid, res.RLIMIT_FSIZE, (size*3, res.RLIM_INFINITY))
115
116
while True:
117
try:
118
utils.run_cmd("ps -p {}".format(firecracker_pid))
119
sleep(1)
120
except ChildProcessError:
121
break
122
123
microvm.expect_kill_by_signal = True
124
msg = 'Shutting down VM after intercepting signal 25, code 0'
125
microvm.check_log_message(msg)
126
metric_line = json.loads(metrics_fd.readlines()[0])
127
assert metric_line["signals"]["sigxfsz"] == 1
128
129
130
def test_handled_signals(test_microvm_with_ssh, network_config):
131
"""Test that handled signals don't kill the microVM."""
132
microvm = test_microvm_with_ssh
133
microvm.spawn()
134
135
# We don't need to monitor the memory for this test.
136
microvm.memory_monitor = None
137
138
microvm.basic_config(vcpu_count=2)
139
140
# Configure a network interface.
141
_tap, _, _ = microvm.ssh_network_config(network_config, '1')
142
143
microvm.start()
144
firecracker_pid = int(microvm.jailer_clone_pid)
145
146
# Open a SSH connection to validate the microVM stays alive.
147
ssh_connection = net_tools.SSHConnection(microvm.ssh_config)
148
# Just validate a simple command: `nproc`
149
cmd = "nproc"
150
_, stdout, stderr = ssh_connection.execute_command(cmd)
151
assert stderr.read() == ""
152
assert int(stdout.read()) == 2
153
154
# We have a handler installed for this signal.
155
# The 35 is the SIGRTMIN for musl libc.
156
# We hardcode this value since the SIGRTMIN python reports
157
# is 34, which is likely the one for glibc.
158
os.kill(firecracker_pid, 35)
159
160
# Validate the microVM is still up and running.
161
_, stdout, stderr = ssh_connection.execute_command(cmd)
162
assert stderr.read() == ""
163
assert int(stdout.read()) == 2
164
165