vmsilo/flake.nix
Davíð Steinn Geirsson 1b826e4dd7 build: update flake.nix to include vmsilo-vsock in both Rust builds
Uses fileset.toSource to widen src for vmsilo-tools and vmsilo-dbus-proxy
so both can resolve the shared vmsilo-vsock path dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:38:55 +00:00

648 lines
24 KiB
Nix

{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
wayland-proxy-virtwl = {
url = "git+https://git.dsg.is/dsg/wayland-proxy-virtwl.git?submodules=1";
inputs.nixpkgs.follows = "nixpkgs";
};
crosvm = {
url = "git+https://git.dsg.is/dsg/crosvm.git?ref=vmsilo&submodules=1";
inputs.nixpkgs.follows = "nixpkgs";
};
vhost-device = {
url = "git+https://git.dsg.is/dsg/vhost-device.git";
inputs.nixpkgs.follows = "nixpkgs";
};
cloud-hypervisor = {
url = "git+https://git.dsg.is/dsg/cloud-hypervisor.git";
inputs.nixpkgs.follows = "nixpkgs";
};
usbip-rs = {
url = "git+https://git.dsg.is/dsg/usbip-rs.git";
inputs.nixpkgs.follows = "nixpkgs";
};
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
wayland-proxy-virtwl,
crosvm,
vhost-device,
cloud-hypervisor,
usbip-rs,
treefmt-nix,
rust-overlay,
}:
let
usbip-rs-input = usbip-rs;
eachSystem = nixpkgs.lib.genAttrs [
"x86_64-linux"
"aarch64-linux"
];
# Build NixOS-based rootfs as qcow2 image
makeRootfsNixos =
system:
{
guestPrograms ? [ ],
guestConfig ? [ ],
usbip-rs ? usbip-rs-input.packages.${system}.default,
}:
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.callPackage (import ./rootfs-nixos) {
inherit guestPrograms guestConfig usbip-rs;
wayland-proxy-virtwl = wayland-proxy-virtwl.packages.${system}.default;
};
# Build vmsilo-balloond Rust binary
buildVmsiloBalloond =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.rustPlatform.buildRustPackage {
pname = "vmsilo-balloond";
version = "0.1.0";
src = ./vmsilo-balloond;
cargoLock = {
lockFile = ./vmsilo-balloond/Cargo.lock;
};
};
# Build vmsilo-dbus-proxy Rust binaries
buildVmsiloDbusProxy =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
src = pkgs.lib.fileset.toSource {
root = ./.;
fileset = pkgs.lib.fileset.unions [
./vmsilo-dbus-proxy
./vmsilo-vsock
];
};
in
pkgs.rustPlatform.buildRustPackage {
pname = "vmsilo-dbus-proxy";
version = "0.1.0";
inherit src;
sourceRoot = "source/vmsilo-dbus-proxy";
cargoLock = {
lockFile = ./vmsilo-dbus-proxy/Cargo.lock;
};
};
# Build vmsilo-wayland-seccontext Rust binary
buildVmsiloWaylandSeccontext =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.rustPlatform.buildRustPackage {
pname = "vmsilo-wayland-seccontext";
version = "0.1.0";
src = ./vmsilo-wayland-seccontext;
cargoLock = {
lockFile = ./vmsilo-wayland-seccontext/Cargo.lock;
};
nativeBuildInputs = with pkgs; [ pkg-config ];
buildInputs = with pkgs; [ wayland ];
};
# Build vmsilo-tools workspace
buildVmsiloTools =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
src = pkgs.lib.fileset.toSource {
root = ./.;
fileset = pkgs.lib.fileset.unions [
./vmsilo-tools
./vmsilo-vsock
];
};
in
pkgs.rustPlatform.buildRustPackage {
pname = "vmsilo-tools";
version = "0.1.0";
inherit src;
sourceRoot = "source/vmsilo-tools";
cargoLock = {
lockFile = ./vmsilo-tools/Cargo.lock;
};
};
# Build vmsilo-device-tray
buildVmsiloDeviceTray =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.callPackage ./vmsilo-device-tray/package.nix {
ksniPatch = ./patches/ksni-about-to-show-refresh.patch;
};
# treefmt configuration
treefmtConfig = {
projectRootFile = "flake.nix";
programs.nixfmt.enable = true;
};
in
{
formatter = eachSystem (
system:
(treefmt-nix.lib.evalModule nixpkgs.legacyPackages.${system} treefmtConfig).config.build.wrapper
);
packages = eachSystem (system: {
default = makeRootfsNixos system { };
rootfs-nixos = makeRootfsNixos system { };
vmsilo-balloond = buildVmsiloBalloond system;
vmsilo-dbus-proxy = buildVmsiloDbusProxy system;
vmsilo-wayland-seccontext = buildVmsiloWaylandSeccontext system;
vmsilo-tools = buildVmsiloTools system;
vmsilo-device-tray = buildVmsiloDeviceTray system;
"cloud-hypervisor" = cloud-hypervisor.packages.${system}.cloud-hypervisor;
decoration-tests =
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.stdenv.mkDerivation {
pname = "decoration-tests";
version = "0.1.0";
src = ./wayland_decoration_tests;
nativeBuildInputs = with pkgs; [
wayland-scanner
pkg-config
];
buildInputs = [ pkgs.wayland ];
makeFlags = [
"WAYLAND_PROTOCOLS=${pkgs.wayland-protocols}/share/wayland-protocols"
"PLASMA_WAYLAND_PROTOCOLS=${pkgs.kdePackages.plasma-wayland-protocols}/share/plasma-wayland-protocols"
"WLR_PROTOCOLS=${pkgs.wlr-protocols}/share/wlr-protocols"
];
installPhase = ''
mkdir -p $out/bin
cp test-csd-request test-no-decoration-protocol test-mode-none test-server-decoration-none test-fullscreen test-layer-shell test-large-popup test-subsurface-overflow test-ssd-request $out/bin/
'';
};
});
devShells = eachSystem (system: {
default =
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.mkShell {
buildInputs = with pkgs; [
# Rust toolchain
cargo
rustc
rust-analyzer
rustfmt
clippy
# Build dependencies
pkg-config
];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
decoration-tests =
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.mkShell {
buildInputs = with pkgs; [
wayland
wayland-protocols
kdePackages.plasma-wayland-protocols
wlr-protocols
wayland-scanner
pkg-config
gcc
];
WAYLAND_PROTOCOLS = "${pkgs.wayland-protocols}/share/wayland-protocols";
PLASMA_WAYLAND_PROTOCOLS = "${pkgs.kdePackages.plasma-wayland-protocols}/share/plasma-wayland-protocols";
WLR_PROTOCOLS = "${pkgs.wlr-protocols}/share/wlr-protocols";
};
fuzz =
let
pkgs = nixpkgs.legacyPackages.${system};
rust-nightly = rust-overlay.packages.${system}.rust-nightly;
in
pkgs.mkShell {
buildInputs = [
rust-nightly
pkgs.cargo-fuzz
];
nativeBuildInputs = [ pkgs.stdenv.cc ];
};
fuzz-afl =
let
pkgs = nixpkgs.legacyPackages.${system};
rust-nightly = rust-overlay.packages.${system}.rust-nightly;
aflplusplus = usbip-rs-input.packages.${system}.aflplusplus;
in
pkgs.mkShell {
buildInputs = [
rust-nightly
usbip-rs-input.packages.${system}.cargo-afl
usbip-rs-input.packages.${system}.symcc
];
nativeBuildInputs = [ pkgs.stdenv.cc ];
CARGO_AFL_DIR = "${aflplusplus}";
CARGO_AFL_LLVM_DIR = "${aflplusplus}/lib/afl";
};
});
apps = eachSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
rust-nightly = rust-overlay.packages.${system}.rust-nightly;
fuzz-cargo-dbus-proxy = pkgs.writeShellScriptBin "fuzz-cargo-dbus-proxy" ''
set -euo pipefail
export PATH="${rust-nightly}/bin:${pkgs.cargo-fuzz}/bin:${pkgs.stdenv.cc}/bin:$PATH"
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/vmsilo-dbus-proxy"
if [ $# -eq 0 ]; then
cargo fuzz list
else
target="$1"
shift
fork=0
args=()
for arg in "$@"; do
case "$arg" in
--fork=*) fork=''${arg#--fork=} ;;
*) args+=("$arg") ;;
esac
done
if [ "$fork" -gt 0 ]; then
while true; do
cargo fuzz run "$target" -- -max_len=1048576 "-fork=$fork" "''${args[@]}" || true
echo "--- fuzzer exited, restarting (artifacts saved) ---"
done
else
cargo fuzz run "$target" -- -max_len=1048576 "''${args[@]}"
fi
fi
'';
fuzz-clean-cargo-dbus-proxy = pkgs.writeShellScriptBin "fuzz-clean-cargo-dbus-proxy" ''
set -euo pipefail
export PATH="${rust-nightly}/bin:${pkgs.cargo-fuzz}/bin:${pkgs.stdenv.cc}/bin:${pkgs.coreutils}/bin:$PATH"
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/vmsilo-dbus-proxy"
if [ $# -eq 0 ]; then
echo "Usage: fuzz-clean-cargo-dbus-proxy <target>"
echo "Available targets:"
cargo fuzz list
exit 1
fi
target="$1"
dir="fuzz/artifacts/$target"
if [ ! -d "$dir" ]; then
echo "No artifacts directory: $dir"
exit 0
fi
shopt -s nullglob
files=("$dir"/crash-* "$dir"/oom-* "$dir"/timeout-*)
if [ ''${#files[@]} -eq 0 ]; then
echo "No artifacts to test."
exit 0
fi
echo "Building $target..."
if ! cargo fuzz build "$target" 2>&1; then
echo "Build failed not touching artifacts."
exit 1
fi
echo "Testing ''${#files[@]} artifacts for $target..."
removed=0
kept=0
for f in "''${files[@]}"; do
if timeout 30 cargo fuzz run "$target" "$f" -- -max_len=1048576 >/dev/null 2>&1; then
rm "$f"
removed=$((removed + 1))
else
kept=$((kept + 1))
fi
echo -ne "\r tested $((removed + kept))/''${#files[@]}, removed $removed, kept $kept"
done
echo ""
echo "Done: removed $removed fixed, kept $kept still-crashing."
'';
afl-env = ''
export CARGO_AFL_DIR="${usbip-rs-input.packages.${system}.aflplusplus}"
export CARGO_AFL_LLVM_DIR="${usbip-rs-input.packages.${system}.aflplusplus}/lib/afl"
export PATH="${usbip-rs-input.packages.${system}.cargo-afl}/bin:${rust-nightly}/bin:${
usbip-rs-input.packages.${system}.symcc
}/bin:${pkgs.stdenv.cc}/bin:${pkgs.coreutils}/bin:$PATH"
root="$(${pkgs.git}/bin/git rev-parse --show-toplevel)"
'';
symcc-companion = pkgs.writeShellScriptBin "symcc-companion" ''
set -uo pipefail
output_dir="$1"
symcc_binary="$2"
symcc_workdir="$output_dir/symcc"
logfile="$symcc_workdir/companion.log"
mkdir -p "$symcc_workdir/queue" "$symcc_workdir/tmp"
log() { echo "[symcc $(date +%H:%M:%S)] $*" >> "$logfile"; }
log "companion started (pid $$)"
# Wait for any AFL++ instance to create a queue directory
while ! ls -d "$output_dir"/*/queue >/dev/null 2>&1; do sleep 1; done
log "AFL++ queue detected"
processed="$symcc_workdir/.processed"
touch "$processed"
shopt -s nullglob
while true; do
# Process queue entries from all AFL++ instances
for f in "$output_dir"/*/queue/id:*; do
[ -f "$f" ] || continue
# Track by full path to avoid collisions across instances
grep -qxF "$f" "$processed" 2>/dev/null && continue
rm -rf "$symcc_workdir/tmp"/*
SYMCC_OUTPUT_DIR="$symcc_workdir/tmp" \
${pkgs.coreutils}/bin/timeout 30 "$symcc_binary" < "$f" >/dev/null 2>&1 || true
new_count=0
for new in "$symcc_workdir/tmp"/*; do
[ -f "$new" ] || continue
# Write to symcc/queue/ AFL++ auto-syncs from all
# instance directories under the output dir
cp "$new" "$symcc_workdir/queue/id:symcc_''${RANDOM}_''${RANDOM}" 2>/dev/null || true
new_count=$((new_count + 1))
done
log "processed $(basename "$f") -> $new_count new inputs"
echo "$f" >> "$processed"
done
sleep 5
done
'';
fuzz-afl-dbus-proxy = pkgs.writeShellScriptBin "fuzz-afl-dbus-proxy" ''
set -euo pipefail
${afl-env}
manifest="$root/vmsilo-dbus-proxy/fuzz-afl/Cargo.toml"
if [ $# -eq 0 ]; then
echo "Available targets:"
${pkgs.gawk}/bin/awk '/^\[\[bin\]\]/{found=1} found && /^name = "/{gsub(/"/, "", $3); print " " $3; found=0}' "$manifest"
exit 0
fi
target="$1"
shift
# Parse --jobs=N or --jobs N from arguments
jobs=1
afl_args=()
skip_next=false
for arg in "$@"; do
if $skip_next; then jobs="$arg"; skip_next=false; continue; fi
case "$arg" in
--jobs=*) jobs="''${arg#--jobs=}" ;;
--jobs) skip_next=true ;;
*) afl_args+=("$arg") ;;
esac
done
afl_target_dir="$root/vmsilo-dbus-proxy/fuzz-afl/target/afl"
symcc_target_dir="$root/vmsilo-dbus-proxy/fuzz-afl/target/symcc"
echo "Building $target with cargo-afl (plugins + CmpLog)..."
CARGO_TARGET_DIR="$afl_target_dir" \
cargo afl build --manifest-path "$manifest" --release --bin "$target"
echo "Building $target with SymCC instrumentation..."
CC=symcc CXX=sym++ RUSTFLAGS="-C linker=symcc -Clink-arg=$CARGO_AFL_LLVM_DIR/afl-compiler-rt.o" \
CARGO_TARGET_DIR="$symcc_target_dir" \
cargo build --manifest-path "$manifest" --release --bin "$target"
corpus_dir="$root/vmsilo-dbus-proxy/fuzz-afl/corpus/$target"
output_dir="$root/vmsilo-dbus-proxy/fuzz-afl/output/$target"
mkdir -p "$corpus_dir" "$output_dir"
# Ensure corpus has at least one seed
if [ -z "$(ls -A "$corpus_dir" 2>/dev/null)" ]; then
echo "Warning: corpus dir is empty, creating minimal seed"
printf '\x00' > "$corpus_dir/seed-minimal"
fi
# Use a systemd slice for proper process management.
# All background fuzzers and the SymCC companion run as
# transient systemd units; stopping the slice kills them all.
slice="fuzz_afl_dbus_proxy_$target"
${pkgs.systemd}/bin/systemctl --user stop "$slice.slice" 2>/dev/null || true
# Clean stale AFL++ lock files from killed previous runs
rm -f "$output_dir"/*/is_main_node "$output_dir"/*/.cur_input 2>/dev/null || true
trap '${pkgs.systemd}/bin/systemctl --user stop "$slice.slice" 2>/dev/null || true' EXIT
# SymCC companion
${pkgs.systemd}/bin/systemd-run --user --collect \
--slice="$slice" --unit="$slice-symcc" --quiet \
${symcc-companion}/bin/symcc-companion \
"$output_dir" "$symcc_target_dir/release/$target"
# Secondary AFL++ instances (--jobs > 1)
for i in $(seq 2 "$jobs"); do
${pkgs.systemd}/bin/systemd-run --user --collect \
--slice="$slice" --unit="$slice-s$i" --quiet \
--setenv=AFL_AUTORESUME=1 \
"$CARGO_AFL_DIR/bin/afl-fuzz" \
-S "secondary_$i" -p fast \
-i "$corpus_dir" -o "$output_dir" \
-- "$afl_target_dir/release/$target"
done
# Determine main instance flags
if [ "$jobs" -gt 1 ]; then
main_flag="-M main"
else
main_flag=""
fi
echo "Starting AFL++ on $target..."
echo " jobs : $jobs''${jobs:+ (main + $((jobs-1)) secondaries)}"
echo " persistent mode : yes (afl::fuzz! macro)"
echo " CmpLog : -c 0 -l 2AT"
echo " power schedule : -p fast"
echo " LLVM plugins : loaded"
echo " SymCC companion : running (log: $output_dir/symcc/companion.log)"
echo " stop all : systemctl --user stop $slice.slice"
echo ""
# Main instance runs in foreground so the user sees the TUI.
# shellcheck disable=SC2086
AFL_AUTORESUME=1 "$CARGO_AFL_DIR/bin/afl-fuzz" \
$main_flag \
-c 0 -l 2AT -p fast \
-i "$corpus_dir" \
-o "$output_dir" \
"''${afl_args[@]}" \
-- "$afl_target_dir/release/$target"
'';
fuzz-clean-afl-dbus-proxy = pkgs.writeShellScriptBin "fuzz-clean-afl-dbus-proxy" ''
set -euo pipefail
${afl-env}
manifest="$root/vmsilo-dbus-proxy/fuzz-afl/Cargo.toml"
if [ $# -eq 0 ]; then
echo "Usage: fuzz-clean-afl-dbus-proxy <target>"
echo "Available targets:"
${pkgs.gawk}/bin/awk '/^\[\[bin\]\]/{found=1} found && /^name = "/{gsub(/"/, "", $3); print " " $3; found=0}' "$manifest"
exit 1
fi
target="$1"
afl_target_dir="$root/vmsilo-dbus-proxy/fuzz-afl/target/afl"
# Collect crashes from all AFL++ instance directories
shopt -s nullglob
files=()
for d in "$root/vmsilo-dbus-proxy/fuzz-afl/output/$target"/*/crashes; do
files+=("$d"/id:*)
done
if [ ''${#files[@]} -eq 0 ]; then
echo "No crash files to test."
exit 0
fi
echo "Building $target with cargo-afl..."
CARGO_TARGET_DIR="$afl_target_dir" \
cargo afl build --manifest-path "$manifest" --release --bin "$target"
echo "Testing ''${#files[@]} crash files for $target..."
removed=0
kept=0
for f in "''${files[@]}"; do
if timeout 30 "$afl_target_dir/release/$target" < "$f" >/dev/null 2>&1; then
rm "$f"
removed=$((removed + 1))
else
kept=$((kept + 1))
fi
echo -ne "\r tested $((removed + kept))/''${#files[@]}, removed $removed, kept $kept"
done
echo ""
echo "Done: removed $removed fixed, kept $kept still-crashing."
'';
fuzz-gen-corpus = pkgs.writeShellScriptBin "fuzz-gen-corpus" ''
set -euo pipefail
export PATH="${rust-nightly}/bin:${pkgs.stdenv.cc}/bin:$PATH"
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/vmsilo-dbus-proxy"
cargo run --manifest-path fuzz/Cargo.toml --bin generate_seeds
'';
in
{
fuzz-cargo-dbus-proxy = {
type = "app";
program = "${fuzz-cargo-dbus-proxy}/bin/fuzz-cargo-dbus-proxy";
};
fuzz-clean-cargo-dbus-proxy = {
type = "app";
program = "${fuzz-clean-cargo-dbus-proxy}/bin/fuzz-clean-cargo-dbus-proxy";
};
fuzz-afl-dbus-proxy = {
type = "app";
program = "${fuzz-afl-dbus-proxy}/bin/fuzz-afl-dbus-proxy";
};
fuzz-clean-afl-dbus-proxy = {
type = "app";
program = "${fuzz-clean-afl-dbus-proxy}/bin/fuzz-clean-afl-dbus-proxy";
};
fuzz-gen-corpus = {
type = "app";
program = "${fuzz-gen-corpus}/bin/fuzz-gen-corpus";
};
}
);
# Helper function for building custom NixOS rootfs
lib.makeRootfsNixos = makeRootfsNixos;
nixosModules.default =
{
config,
pkgs,
lib,
...
}:
{
imports = [ ./modules ];
# Inject dependencies when module is enabled
config = lib.mkIf config.programs.vmsilo.enable {
programs.vmsilo._internal = {
crosvm = crosvm.packages.${pkgs.stdenv.hostPlatform.system}.default;
"cloud-hypervisor" = cloud-hypervisor.packages.${pkgs.stdenv.hostPlatform.system}.cloud-hypervisor;
vmsilo-wayland-seccontext = buildVmsiloWaylandSeccontext pkgs.stdenv.hostPlatform.system;
wayland-proxy-virtwl = wayland-proxy-virtwl.packages.${pkgs.stdenv.hostPlatform.system}.default;
vhost-device-sound = vhost-device.packages.${pkgs.stdenv.hostPlatform.system}.vhost-device-sound;
vhost-device-gpu = vhost-device.packages.${pkgs.stdenv.hostPlatform.system}.vhost-device-gpu;
vmsilo-balloond = buildVmsiloBalloond pkgs.stdenv.hostPlatform.system;
vmsilo-dbus-proxy = buildVmsiloDbusProxy pkgs.stdenv.hostPlatform.system;
vmsilo-tools = buildVmsiloTools pkgs.stdenv.hostPlatform.system;
"usbip-rs" = usbip-rs-input.packages.${pkgs.stdenv.hostPlatform.system}.default;
"vmsilo-device-tray" = buildVmsiloDeviceTray pkgs.stdenv.hostPlatform.system;
};
};
};
nixosModules.optionalGuestSettings =
# Dark Breeze theme for Qt and GTK apps. Not used by default.
{
config,
pkgs,
lib,
...
}:
{
config = {
environment.etc."xdg/gtk-3.0/settings.ini".text = ''
[Settings]
gtk-theme-name=Breeze-Dark
gtk-application-prefer-dark-theme=true
'';
environment.etc."xdg/gtk-4.0/settings.ini".text = ''
[Settings]
gtk-theme-name=Breeze-Dark
gtk-application-prefer-dark-theme=true
'';
# plasma-integration reads kdeglobals for color scheme and style.
# xdg-desktop-portal-kde reads Colors:Window BackgroundNormal to
# report org.freedesktop.appearance color-scheme preference.
environment.etc."xdg/kdeglobals".text = ''
[General]
ColorScheme=BreezeDark
widgetStyle=breeze
[Icons]
Theme=breeze-dark
[KDE]
widgetStyle=breeze
[Colors:Window]
BackgroundNormal=32,35,38
'';
};
};
};
}