From e843b96ece0292ebcdd086541524afeaeefc9d75 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 8 Jul 2026 02:45:06 +0000 Subject: [PATCH 1/2] chore(fire_vmm): bump vmlinux to release bab2b3a9 Upstream trimmed kernel configs (~480 options dropped per config) for faster Firecracker boot; kernel versions unchanged (5.10.259 / 6.1.176). Manifest vendored verbatim from the release; asset sha256s verified against the release sidecar files. --- priv/vmlinux/manifest.json | 12 ++++++------ test/hyper/node/fire_vmm/vm_linux/manifest_test.exs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/priv/vmlinux/manifest.json b/priv/vmlinux/manifest.json index d142741a..e231d15c 100644 --- a/priv/vmlinux/manifest.json +++ b/priv/vmlinux/manifest.json @@ -1,34 +1,34 @@ { - "sha" : "54d6c3f843c01fb55107a024cca5bf60af235c42", + "sha" : "bab2b3a92382df318e60b6b52376d2a9f5b5456c", "builds" : [ { "name" : "x86_64-5.10", "arch" : "x86_64", "version" : "5.10.259", "asset" : "vmlinux-x86_64-5.10", - "sha256" : "96b8f6a665536e560777f9634773b0ff01a41c24dda2e4ecd0f395b18f5cfa62" + "sha256" : "50b1a0b00d10b4e02ddcbe9e9860328968d400e86cbda4c0ed2bd2919947e315" }, { "name" : "x86_64-6.1", "arch" : "x86_64", "version" : "6.1.176", "asset" : "vmlinux-x86_64-6.1", - "sha256" : "3a9a115ce4c07951dd83f9ba048607a890e59118d21d23f5f3135e41ef2ff05f" + "sha256" : "fd24f0ce7c5d6f23d86c9cf7c63a140b46bcf5e7756a9d522756a80ea89ce5e4" }, { "name" : "aarch64-5.10", "arch" : "aarch64", "version" : "5.10.259", "asset" : "vmlinux-aarch64-5.10", - "sha256" : "853306d4d1c9110208c5df21c8602bc40d68556ce7fd3519e2f08e296460606a" + "sha256" : "de5009cb24e6204a6dedc3baf1790c8f3016bd724d4745e36e12f2ea45ce5bd2" }, { "name" : "aarch64-6.1", "arch" : "aarch64", "version" : "6.1.176", "asset" : "vmlinux-aarch64-6.1", - "sha256" : "d7ecc631a3b59e0d66c8960b35a3374f402e45fb46179507c5aa48b4629e3017" + "sha256" : "f324c34ee5536690e39e65991393d5813ad34b4a991cfb85ca65440fba9ec6fa" }, { "name" : "x86_64-5.10-no-acpi", "arch" : "x86_64", "version" : "5.10.259", "asset" : "vmlinux-x86_64-5.10-no-acpi", - "sha256" : "1b8bea12ee405322cad329f0a3ff20e36bb801bc11baabde9832b69f29072709" + "sha256" : "8c1cea3bd8b54f05a1fa13dbbc3c46b17b1df28875720a5b59a06971daaf32f7" } ] } \ No newline at end of file diff --git a/test/hyper/node/fire_vmm/vm_linux/manifest_test.exs b/test/hyper/node/fire_vmm/vm_linux/manifest_test.exs index c2025eb6..20e6715a 100644 --- a/test/hyper/node/fire_vmm/vm_linux/manifest_test.exs +++ b/test/hyper/node/fire_vmm/vm_linux/manifest_test.exs @@ -28,6 +28,6 @@ defmodule Hyper.Node.FireVMM.VmLinux.ManifestTest do assert Manifest.asset_url(build) == "https://github.com/harmont-dev/hyper-vmlinux/releases/download/" <> - "release-54d6c3f843c01fb55107a024cca5bf60af235c42/vmlinux-x86_64-6.1" + "release-bab2b3a92382df318e60b6b52376d2a9f5b5456c/vmlinux-x86_64-6.1" end end From d6cd9faee7e0f4aa4564168c60797934dc361b16 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 8 Jul 2026 03:09:20 +0000 Subject: [PATCH 2/2] fix(fire_vmm): verify cached vmlinux hashes, not just presence install_state/2 counted an asset as installed if the file existed, so a manifest bump never invalidated the redist cache: nodes kept booting the previous release's kernels from the same asset paths forever. A cached file now counts as installed only when its SHA-256 matches the manifest; stale files are re-fetched (all stale -> reinstall in place, partially stale -> wipe and reinstall). Found while validating this branch's vmlinux bump: a provisioned host with the old release cached silently "passed" E2E on the old kernels. --- lib/hyper/node/fire_vmm/vm_linux/provider.ex | 23 ++++++---- .../node/fire_vmm/vm_linux/provider_test.exs | 42 +++++++++++++++++-- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/lib/hyper/node/fire_vmm/vm_linux/provider.ex b/lib/hyper/node/fire_vmm/vm_linux/provider.ex index b01daf84..c1155388 100644 --- a/lib/hyper/node/fire_vmm/vm_linux/provider.ex +++ b/lib/hyper/node/fire_vmm/vm_linux/provider.ex @@ -50,23 +50,32 @@ defmodule Hyper.Node.FireVMM.VmLinux.Provider do end @doc """ - Install state of `builds` under `dir`: `:ok` if every asset file is present; - `{:error, :not_installed}` if none are; `{:error, :bad_install}` if only some - are (a partial/corrupt install - `Redist.File` keeps existing files, so - the remedy is to wipe and reinstall). + Install state of `builds` under `dir`: `:ok` if every asset file is present + with the manifest's SHA-256; `{:error, :not_installed}` if none are; + `{:error, :bad_install}` if only some are (a partial/corrupt install - + `Redist.File` keeps existing files, so the remedy is to wipe and reinstall). + + Presence alone is not enough: after a manifest bump the old release's kernels + sit at the same asset paths, and skipping the hash check would keep booting + guests on the previous release forever. """ @spec install_state(Path.t(), [Manifest.Build.t()]) :: :ok | {:error, :not_installed | :bad_install} def install_state(dir, builds) do - present = Enum.count(builds, &File.regular?(build_path(dir, &1))) + installed = Enum.count(builds, &installed?(dir, &1)) cond do - present == length(builds) -> :ok - present == 0 -> {:error, :not_installed} + installed == length(builds) -> :ok + installed == 0 -> {:error, :not_installed} true -> {:error, :bad_install} end end + defp installed?(dir, build) do + path = build_path(dir, build) + File.regular?(path) and Redist.Sha256.file(path) == build.sha256 + end + defp install_all(builds) do Enum.reduce_while(builds, :ok, fn build, :ok -> case Redist.File.install( diff --git a/test/hyper/node/fire_vmm/vm_linux/provider_test.exs b/test/hyper/node/fire_vmm/vm_linux/provider_test.exs index 7ddf1db6..9e41bbad 100644 --- a/test/hyper/node/fire_vmm/vm_linux/provider_test.exs +++ b/test/hyper/node/fire_vmm/vm_linux/provider_test.exs @@ -18,8 +18,11 @@ defmodule Hyper.Node.FireVMM.VmLinux.ProviderTest do assert Provider.install_state(dir, builds) == {:error, :not_installed} end - test "install_state/2 is :ok when every asset file is present", %{dir: dir, builds: builds} do - for b <- builds, do: File.write!(Path.join(dir, b.asset), "kernel") + test "install_state/2 is :ok when every asset file is present with its manifest hash", %{ + dir: dir, + builds: builds + } do + builds = Enum.map(builds, &write_asset(dir, &1)) assert Provider.install_state(dir, builds) == :ok end @@ -28,8 +31,30 @@ defmodule Hyper.Node.FireVMM.VmLinux.ProviderTest do builds: builds } do assert length(builds) > 1 - [first | _] = builds - File.write!(Path.join(dir, first.asset), "kernel") + [first | rest] = builds + + assert Provider.install_state(dir, [write_asset(dir, first) | rest]) == + {:error, :bad_install} + end + + test "install_state/2 is :not_installed when every present file has a stale hash", %{ + dir: dir, + builds: builds + } do + # A manifest bump leaves the previous release's kernels at the same asset + # paths; they must not count as installed or nodes would boot old kernels. + for b <- builds, do: File.write!(Path.join(dir, b.asset), "stale #{b.asset}") + assert Provider.install_state(dir, builds) == {:error, :not_installed} + end + + test "install_state/2 is :bad_install when some present files have a stale hash", %{ + dir: dir, + builds: builds + } do + assert length(builds) > 1 + [first | rest] = builds + builds = [write_asset(dir, first) | rest] + for b <- rest, do: File.write!(Path.join(dir, b.asset), "stale #{b.asset}") assert Provider.install_state(dir, builds) == {:error, :bad_install} end @@ -46,4 +71,13 @@ defmodule Hyper.Node.FireVMM.VmLinux.ProviderTest do test "path/1 rejects an unknown build name" do assert Provider.path("nope") == {:error, {:unknown_build, "nope"}} end + + # Writes fabricated content for `build` under `dir` and returns the build + # with its sha256 matching that content, so install_state/2 sees it as a + # faithful install. + defp write_asset(dir, build) do + content = "kernel #{build.asset}" + File.write!(Path.join(dir, build.asset), content) + %{build | sha256: Base.encode16(:crypto.hash(:sha256, content), case: :lower)} + end end