isRunning()/Signal()/Kill()/joinSandboxNetNs() trust a raw PID with no liveness/identity check, so PID reuse after the VMM exits lets urunc signal (and even network-namespace-join) an unrelated host process
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 52/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- go, linux
調査の方向性
まず pkg/unikontainers/unikontainers.go の Create とライフサイクルメソッドを、特に isRunning、Signal、Kill、joinSandboxNetNs を追跡して確認し、次に pkg/unikontainers/utils.go の checkValidNsPath と pkg/unikontainers/hypervisors/utils.go の killProcess を調べます。u.State.Pid がどのように保存されているかを確認し、これらの経路に対応する既存のテストまたはテストエントリポイントを特定します。シグナル送信、停止、削除、またはネットワーク名前空間への参加を行う前に、PID の再利用が一貫して拒否されれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Description
urunc records the monitor (VMM) process's PID once, in u.State.Pid, when the container is created, and then uses that raw integer for the rest of the container's lifecycle: Signal(), Kill(), Delete() via isRunning(), and joinSandboxNetNs(). None of these call sites verify that the PID still refers to the same process that was originally launched, they only check whether some process currently holds that PID number.
On Linux, PIDs are recycled. Once the VMM exits and gets reaped, that PID becomes eligible for reuse by any new process on the host. Issue #716 shows urunc containers can sit around for a long time after their VMM has already died, which is exactly the kind of window that makes PID recycling realistic on a busy node.
Relevant code:
pkg/unikontainers/unikontainers.go:1407-1415 (isRunning):
func (u *Unikontainer) isRunning() bool {
vmmType := hypervisors.VmmType(u.State.Annotations[annotHypervisor])
if vmmType != hypervisors.HedgeVmm {
return syscall.Kill(u.State.Pid, syscall.Signal(0)) == nil
}
...
}
pkg/unikontainers/unikontainers.go:794-838 (Signal, Kill):
func (u *Unikontainer) Signal(signal unix.Signal) error {
...
return vmm.Signal(u.State.Pid, signal)
}
func (u *Unikontainer) Kill() error {
err := u.joinSandboxNetNs()
...
err = vmm.Stop(u.State.Pid)
...
err = network.CleanupAllUruncTaps()
...
}
pkg/unikontainers/unikontainers.go:921-948 (joinSandboxNetNs):
if netNsPath == "" {
netNsPath = fmt.Sprintf("/proc/%d/ns/net", u.State.Pid)
err := checkValidNsPath(netNsPath)
...
}
...
fd, err := unix.Open(netNsPath, unix.O_RDONLY|unix.O_CLOEXEC, 0)
...
err = unix.Setns(int(fd), unix.CLONE_NEWNET)
checkValidNsPath (pkg/unikontainers/utils.go:209-220) only does an os.Lstat(path) existence check, never an identity check.
pkg/unikontainers/hypervisors/utils.go:69-92 (killProcess, used by every hypervisor's Stop):
func killProcess(pid int) error {
const timeout = 2 * time.Second
err := unix.Kill(pid, unix.SIGKILL)
...
}
Sends SIGKILL to whatever process currently owns that PID, with no identity check.
None of these five call sites cross check the PID against anything that proves it is still the original VMM, for example /proc/<pid>/stat starttime or cgroup membership, which is a technique other runtimes such as runc rely on for this exact reason.
If the VMM's PID gets reused by an unrelated host process before urunc kill or urunc delete runs:
isRunning()reports true for the unrelated process, soDelete()permanently refuses to clean up the already dead container.Kill()callsjoinSandboxNetNs(), which opens the unrelated process's network namespace and joins it, then callsvmm.Stop(), which SIGKILLs the unrelated process, then runsnetwork.CleanupAllUruncTaps()inside the wrong namespace, removing tap devices that do not belong to this container.
This causes two real failure modes that need no attacker or malicious image author, just ordinary OS PID recycling:
- Host level collateral damage: an unrelated process gets killed, and unrelated network resources are torn down in the wrong netns.
- Stuck containers:
Delete()can refuse indefinitely to remove an already dead container whose PID was reused, leaving orphaned state and blocking CRI or Kubernetes garbage collection, which compounds known issue #716.
Suggested fix: record a lightweight identity token for the VMM PID at Create() time, for example the /proc/<pid>/stat starttime field (which the kernel guarantees changes across PID reuse), and validate it in isRunning(), Signal(), Kill(), and joinSandboxNetNs() before treating the PID as belonging to this container. A mismatch should be treated the same as the process no longer existing.
System info
- Urunc version: main branch
- Arch: any
- VMM: any (Qemu, Firecracker, Cloud Hypervisor, HVT, SPT), any backend going through
killProcess - Unikernel: any
Steps to reproduce
- Start a urunc container with any hypervisor.
u.State.Pidis set to the VMM's host PID, for example 12345. - Let the VMM exit without going through
urunc killorurunc delete(guest panic, VMM OOM kill, or normal completion), so PID 12345 gets reaped and freed. - On a host with enough process churn, an unrelated process ends up receiving PID 12345. This is normal PID reuse behavior on Linux.
- Call
urunc kill <id> 9orurunc delete <id>and observe:isRunning()returns true for the unrelated process, so delete is refused.Kill()joins the unrelated process's network namespace, sends it SIGKILL, and cleans up tap devices in the wrong namespace.
Note on LLM usage
This issue was drafted with the assistance of an LLM (Claude Sonnet 5, Anthropic), which was used to trace the code paths and structure this report. The underlying code, call sites, and failure scenario described above were read and verified directly against the repository by me before filing; the analysis and conclusion are my own responsibility.
- 主要言語
- Go
- スター
- 306
- フォーク
- 214
- 平均マージ
- 2日 8時間
- マージ済み PR(30日)
- 26
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
urunc-dev/urunc のほかの issue
-
urunc-deploy cleanup never labels the node, so the cleanup DaemonSet does not run対応中かも @Sarthak-Shreshtha01 が 1 日前に担当しました。 オープンbug K8s/Tools
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
メンテナーはふだん 1 日以内に返信
-
documentation enhancement Storage
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信
-
Rumprun guests receive &, < and > as \u0026, \u003c and \u003e対応中かも @Sarthak-Shreshtha01 が 7 日前に担当しました。 オープンbug Guests
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
urunc-dev/urunc#1074 · コメント 4 件 ·
メンテナーはふだん 1 日以内に返信
-
Hooks run concurrently instead of in the listed order再び着手できるかも @anishmehta24 が 12 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンCore enhancement OCI
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
urunc-dev/urunc#1066 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
Flag to keep pulled images on local runs of e2e対応中かも @slash-init が 8 日前に担当しました。 オープンdev
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
urunc-dev/urunc#1057 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
urunc-dev/urunc の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
BurntSushi/toml#530 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
メンテナーはふだん 1 日以内に返信
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:session-state issue-rating: 🦞 diamond lobster no-stale P2
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
openclaw/wacli#503 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
[Bug] rmqctl error diagnostics emit terminal control characters対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 81/100
apache/rocketmq-dashboard#6895 ·
メンテナーはふだん 3 日以内に返信