Skip to content

Commit 3e3ad15

Browse files
authored
Merge branch 'main' into tiny-test-fixes
2 parents 17f40ef + c54ecd7 commit 3e3ad15

File tree

14 files changed

+192
-133
lines changed

14 files changed

+192
-133
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ and this project adheres to
1010

1111
### Added
1212

13+
### Changed
14+
15+
### Deprecated
16+
17+
### Removed
18+
19+
### Fixed
20+
21+
## [1.11.0]
22+
23+
### Added
24+
1325
- [#4987](https://github.com/firecracker-microvm/firecracker/pull/4987): Reset
1426
physical counter register (`CNTPCT_EL0`) on VM startup. This avoids VM reading
1527
the host physical counter value. This is only possible on 6.4 and newer
@@ -64,6 +76,9 @@ and this project adheres to
6476
- [#5046](https://github.com/firecracker-microvm/firecracker/pull/5046): Retry
6577
KVM_CREATE_VM on EINTR that occasionally happen on heavily loaded hosts to
6678
improve reliability of microVM creation.
79+
- [#5052](https://github.com/firecracker-microvm/firecracker/pull/5052): Build
80+
the empty seccomp policy as default for debug builds to avoid crashes on
81+
syscalls introduced by debug assertions from Rust 1.80.0.
6782

6883
## [1.10.1]
6984

docs/seccomp.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ follows:
1111
- API - right before launching the HTTP server;
1212
- VCPUs - right before executing guest code.
1313

14-
**Note**: On experimental GNU targets, there are no default seccomp filters
15-
installed, since they are not intended for production use.
14+
> [!WARNING]
15+
>
16+
> On debug binaries and experimental GNU targets, there are no default seccomp
17+
> filters installed, since they are not intended for production use.
1618
1719
Firecracker uses JSON files for expressing the filter rules and relies on the
1820
[seccompiler](seccompiler.md) tool for all the seccomp functionality.
@@ -58,6 +60,12 @@ Potential use cases:
5860
- Users of experimentally-supported targets (like GNU libc builds) may be able
5961
to use this feature to implement seccomp filters without needing to have a
6062
custom build of Firecracker.
63+
- Users of debug binaries who need to use a seccomp filter for any reason will
64+
be able to use this feature to implement seccomp filters without needing to
65+
have a custom build of Firecracker. Note: there may be some differences in
66+
syscalls between `debug` and `release` builds. A non-comprehensive list is:
67+
- `fcntl(F_GETFD)` is used by debug assertions to verify a dropped `fd` is
68+
valid.
6169
- Faced with a _theoretical_ production issue, due to a syscall that was issued
6270
by the Firecracker process, but not allowed by the seccomp policy, one may use
6371
a custom filter in order to quickly mitigate the issue. This can speed up the

resources/guest_configs/ci.config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@ CONFIG_SQUASHFS_ZSTD=y
55
# aarch64 only TBD split into a separate file
66
CONFIG_DEVMEM=y
77
# CONFIG_ARM64_ERRATUM_3194386 is not set
8+
# Needed for CTRL+ALT+DEL support
9+
CONFIG_SERIO=y
10+
CONFIG_SERIO_I8042=y
11+
CONFIG_SERIO_LIBPS2=y
12+
CONFIG_SERIO_GSCPS2=y
13+
CONFIG_KEYBOARD_ATKBD=y
14+
CONFIG_INPUT_KEYBOARD=y

resources/overlay/usr/local/bin/fast_page_fault_helper.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,27 @@
2020
int main(int argc, char *const argv[]) {
2121
sigset_t set;
2222
int signal;
23+
void *ptr;
2324

2425
sigemptyset(&set);
25-
if(sigaddset(&set, SIGUSR1) == -1) {
26+
if (sigaddset(&set, SIGUSR1) == -1) {
2627
perror("sigaddset");
27-
return -1;
28+
return 1;
29+
}
30+
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
31+
perror("sigprocmask");
32+
return 1;
2833
}
2934

30-
void *ptr = mmap(NULL, MEM_SIZE_MIB, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
31-
32-
memset(ptr, 1, MEM_SIZE_MIB);
35+
ptr = mmap(NULL, MEM_SIZE_MIB, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
3336

34-
if(MAP_FAILED == ptr) {
37+
if (MAP_FAILED == ptr) {
3538
perror("mmap");
36-
return -1;
39+
return 1;
3740
}
3841

42+
memset(ptr, 1, MEM_SIZE_MIB);
43+
3944
sigwait(&set, &signal);
4045

4146
memset(ptr, 2, MEM_SIZE_MIB);

resources/rebuild.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ EOF
120120
}
121121

122122
function clone_amazon_linux_repo {
123-
[ -d linux ] || git clone https://github.com/amazonlinux/linux linux
123+
[ -d linux ] || git clone --no-checkout --filter=tree:0 https://github.com/amazonlinux/linux
124124
}
125125

126126
# prints the git tag corresponding to the newest and best matching the provided kernel version $1
@@ -145,7 +145,8 @@ function build_al_kernel {
145145
local KERNEL_VERSION=$(echo $KERNEL_CFG | grep -Po "microvm-kernel-ci-$ARCH-\K(\d+\.\d+)")
146146

147147
pushd linux
148-
make distclean
148+
# fails immediately after clone because nothing is checked out
149+
make distclean || true
149150

150151
git checkout $(get_tag $KERNEL_VERSION)
151152

src/firecracker/build.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,34 @@ const SECCOMPILER_SRC_DIR: &str = "../seccompiler/src";
1414
fn main() {
1515
// Target triple
1616
let target = std::env::var("TARGET").expect("Missing target.");
17+
let debug: bool = std::env::var("DEBUG")
18+
.expect("Missing debug.")
19+
.parse()
20+
.expect("Invalid env variable DEBUG");
1721
let out_dir = std::env::var("OUT_DIR").expect("Missing build-level OUT_DIR.");
1822
// Target arch (x86_64 / aarch64)
1923
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").expect("Missing target arch.");
2024

2125
let seccomp_json_path = format!("{}/{}.json", JSON_DIR, target);
22-
// If the current target doesn't have a default filter, use a default, empty filter.
26+
// If the current target doesn't have a default filter, or if we're building a debug binary,
27+
// use a default, empty filter.
2328
// This is to make sure that Firecracker builds even with libc toolchains for which we don't
2429
// provide a default filter. For example, GNU libc.
25-
let seccomp_json_path = if Path::new(&seccomp_json_path).exists() {
26-
seccomp_json_path
27-
} else {
30+
let seccomp_json_path = if debug {
31+
println!(
32+
"cargo:warning=Using empty default seccomp policy for debug builds: \
33+
`resources/seccomp/unimplemented.json`."
34+
);
35+
format!("{}/unimplemented.json", JSON_DIR)
36+
} else if !Path::new(&seccomp_json_path).exists() {
2837
println!(
2938
"cargo:warning=No default seccomp policy for target: {}. Defaulting to \
3039
`resources/seccomp/unimplemented.json`.",
3140
target
3241
);
3342
format!("{}/unimplemented.json", JSON_DIR)
43+
} else {
44+
seccomp_json_path
3445
};
3546

3647
// Retrigger the build script if the JSON file has changed.

tests/conftest.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,6 @@ def bin_seccomp_paths():
252252
yield demos
253253

254254

255-
@pytest.fixture
256-
def uffd_handler_paths():
257-
"""Build UFFD handler binaries."""
258-
handlers = {
259-
f"{handler}_handler": build_tools.get_example(f"uffd_{handler}_handler")
260-
for handler in ["malicious", "valid", "fault_all"]
261-
}
262-
yield handlers
263-
264-
265255
@pytest.fixture(scope="session")
266256
def netns_factory(worker_id):
267257
"""A network namespace factory

tests/framework/utils.py

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import re
1111
import select
1212
import signal
13-
import stat
1413
import subprocess
1514
import time
1615
import typing
@@ -131,69 +130,6 @@ def chroot(path):
131130
os.chdir(working_dir)
132131

133132

134-
class UffdHandler:
135-
"""Describe the UFFD page fault handler process."""
136-
137-
def __init__(self, name, socket_path, mem_file, chroot_path, log_file_name):
138-
"""Instantiate the handler process with arguments."""
139-
self._proc = None
140-
self._handler_name = name
141-
self._socket_path = socket_path
142-
self._mem_file = mem_file
143-
self._chroot = chroot_path
144-
self._log_file = log_file_name
145-
146-
def spawn(self, uid, gid):
147-
"""Spawn handler process using arguments provided."""
148-
149-
with chroot(self._chroot):
150-
st = os.stat(self._handler_name)
151-
os.chmod(self._handler_name, st.st_mode | stat.S_IEXEC)
152-
153-
chroot_log_file = Path("/") / self._log_file
154-
with open(chroot_log_file, "w", encoding="utf-8") as logfile:
155-
args = [f"/{self._handler_name}", self._socket_path, self._mem_file]
156-
self._proc = subprocess.Popen(
157-
args, stdout=logfile, stderr=subprocess.STDOUT
158-
)
159-
160-
# Give it time start and fail, if it really has too (bad things happen).
161-
time.sleep(1)
162-
if not self.is_running():
163-
print(chroot_log_file.read_text(encoding="utf-8"))
164-
assert False, "Could not start PF handler!"
165-
166-
# The page fault handler will create the socket path with root rights.
167-
# Change rights to the jailer's.
168-
os.chown(self._socket_path, uid, gid)
169-
170-
@property
171-
def proc(self):
172-
"""Return UFFD handler process."""
173-
return self._proc
174-
175-
def is_running(self):
176-
"""Check if UFFD process is running"""
177-
return self.proc is not None and self.proc.poll() is None
178-
179-
@property
180-
def log_file(self):
181-
"""Return the path to the UFFD handler's log file"""
182-
return Path(self._chroot) / Path(self._log_file)
183-
184-
@property
185-
def log_data(self):
186-
"""Return the log data of the UFFD handler"""
187-
if self.log_file is None:
188-
return ""
189-
return self.log_file.read_text(encoding="utf-8")
190-
191-
def __del__(self):
192-
"""Tear down the UFFD handler process."""
193-
if self.proc is not None:
194-
self.proc.kill()
195-
196-
197133
class CpuMap:
198134
"""Cpu map from real cpu cores to containers visible cores.
199135

tests/framework/utils_cpuid.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class CpuModel(str, Enum):
2828
AMD_GENOA = "AMD_GENOA"
2929
ARM_NEOVERSE_N1 = "ARM_NEOVERSE_N1"
3030
ARM_NEOVERSE_V1 = "ARM_NEOVERSE_V1"
31+
ARM_NEOVERSE_V2 = "ARM_NEOVERSE_V2"
3132
INTEL_SKYLAKE = "INTEL_SKYLAKE"
3233
INTEL_CASCADELAKE = "INTEL_CASCADELAKE"
3334
INTEL_ICELAKE = "INTEL_ICELAKE"
@@ -41,7 +42,11 @@ class CpuModel(str, Enum):
4142
"Intel(R) Xeon(R) Platinum 8375C CPU": "INTEL_ICELAKE",
4243
},
4344
CpuVendor.AMD: {"AMD EPYC 7R13": "AMD_MILAN", "AMD EPYC 9R14": "AMD_GENOA"},
44-
CpuVendor.ARM: {"0xd0c": "ARM_NEOVERSE_N1", "0xd40": "ARM_NEOVERSE_V1"},
45+
CpuVendor.ARM: {
46+
"0xd0c": "ARM_NEOVERSE_N1",
47+
"0xd40": "ARM_NEOVERSE_V1",
48+
"0xd4f": "ARM_NEOVERSE_V2",
49+
},
4550
}
4651

4752

tests/framework/utils_uffd.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""UFFD related utility functions"""
4+
5+
import os
6+
import stat
7+
import subprocess
8+
import time
9+
from pathlib import Path
10+
11+
from framework.utils import chroot
12+
from host_tools import cargo_build
13+
14+
SOCKET_PATH = "/firecracker-uffd.sock"
15+
16+
17+
class UffdHandler:
18+
"""Describe the UFFD page fault handler process."""
19+
20+
def __init__(self, name, socket_path, mem_file, chroot_path, log_file_name):
21+
"""Instantiate the handler process with arguments."""
22+
self._proc = None
23+
self._handler_name = name
24+
self._socket_path = socket_path
25+
self._mem_file = mem_file
26+
self._chroot = chroot_path
27+
self._log_file = log_file_name
28+
29+
def spawn(self, uid, gid):
30+
"""Spawn handler process using arguments provided."""
31+
32+
with chroot(self._chroot):
33+
st = os.stat(self._handler_name)
34+
os.chmod(self._handler_name, st.st_mode | stat.S_IEXEC)
35+
36+
chroot_log_file = Path("/") / self._log_file
37+
with open(chroot_log_file, "w", encoding="utf-8") as logfile:
38+
args = [f"/{self._handler_name}", self._socket_path, self._mem_file]
39+
self._proc = subprocess.Popen(
40+
args, stdout=logfile, stderr=subprocess.STDOUT
41+
)
42+
43+
# Give it time start and fail, if it really has too (bad things happen).
44+
time.sleep(1)
45+
if not self.is_running():
46+
print(chroot_log_file.read_text(encoding="utf-8"))
47+
assert False, "Could not start PF handler!"
48+
49+
# The page fault handler will create the socket path with root rights.
50+
# Change rights to the jailer's.
51+
os.chown(self._socket_path, uid, gid)
52+
53+
@property
54+
def proc(self):
55+
"""Return UFFD handler process."""
56+
return self._proc
57+
58+
def is_running(self):
59+
"""Check if UFFD process is running"""
60+
return self.proc is not None and self.proc.poll() is None
61+
62+
@property
63+
def log_file(self):
64+
"""Return the path to the UFFD handler's log file"""
65+
return Path(self._chroot) / Path(self._log_file)
66+
67+
@property
68+
def log_data(self):
69+
"""Return the log data of the UFFD handler"""
70+
if self.log_file is None:
71+
return ""
72+
return self.log_file.read_text(encoding="utf-8")
73+
74+
def __del__(self):
75+
"""Tear down the UFFD handler process."""
76+
if self.proc is not None:
77+
self.proc.kill()
78+
79+
80+
def spawn_pf_handler(vm, handler_path, mem_path):
81+
"""Spawn page fault handler process."""
82+
# Copy snapshot memory file into chroot of microVM.
83+
jailed_mem = vm.create_jailed_resource(mem_path)
84+
# Copy the valid page fault binary into chroot of microVM.
85+
jailed_handler = vm.create_jailed_resource(handler_path)
86+
handler_name = os.path.basename(jailed_handler)
87+
88+
uffd_handler = UffdHandler(
89+
handler_name, SOCKET_PATH, jailed_mem, vm.chroot(), "uffd.log"
90+
)
91+
uffd_handler.spawn(vm.jailer.uid, vm.jailer.gid)
92+
93+
return uffd_handler
94+
95+
96+
def uffd_handler(handler_name):
97+
"""Retrieves the uffd handler with the given name"""
98+
return cargo_build.get_example(f"uffd_{handler_name}_handler")

0 commit comments

Comments
 (0)