Skip to content

Commit f3845ef

Browse files
author
Roy Lin
committed
chore(release): v2.2.0 — hardening + CRI fixes
Bump workspace 2.1.0 -> 2.2.0 (all 9 crates inherit; Cargo.lock via cargo update --workspace). A correctness/hardening release of 24 fixes across the CLI state machine, runtime resource limits, guest-init I/O, the OCI store, networking, the warm pool, and the CRI server (#31-#54). No breaking changes. Docs: CHANGELOG [2.2.0]; cri-conformance refreshed to the 2026-06-15 re-verification (73 Passed / 7 Failed / 17 Skipped on main, zero regression); new ANNOUNCEMENT-v2.2.0.md.
1 parent 91bc80c commit f3845ef

5 files changed

Lines changed: 129 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,60 @@ All notable changes to A3S Box will be documented in this file.
44

55
## [Unreleased]
66

7+
## [2.2.0] — 2026-06-15
8+
9+
A correctness and hardening release: 24 fixes across the CLI state machine,
10+
runtime resource limits, guest-init I/O, the OCI store, networking, the warm
11+
pool, and the CRI server. No breaking changes. CRI conformance re-verified with
12+
zero regression (see below).
13+
14+
### Added
15+
16+
- **Health-check TSI warning**: `run` now warns when a health check probes
17+
`localhost` under TSI networking, where the probe cannot reach the guest.
18+
19+
### Fixed
20+
21+
- **CLI state machine** — route every status-update command (`stop`, `start`,
22+
`kill`, `pause`, `unpause`, `rename`, `restart`) through the atomic
23+
`StateFile` primitives, closing a load-modify-save TOCTOU that could clobber
24+
concurrent box state; make box registration atomic in `compose` + `snapshot`
25+
(orphan-VM race) and unmount the overlay before deleting the box directory in
26+
`compose` cleanup.
27+
- **Resource limits** — enforce `--pids-limit` on the `run` path via an in-guest
28+
cgroup `pids.max`; harden `resize` by rejecting shell-injectable cpuset strings
29+
and clamping `cpu.weight` to its valid range.
30+
- **guest-init** — retry the stdio relay on `EINTR` so container output is never
31+
truncated; fix a cgroup-mount TOCTOU, an stdio fd-leak, and a signal-64 edge;
32+
make container `stdout`/`stderr` re-openable by path (`/dev/stdout`,
33+
`/proc/self/fd/N`) so apps that reopen their logs (e.g. Apache httpd) start.
34+
- **exec** — base64-encode exec args/env so shell quotes survive libkrun's
35+
environment passing.
36+
- **OCI store** — refuse to store blobs whose digest algorithm cannot be
37+
verified; make store and build-cache writes atomic (stage + rename) so
38+
concurrent pulls/builds cannot corrupt a layer.
39+
- **rootfs & networking** — overlay comma-guard + bounded unmount-retry in
40+
provider cleanup; stage single-file bind mounts so directory-sharing virtio-fs
41+
can serve them; kill passt on a boot-failure timeout, guard `terminate`
42+
against PID reuse, and reap passt on boot failure so the published port is
43+
released.
44+
- **Warm pool** — fall back to cold boot when snapshot-fork is unavailable,
45+
instead of failing the pool fill.
46+
- **CRI** — maintain the `StopPodSandbox` state invariant and report stats
47+
correctly for non-running containers; close stdin and send a port-forward
48+
`CLOSE` frame on streaming error paths; reject empty image references in
49+
pull/status/remove; resolve the image reference in `RemoveImage` so
50+
`rmi <short-tag>` (e.g. `alpine:latest`) works; surface container log-file
51+
open failures instead of swallowing them.
52+
53+
### Verified
54+
55+
- **CRI conformance: 73 Passed / 7 Failed / 17 Skipped** (`critest` v1.30.1, skip
56+
portforward; 80 of 97 specs) on `main` — unchanged pass count, **zero
57+
regression** from the fixes above. The 7 remaining failures are all
58+
microVM-architectural (mount propagation, host namespaces, AppArmor-enforce,
59+
non-recursive readonly mounts), not code defects.
60+
761
## [2.1.0] — 2026-06-13
862

963
### Added

docs/ANNOUNCEMENT-v2.2.0.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# A3S Box v2.2.0:把 v2.1.0 的能力焊死在正确性上
2+
3+
> 发布日期:2026-06-15 · 仓库:[AI45Lab/Box](https://github.com/AI45Lab/Box)
4+
5+
v2.1.0 带来了原生 snapshot-fork(虚拟机的隔离 + 容器的启动速度)。v2.2.0 不加新卖点,而是把已有能力**焊死在正确性上**:24 个修复,覆盖 CLI 状态机、运行时资源限制、guest-init 的 I/O、OCI 镜像存储、网络、温池(warm pool),以及 CRI 服务端。没有破坏性变更,CRI 一致性零回归。
6+
7+
一句话:**同样的能力,更经得起并发、异常路径和边界条件。**
8+
9+
---
10+
11+
## 为什么是一个纯加固版本
12+
13+
新特性容易展示,但真正决定一个运行时能不能托付生产的,是它在**并发、崩溃、异常路径**下的行为。v2.2.0 是一轮系统性的对抗式审计的产出——每一条都对应一个真实可复现的缺陷,并在 `/dev/kvm` 真机上用真实 OCI 镜像验证过。
14+
15+
---
16+
17+
## 修了什么
18+
19+
- **CLI 状态机**`stop`/`start`/`kill`/`pause`/`unpause`/`rename`/`restart` 全部改走原子 `StateFile` 原语,关闭了「读-改-写」竞态对并发 box 状态的覆盖;`compose`/`snapshot` 的 box 注册改为原子(消除孤儿 VM 竞态);`compose` 清理时先卸载 overlay 再删目录。
20+
- **资源限制**`--pids-limit``run` 路径上通过 guest 内 cgroup `pids.max` 真正生效;`resize` 加固——拒绝可注入的 cpuset 字符串、把 `cpu.weight` 钳到合法区间。
21+
- **guest-init**:stdio 中继遇 `EINTR` 重试(容器输出不再被截断);修复 cgroup 挂载 TOCTOU、stdio fd 泄漏、signal-64 边界;容器 `stdout`/`stderr` 可按路径重开(`/dev/stdout``/proc/self/fd/N`),让 Apache httpd 这类按路径重开日志的程序能正常启动。
22+
- **exec**:exec 参数/环境变量做 base64 编码,shell 引号能安全穿过 libkrun 的环境传递。
23+
- **OCI 存储**:拒绝存入摘要算法不可校验的 blob;store 与 build-cache 写入原子化(先暂存再 rename),并发 pull/build 不会损坏层。
24+
- **rootfs 与网络**:overlay 逗号防护 + 有界卸载重试;单文件 bind mount 先暂存,让只共享目录的 virtio-fs 能服务它;passt 在启动超时时被杀、`terminate` 防 PID 复用、启动失败时回收 passt 以释放已发布端口。
25+
- **温池**:snapshot-fork 不可用时回退到冷启动,而不是让池填充失败。
26+
- **CRI**:维持 `StopPodSandbox` 状态不变量、为非运行容器正确上报 stats;流式错误路径上关闭 stdin 并发送 port-forward `CLOSE` 帧;拒绝空镜像引用;`RemoveImage` 解析镜像引用,使 `rmi <短标签>`(如 `alpine:latest`)可用;容器日志文件打开失败时上报而非吞掉。
27+
28+
---
29+
30+
## 一致性:零回归
31+
32+
`main` 上用真实 `crictl`/`critest` 重新跑了 Kubernetes CRI 一致性套件:
33+
34+
**73 通过 · 7 失败 · 17 跳过**`critest` v1.30.1,跳过 portforward,80/97 specs)
35+
36+
通过数与既有基线一致——上述所有修复**零回归**。剩下的 7 个失败全部是 microVM 架构限制(mount 传播、宿主 namespace 共享、AppArmor enforce、非递归只读挂载),不是代码缺陷。详见 [`docs/cri-conformance.md`](./cri-conformance.md)
37+
38+
---
39+
40+
## 升级
41+
42+
完全兼容 v2.1.0,无破坏性变更。直接升级即可:
43+
44+
```bash
45+
brew upgrade a3s-box # Homebrew
46+
# 或从 GitHub Release 下载对应平台的 tarball
47+
```
48+
49+
完整变更见 [CHANGELOG](../CHANGELOG.md)

docs/cri-conformance.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,22 @@ suite so regressions are visible and progress is measurable.
1313

1414
| Field | Value |
1515
|-------|-------|
16-
| Date | 2026-06-02 |
16+
| Date | 2026-06-15 |
1717
| `critest` | v1.30.1 |
18-
| a3s-box | 2.0.6 |
18+
| a3s-box | 2.2.0 |
1919
| Host | Linux KVM node (`/dev/kvm`), Ubuntu 24.04 |
20-
| Result | **73 Passed · 9 Failed · 15 Skipped** (ran 82 of 97 specs) |
20+
| Result | **73 Passed · 7 Failed · 17 Skipped** (ran 80 of 97 specs, `--ginkgo.skip=portforward`) |
2121

2222
Up from the original **21 Passed / 59 Failed** (2.0.4) and the **44 Passed**
2323
mid-point. With registry mirrors configured (so the e2e webserver/helper images
2424
resolve) plus per-container capabilities, image-defined users/groups, pod DNS,
2525
crash-recovery reaping, standard `/dev` nodes, routable pod IPs, **OOMKilled
26-
detection**, **MaskedPaths**, and an **SPDY port-forward bridge**, the suite now
27-
passes 73 of the 82 specs that run. Every one of the 9 remaining failures is
26+
detection**, **MaskedPaths**, and an **SPDY port-forward bridge**, the suite
27+
passes 73 of the 80 specs that run. Every one of the 7 remaining failures is
2828
architectural (microVM-per-pod) or environmental — none is a logic defect.
29+
Re-verified on `main` for the 2.2.0 release with **zero regression** from that
30+
release's CRI fixes (lifecycle/stats, streaming cleanup, empty-ref rejection,
31+
`rmi` short-tag resolution).
2932

