NVIDIA / NVIDIA/nvrc

libc-free Rust and hardened_std

Open
#118 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Rust
Stars
45
Forks
22
Avg merge
8h 44m
Merged PRs (30d)
6

Description

Summary

This issue tracks the development of hardened_std, a security-hardened replacement for Rust's standard library, specifically designed for NVRC's minimal init use case in confidential VMs.

Status: Planning

Motivation

NVRC runs as PID 1 in ephemeral confidential VMs with NVIDIA GPUs. As the init process, it has full system access, making security paramount. The standard library provides too many capabilities and too much attack surface for our constrained environment.

Key drivers:

  1. Reduced attack surface - Less compiled code = fewer potential vulnerabilities
  2. Enforced security constraints - Whitelist-only access to filesystem, processes, and sockets
  3. no_std compatibility - Enable smaller binary size and pure syscall interface
  4. Fail-fast philosophy - In an ephemeral VM with fresh filesystem, if something exists that shouldn't, it's an error

Design Principles

1. Fresh Filesystem Assumption

NVRC boots in an ephemeral VM with a fresh filesystem. No defensive "remove if exists" logic needed:

// Traditional approach (hidden bugs):
if path.exists() { remove_file(path)?; }
create_file(path)?;

// hardened_std approach (fail-fast):
create_file(path)?;  // Fails if exists - something is wrong!
2. Whitelist-Only Access

All operations restricted to explicitly allowed paths/binaries:

const ALLOWED_PATHS: &[&str] = &["/proc/sys/", "/dev/", "/var/run/", "/sys/"];
const ALLOWED_BINARIES: &[&str] = &[
    "/bin/nvidia-persistenced",
    "/bin/nvidia-smi", 
    "/sbin/modprobe",
    "/usr/bin/kata-agent",
];
3. Static Arguments Only

Process arguments must be &'static str - prevents runtime injection:

// Allowed:
Command::new("/sbin/modprobe").args(&["nvidia"]).spawn()?;

// Not allowed (won't compile with strict mode):
Command::new("/bin/sh").args(&["-c", &user_input]).spawn()?;
4. Size Limits

Write operations have enforced size limits (100 bytes sufficient for sysctl values).

Proposed Modules

Module Description
hardened_std::fs File operations with path whitelisting, size limits
hardened_std::process Process execution with binary whitelisting
hardened_std::os::unix::net Unix sockets with path whitelisting
hardened_std::panic VM power-off on panic
hardened_std::collections Stack-allocated HashMap
hardened_std::kernlog Direct syslog via libc
hardened_std::os::fd AsFd/AsRawFd traits

no_std Transition Roadmap

Planned
  • hardened_std::fs - File operations with path whitelisting
  • hardened_std::process - Process execution with binary whitelisting
  • hardened_std::os::unix::net - Unix sockets with path whitelisting
  • Replace std::sync::Once with once_cell (no_std compatible)
  • hardened_std::fs::exists() - Replace std::path::Path::exists()
  • hardened_std::panic - Panic hook and power_off for VM shutdown
  • Implement std::os::fd::AsFd for hardened types
  • Full #![no_std] main.rs with panic handler and global allocator
  • Replace nix crate with direct libc syscalls
  • Replace kernlog crate with hardened_std::kernlog
  • Replace rlimit crate with direct libc calls
  • Remove anyhow dependency (use hardened_std::error)
  • Audit all dependencies for no_std compatibility

Technical Details

no_std Binary Structure
#![no_std]
#![no_main]

extern crate alloc;

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    // Write to stderr, sync, power off
    hardened_std::panic::power_off()
}

#[global_allocator]
static ALLOCATOR: LibcAllocator = LibcAllocator;

#[no_mangle]
pub extern "C" fn main() -> isize { /* ... */ }
Build Configuration

Requires nightly Rust with -Zbuild-std:

# .cargo/config.toml
[target.x86_64-unknown-linux-musl]
build-std = ["core", "alloc"]
rustflags = ["-C", "panic=abort"]

Dependencies no_std Status

Crate no_std? Action
log Yes Keep (default-features = false)
once_cell Yes Keep (features = ["alloc"])
libc Yes Keep
cfg-if Yes Keep
anyhow Yes Replace with hardened_std::error
nix No Replace with direct libc
kernlog No Replace with hardened_std::kernlog
rlimit No Replace with direct libc

Security Benefits

  1. Compile-time enforcement - Invalid paths/binaries won't compile
  2. Runtime validation - Extra layer for dynamic values
  3. Fail-fast - Unexpected state causes immediate failure
  4. Minimal surface - Only implement what's actually needed
  5. No hidden behavior - Direct syscalls, no std abstractions

Open Questions

  • Best approach for incremental migration vs. big-bang switch?
  • How to handle test dependencies that need std?
  • Should the whitelist be configurable via features?

References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ARCHITECTURE.md's libc-free Rust section, then inspect the planned modules and the no_std binary structure in this issue alongside main.rs and .cargo/config.toml. The issue has no single implementation target; completion would require choosing and delivering a defined migration step from the roadmap, with its tests and build configuration established.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.