Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/heimdall

crates/heimdall-probe/src/codegen.rs

// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Ignalina ApS and Vetra Automation AB

//! Code generation (spec §3.1 step 4): `profiles/<workload>/<arch>-<libc>.rs`
//! (a `const` [`heimdall_seccomp::StaticProfile`]), the same profile as
//! reviewable `.json`, and the OCI seccomp `.oci.json`. Heimdall is a
//! defensive sandbox; the output is deterministic (no dates, sorted), so a
//! regenerated profile diffs only where the workload changed.

use std::fmt::Write as _;

use heimdall_seccomp::bpf::Mode;
use heimdall_seccomp::{Action, Arch, Libc, Width};

use crate::minimise::Generated;

fn action(a: Action) -> String {
    match a {
        Action::Allow => "Allow".into(),
        Action::Log => "Log".into(),
        Action::Errno(e) => format!("Errno({e})"),
        Action::UserNotif => "UserNotif".into(),
        Action::Trap => "Trap".into(),
        Action::KillThread => "KillThread".into(),
        Action::KillProcess => "KillProcess".into(),
    }
}

fn arch(a: Arch) -> &'static str {
    match a {
        Arch::X86_64 => "X86_64",
        Arch::Aarch64 => "Aarch64",
    }
}

fn libc_name(l: Libc) -> &'static str {
    match l {
        Libc::Glibc => "Glibc",
        Libc::Musl => "Musl",
    }
}

fn comment_list(out: &mut String, title: &str, items: &[String]) {
    if items.is_empty() {
        return;
    }
    let _ = writeln!(out, "//\n// {title}:");
    for i in items {
        let _ = writeln!(out, "//   {i}");
    }
}

/// The first lines of every generated `.rs`. A generated profile is DATA
/// about the profiled program, not part of Heimdall: it is MIT-0 (OSI
/// approved, no attribution, accepted where code licences are checked; CC0
/// is not, for code, in some policies), so MIT, Apache and proprietary
/// repos can check it in next to the program it describes.
pub const GENERATED_HEADER: &str = "// SPDX-License-Identifier: MIT-0\n\
// This file is generated output of Heimdall (`heimdall profile gen`): data about the profiled\n\
// program, not covered by Heimdall's AGPL. Use it under any licence, including the program's own.\n";

/// The `.rs` file: plain `//` comments and fully qualified paths, so it can
/// be `include!`d anywhere.
pub fn to_rust(g: &Generated) -> String {
    let p = &g.profile;
    let r = &g.review;
    let mut o = String::new();
    o.push_str(GENERATED_HEADER);
    let _ = writeln!(
        o,
        "// GENERATED by heimdall-probe (`heimdall profile gen {}`). Do not edit by hand:",
        p.workload
    );
    let _ = writeln!(
        o,
        "// probe, regenerate and review the diff (spec §3.1). Heimdall is a defensive sandbox;"
    );
    let _ = writeln!(
        o,
        "// this is the whole syscall allowlist of `{}` on {} {}.",
        p.workload, p.arch, p.libc
    );
    let _ = writeln!(
        o,
        "//\n// probed with {} run(s): {}",
        r.runs.len(),
        r.runs.join(", ")
    );
    let fmt = |f: &crate::minimise::Finding| format!("{} (by {})", f.call, f.runs.join(", "));
    comment_list(
        &mut o,
        "near-miss, seen only on error paths",
        &r.near_miss.iter().map(fmt).collect::<Vec<_>>(),
    );
    comment_list(
        &mut o,
        "left out: PROT_WRITE|PROT_EXEC",
        &r.wx.iter().map(fmt).collect::<Vec<_>>(),
    );
    comment_list(&mut o, "bare allow (arguments varied)", &r.bare);
    comment_list(
        &mut o,
        "unknown calls (never allowed)",
        &r.unknown.iter().map(fmt).collect::<Vec<_>>(),
    );
    comment_list(&mut o, "fixed rules", &r.fixed);
    comment_list(&mut o, "notes", &r.notes);
    let _ = writeln!(o);
    let _ = writeln!(
        o,
        "/// The seccomp profile of `{}` on {} {}.",
        p.workload, p.arch, p.libc
    );
    let _ = writeln!(
        o,
        "pub const PROFILE: heimdall_seccomp::StaticProfile = heimdall_seccomp::StaticProfile {{"
    );
    let _ = writeln!(o, "    workload: {:?},", p.workload);
    let _ = writeln!(o, "    arch: heimdall_seccomp::Arch::{},", arch(p.arch));
    let _ = writeln!(
        o,
        "    libc: heimdall_seccomp::Libc::{},",
        libc_name(p.libc)
    );
    let _ = writeln!(o, "    rules: &[");
    for rule in &p.rules {
        let _ = writeln!(o, "        heimdall_seccomp::StaticRule {{");
        let _ = writeln!(o, "            syscall: {:?},", rule.syscall);
        let _ = writeln!(
            o,
            "            action: heimdall_seccomp::Action::{},",
            action(rule.action)
        );
        if rule.when.is_empty() {
            let _ = writeln!(o, "            when: &[],");
        } else {
            let _ = writeln!(o, "            when: &[");
            for alt in &rule.when {
                let _ = writeln!(o, "                &[");
                for c in alt {
                    let w = match c.width {
                        Width::W32 => "W32",
                        Width::W64 => "W64",
                    };
                    let _ = writeln!(
                        o,
                        "                    heimdall_seccomp::ArgCmp {{ arg: {}, width: heimdall_seccomp::Width::{w}, mask: {:#x}, value: {:#x} }},",
                        c.arg, c.mask, c.value
                    );
                }
                let _ = writeln!(o, "                ],");
            }
            let _ = writeln!(o, "            ],");
        }
        let _ = writeln!(o, "        }},");
    }
    let _ = writeln!(o, "    ],");
    let _ = writeln!(o, "}};");
    o
}

/// The `.oci.json` file (enforcing: default `SCMP_ACT_KILL_PROCESS`).
pub fn to_oci_json(g: &Generated) -> String {
    let v = heimdall_seccomp::oci::to_oci(&g.profile, &Mode::Enforce);
    serde_json::to_string_pretty(&v).expect("json") + "\n"
}

/// The three files for a generated profile: (`.rs`, `.json`, `.oci.json`).
pub fn files(g: &Generated) -> (String, String, String) {
    (to_rust(g), g.to_json(), to_oci_json(g))
}