3033
How it was run:
3134

@@ -79,9 +82,16 @@ critest --runtime-endpoint unix:///tmp/a3s-box.sock \
7982

8083
## Remaining gaps (9 failures — all architectural or environmental)
8184

85+
The 2.2.0 run skips the 2 PortForward specs (see the port-forward note above), so
86+
its headline is **7 Failed**; the table below catalogues all 9 specs that do not
87+
pass on the full suite. Mount propagation is nondeterministic across ginkgo's
88+
randomized order — `rprivate` and `non-recursive readonly mounts` trade places
89+
between runs — but both share the one virtio-fs root cause and the count is
90+
unchanged.
91+
8292
| Category | Failing specs | Root cause | Fixable here? |
8393
|----------|---------------|------------|---------------|
84-
| **Mount propagation** (3) | rprivate / rshared / rslave | bidirectional host↔container propagation needs a real shared mount configured at VM boot; containers are created post-boot and volumes are COPIED into the rootfs | ❌ architectural |
94+
| **Mount propagation / readonly** (3) | rshared / rslave / non-recursive readonly mounts | a directory volume is shared as a *single* virtio-fs mount of one host subtree; bidirectional host↔container propagation needs a real shared mount configured at VM boot, and a readonly mount necessarily covers the whole subtree (the test's nested host `tmpfs` is not a separate guest mount, so it cannot stay writable) | ❌ architectural |
8595
| **Host namespaces** (2) | HostNetwork=true, HostIpc=true | each pod is an isolated microVM with its own kernel + namespaces — there is no host network/IPC namespace to share; rejected fail-closed rather than silently mis-running | ❌ architectural |
8696
| **Per-container PID isolation** (1) | ContainerPID | all of a pod's containers share the single VM-wide PID namespace; a per-container PID namespace is not modeled | ❌ architectural |
8797
| **AppArmor enforce** (1) | should enforce a profile blocking writes | the guest kernel has no AppArmor LSM, and CRI passes only the profile *name* — critest loads the profile on the **host** kernel and deletes the source, so the guest has nothing to compile, and the host-compiled binary policy is ABI-tied to the host kernel. CRI's shared-host-kernel AppArmor model does not map onto a separate-kernel microVM | ❌ architectural |

src/Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ resolver = "2"
2222
h2 = { path = "third_party/h2" }
2323

2424
[workspace.package]
25-
version = "2.1.0"
25+
version = "2.2.0"
2626
edition = "2021"
2727
authors = ["A3S Lab Team"]
2828
license = "MIT"

0 commit comments

Comments
 (0)