devices: remove --software-tpm support

The software TPM backed by the libtpm2 simulator was only used for
testing; now that we have the vTPM proxy device, the software TPM is no
longer needed.

This also allows removal of the tpm2 submodule (tpm2-sys/libtpm2).

BUG=b:300673042
TEST=tools/dev_container tools/presubmit

Change-Id: I3feb5f715f9f12f832450df712c0f63ed7b4fb13
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4875221
Reviewed-by: Dennis Kempin <denniskempin@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Daniel Verkamp 2023-09-15 14:50:51 -07:00 committed by crosvm LUCI
parent d2e7bc77c6
commit e10df59fd3
22 changed files with 4 additions and 561 deletions

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "tpm2-sys/libtpm2"]
path = tpm2-sys/libtpm2
url = https://chromium.googlesource.com/chromiumos/third_party/tpm2
[submodule "third_party/minigbm"]
path = third_party/minigbm
url = https://chromium.googlesource.com/chromiumos/platform/minigbm

16
Cargo.lock generated
View file

@ -924,7 +924,6 @@ dependencies = [
"system_api",
"tempfile",
"thiserror",
"tpm2",
"tube_transporter",
"usb_util",
"vfio_sys",
@ -2628,21 +2627,6 @@ dependencies = [
"serde",
]
[[package]]
name = "tpm2"
version = "0.1.0"
dependencies = [
"tpm2-sys",
]
[[package]]
name = "tpm2-sys"
version = "0.1.0"
dependencies = [
"anyhow",
"pkg-config",
]
[[package]]
name = "tube_transporter"
version = "0.1.0"

View file

@ -80,8 +80,6 @@ members = [
"system_api",
"third_party/vmm_vhost",
"tools/impl/catapult_converter",
"tpm2-sys",
"tpm2",
"usb_sys",
"usb_util",
"vendor/generic/anti_tamper",
@ -169,10 +167,6 @@ swap = ["aarch64/swap", "arch/swap", "devices/swap", "vm_control/swap", "x86_64/
## Enables collection of VM statistics.
stats = ["devices/stats"]
## Enables trusted platform module emulation for the guest. This relies on the software emulated
## TPM implementation from libtpm2 which is suited only for testing purposes.
tpm = ["devices/tpm"]
## Enables USB host device passthrough via an emulated XHCI controller.
## USB is supported only on unix/linux. The feature is a no-op on windows.
usb = ["devices/usb"]
@ -332,7 +326,6 @@ all-default = [
"slirp",
"swap",
"trace_marker",
"tpm",
"vaapi",
"video-decoder",
"video-encoder",
@ -371,7 +364,6 @@ all-armhf = [
"gdb",
"libvda-stub",
"net",
"tpm",
]
## All features that are compiled and tested for mingw64

View file

@ -15,7 +15,6 @@ gunyah = []
libvda-stub = ["libvda/libvda-stub"]
net = []
geniezone = []
tpm = ["tpm2"]
usb = []
vaapi = ["cros-codecs/vaapi", "downcast-rs", "crc32fast"]
video-decoder = []
@ -82,7 +81,6 @@ smallvec = "1.6.1"
sync = { path = "../common/sync" }
system_api = { path = "../system_api", optional = true }
thiserror = "1.0.20"
tpm2 = { path = "../tpm2", optional = true }
cros_tracing = { path = "../cros_tracing" }
swap = { path = "../swap" }
vmm_vhost = { path = "../third_party/vmm_vhost", features = ["vmm", "device"] }

View file

@ -25,8 +25,6 @@ pub mod pl030;
pub mod pmc_virt;
mod serial;
pub mod serial_device;
#[cfg(feature = "tpm")]
mod software_tpm;
mod suspendable;
mod sys;
mod virtcpufreq;
@ -127,8 +125,6 @@ pub use self::serial_device::SerialDevice;
pub use self::serial_device::SerialHardware;
pub use self::serial_device::SerialParameters;
pub use self::serial_device::SerialType;
#[cfg(feature = "tpm")]
pub use self::software_tpm::SoftwareTpm;
pub use self::suspendable::DeviceState;
pub use self::suspendable::Suspendable;
pub use self::virtcpufreq::VirtCpufreq;

View file

@ -1,33 +0,0 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Software TPM backend using the TPM2 simulator from the `tpm2` crate.
use std::env;
use std::fs;
use std::path::Path;
use anyhow::Context;
use tpm2::Simulator;
use super::virtio::TpmBackend;
pub struct SoftwareTpm {
simulator: Simulator,
}
impl SoftwareTpm {
pub fn new<P: AsRef<Path>>(storage: P) -> anyhow::Result<Self> {
fs::create_dir_all(storage.as_ref()).context("failed to create directory for simulator")?;
env::set_current_dir(storage).context("failed to change into simulator directory")?;
let simulator = Simulator::singleton_in_current_directory();
Ok(SoftwareTpm { simulator })
}
}
impl TpmBackend for SoftwareTpm {
fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
self.simulator.execute_command(command)
}
}

View file

@ -20,7 +20,7 @@ pub mod net;
pub mod pvclock;
mod queue;
mod rng;
#[cfg(any(feature = "tpm", feature = "vtpm"))]
#[cfg(feature = "vtpm")]
mod tpm;
#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
mod video;
@ -88,9 +88,9 @@ pub use self::queue::Queue;
pub use self::queue::QueueConfig;
pub use self::rng::Rng;
pub use self::scsi::Device as ScsiDevice;
#[cfg(any(feature = "tpm", feature = "vtpm"))]
#[cfg(feature = "vtpm")]
pub use self::tpm::Tpm;
#[cfg(any(feature = "tpm", feature = "vtpm"))]
#[cfg(feature = "vtpm")]
pub use self::tpm::TpmBackend;
#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
pub use self::video::VideoDevice;

View file

@ -27,8 +27,7 @@ Here is a (non-comprehensive) list of emulated devices provided by crosvm.
- [`pmem`] - Persistent memory.
- [`rng`] - Entropy source used to seed guest OS's entropy pool.
- [`snd`] - Encodes and decodes audio streams.
- [`tpm`] - Creates a TPM (Trusted Platform Module) device backed by libtpm2 simulator or vTPM
daemon.
- [`tpm`] - Creates a TPM (Trusted Platform Module) device backed by vTPM daemon.
- [`video`] - Allows the guest to leverage the host's video capabilities.
- [`wayland`] - Allows the guest to use the host's Wayland socket.
- [`vsock`] - Enables use of virtual sockets for the guest.

View file

@ -1,17 +0,0 @@
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@include /usr/share/policy/crosvm/common_device.policy
chdir: 1
fstat: 1
fsync: 1
getrandom: 1
getuid: 1
mkdirat: 1
newfstatat: 1
openat: 1
prctl: arg0 == PR_SET_NAME
socket: return EACCES
statx: 1

View file

@ -1,18 +0,0 @@
# Copyright 2018 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@include /usr/share/policy/crosvm/common_device.policy
chdir: 1
fstat: 1
fsync: 1
getrandom: 1
getuid: 1
mkdir: 1
open: 1
openat: 1
prctl: arg0 == PR_SET_NAME
socket: return EACCES
stat: 1
statx: 1

View file

@ -1,19 +0,0 @@
# Copyright 2018 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@include /usr/share/policy/crosvm/common_device.policy
chdir: 1
fstat: 1
fsync: 1
getrandom: 1
getuid: 1
mkdir: 1
newfstatat: 1
open: 1
openat: 1
prctl: arg0 == PR_SET_NAME
socket: return EACCES
stat: 1
statx: 1

View file

@ -2021,13 +2021,6 @@ pub struct RunCommand {
/// path to put the control socket. If PATH is a directory, a name will be generated
pub socket: Option<PathBuf>,
#[cfg(feature = "tpm")]
#[argh(switch)]
#[serde(skip)] // TODO(b/255223604)
#[merge(strategy = overwrite_option)]
/// enable a software emulated trusted platform module device
pub software_tpm: Option<bool>,
#[cfg(feature = "audio")]
#[argh(option, arg_name = "PATH")]
#[serde(skip)] // TODO(b/255223604)
@ -2818,11 +2811,6 @@ impl TryFrom<RunCommand> for super::config::Config {
cfg.vhost_scmi = cmd.vhost_scmi.unwrap_or_default();
}
#[cfg(feature = "tpm")]
{
cfg.software_tpm = cmd.software_tpm.unwrap_or_default();
}
#[cfg(feature = "vtpm")]
{
cfg.vtpm_proxy = cmd.vtpm_proxy.unwrap_or_default();

View file

@ -864,8 +864,6 @@ pub struct Config {
#[cfg(all(windows, feature = "audio"))]
pub snd_split_config: Option<SndSplitConfig>,
pub socket_path: Option<PathBuf>,
#[cfg(feature = "tpm")]
pub software_tpm: bool,
#[cfg(feature = "audio")]
pub sound: Option<PathBuf>,
pub strict_balloon: bool,
@ -1067,8 +1065,6 @@ impl Default for Config {
#[cfg(all(windows, feature = "audio"))]
snd_split_config: None,
socket_path: None,
#[cfg(feature = "tpm")]
software_tpm: false,
#[cfg(feature = "audio")]
sound: None,
strict_balloon: false,

View file

@ -383,16 +383,6 @@ fn create_virtio_devices(
devs.push(create_rng_device(cfg.protection_type, &cfg.jail_config)?);
}
#[cfg(feature = "tpm")]
{
if cfg.software_tpm {
devs.push(create_software_tpm_device(
cfg.protection_type,
&cfg.jail_config,
)?);
}
}
#[cfg(feature = "vtpm")]
{
if cfg.vtpm_proxy {

View file

@ -59,8 +59,6 @@ use devices::BusDeviceObj;
use devices::IommuDevType;
use devices::PciAddress;
use devices::PciDevice;
#[cfg(feature = "tpm")]
use devices::SoftwareTpm;
use devices::VfioDevice;
use devices::VfioDeviceType;
use devices::VfioPciDevice;
@ -490,49 +488,6 @@ pub fn create_virtio_snd_device(
})
}
#[cfg(feature = "tpm")]
pub fn create_software_tpm_device(
protection_type: ProtectionType,
jail_config: &Option<JailConfig>,
) -> DeviceResult {
use std::ffi::CString;
use std::fs;
use std::process;
let (jail, tpm_storage) = if let Some(jail_config) = jail_config {
let mut config = SandboxConfig::new(jail_config, "tpm_device");
config.bind_mounts = true;
let mut jail =
create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
let pid = process::id();
let crosvm_uid = geteuid();
let crosvm_gid = getegid();
let tpm_pid_dir = format!("/run/vm/tpm.{}", pid);
let tpm_storage = PathBuf::from(&tpm_pid_dir);
fs::create_dir_all(&tpm_storage).with_context(|| {
format!("failed to create tpm storage dir {}", tpm_storage.display())
})?;
let tpm_pid_dir_c = CString::new(tpm_pid_dir).expect("no nul bytes");
chown(&tpm_pid_dir_c, crosvm_uid, crosvm_gid).context("failed to chown tpm storage")?;
jail.mount_bind(&tpm_storage, &tpm_storage, true)?;
(Some(jail), tpm_storage)
} else {
// Path used inside cros_sdk which does not have /run/vm.
(None, PathBuf::from("/tmp/tpm-simulator"))
};
let backend = SoftwareTpm::new(tpm_storage).context("failed to create SoftwareTpm")?;
let dev = virtio::Tpm::new(Box::new(backend), virtio::base_features(protection_type));
Ok(VirtioDeviceStub {
dev: Box::new(dev),
jail,
})
}
#[cfg(feature = "vtpm")]
pub fn create_vtpm_proxy_device(
protection_type: ProtectionType,

View file

@ -15,8 +15,6 @@ BUILD_FEATURES: Dict[str, str] = {
# Do not build these on riscv64. They don't yet have riscv64 support of the backing libraries in the
# dev container.
DO_NOT_BUILD_RISCV64 = [
"tpm2",
"tpm2-sys",
"libvda",
"libva",
"ffmpeg",

View file

@ -1,10 +0,0 @@
[package]
name = "tpm2-sys"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2021"
links = "tpm2"
[build-dependencies]
pkg-config = "*"
anyhow = "*"

View file

@ -1,91 +0,0 @@
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use anyhow::bail;
use anyhow::Result;
/// Returns the target triplet prefix for gcc commands. No prefix is required
/// for native builds.
fn get_cross_compile_prefix() -> String {
let target = env::var("TARGET").unwrap();
if env::var("HOST").unwrap() == target {
return String::from("");
}
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let env = if target.ends_with("-gnueabihf") {
String::from("gnueabihf")
} else {
env::var("CARGO_CFG_TARGET_ENV").unwrap()
};
format!("{}-{}-{}-", arch, os, env)
}
fn build_libtpm2(out_dir: &Path) -> Result<()> {
let lib_path = out_dir.join("libtpm2.a");
if lib_path.exists() {
return Ok(());
}
if !Path::new("libtpm2/.git").exists() {
bail!(
"tpm2-sys/libtpm2 source does not exist, did you forget to \
`git submodule update --init`?"
);
}
let make_flags = env::var("CARGO_MAKEFLAGS").unwrap();
let prefix = get_cross_compile_prefix();
let status = Command::new("make")
.env("MAKEFLAGS", make_flags)
.arg(format!("AR={}ar", prefix))
.arg(format!("CC={}gcc", prefix))
.arg(format!("OBJCOPY={}objcopy", prefix))
.arg("CFLAGS=-Wno-error")
.arg(format!("obj={}", out_dir.display()))
.current_dir("libtpm2")
.status()?;
if !status.success() {
bail!("make failed with status: {}", status);
}
Ok(())
}
fn main() -> Result<()> {
// Skip installing dependencies when generating documents.
if std::env::var("CARGO_DOC").is_ok() {
return Ok(());
}
// libtpm2 is unix only
if std::env::var("CARGO_CFG_UNIX").is_err() {
return Ok(());
}
// Use tpm2 package from the standard system location if available.
if pkg_config::Config::new()
.statik(true)
.probe("libtpm2")
.is_ok()
{
return Ok(());
}
// Otherwise build from source
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
build_libtpm2(&out_dir)?;
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rustc-link-lib=static=tpm2");
println!("cargo:rustc-link-lib=ssl");
println!("cargo:rustc-link-lib=crypto");
Ok(())
}

@ -1 +0,0 @@
Subproject commit 96017dcdb1104099315b9e448c85d1489a41ae82

View file

@ -1,24 +0,0 @@
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Bindings for the TPM2 simulator library.
#![cfg(unix)]
use std::os::raw::c_int;
use std::os::raw::c_uchar;
use std::os::raw::c_uint;
extern "C" {
pub fn TPM_Manufacture(firstTime: c_int) -> c_int;
pub fn _plat__SetNvAvail();
pub fn _plat__Signal_PowerOn() -> c_int;
pub fn _TPM_Init();
pub fn ExecuteCommand(
requestSize: c_uint,
request: *mut c_uchar,
responseSize: *mut c_uint,
response: *mut *mut c_uchar,
);
}

View file

@ -1,8 +0,0 @@
[package]
name = "tpm2"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2021"
[dependencies]
tpm2-sys = { path = "../tpm2-sys" }

View file

@ -1,229 +0,0 @@
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! TPM2 (Trusted Platform Module 2.0) simulator.
#![cfg(unix)]
use std::os::raw::c_int;
use std::os::raw::c_uint;
use std::ptr;
use std::slice;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
static SIMULATOR_EXISTS: AtomicBool = AtomicBool::new(false);
/// A libtpm2-based TPM simulator.
///
/// At most one simulator may exist per process because libtpm2 uses a static
/// global response buffer.
///
/// # Examples
///
/// ```no_run
/// let mut simulator = tpm2::Simulator::singleton_in_current_directory();
///
/// let command = &[ /* ... */ ];
/// let response = simulator.execute_command(command);
/// println!("{:?}", response);
/// ```
pub struct Simulator {
_priv: (),
}
impl Simulator {
/// Initializes a TPM simulator in the current working directory.
///
/// # Panics
///
/// Panics if a TPM simulator has already been initialized by this process.
pub fn singleton_in_current_directory() -> Self {
let already_existed = SIMULATOR_EXISTS.swap(true, Ordering::SeqCst);
if already_existed {
panic!("libtpm2 simulator singleton already exists");
}
// Based on trunks:
// https://chromium.googlesource.com/chromiumos/platform2/+/e4cf13c05773f3446bd76a13c4e37f0b80728711/trunks/tpm_simulator_handle.cc
tpm_manufacture(true);
plat_set_nv_avail();
plat_signal_power_on();
tpm_init();
let mut simulator = Simulator { _priv: () };
// Send TPM2_Startup(TPM_SU_CLEAR), ignore the result. This is normally
// done by firmware.
let startup_command = &[
0x80, 0x01, // TPM_ST_NO_SESSIONS
0x00, 0x00, 0x00, 0x0c, // commandSize = 12
0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup
0x00, 0x00, // TPM_SU_CLEAR
];
let _ = simulator.execute_command(startup_command);
simulator
}
/// Sends a TPM command to the TPM simulator, waits for the work to be
/// performed, and receives back the TPM response.
///
/// Executing a command requires exclusive access to the TPM simulator
/// because it mutates libtpm2 static state.
///
/// The returned response buffer remains valid until the next TPM command is
/// executed.
#[must_use]
pub fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
let request_size = command.len() as c_uint;
let request = command.as_ptr() as *mut u8;
let mut response_size: c_uint = 0;
let mut response: *mut u8 = ptr::null_mut();
// We need to provide the following guarantees in order for this block
// of code to be safe:
//
// - The TPM must have been initialized.
//
// - There must not be a concurrently executing call to
// ExecuteCommand.
//
// - The `request` pointer must be a valid pointer to `request_size`
// bytes of data that remain valid and constant for the full
// duration of the call to ExecuteCommand. The implementation may
// read up to `request_size` bytes of data from this address.
//
// - The `response_size` pointer must be a valid pointer to a mutable
// unsigned int. The implementation will write the response buffer
// size to this address.
//
// - The `response` pointer must be a valid pointer to a mutable
// unsigned char pointer. The implementation will write a pointer to
// the start of the response buffer to this address.
//
// - No more than `response_size` bytes may be read from the response
// buffer after the call returns.
//
// - Data may be read from the response buffer only until the next
// call to ExecuteCommand.
//
// The first guarantee is enforced by the public API of the Simulator
// struct, and in particular the singleton_in_current_directory
// constructor, which only makes a value of type Simulator available
// outside of this module after TPM initialization has been performed.
// Thus any Simulator on which the caller may be calling execute_command
// from outside of this module is witness that initialization has taken
// place.
//
// The second guarantee is made jointly by the &mut self reference in
// execute_command and the singleton_in_current_directory constructor
// which uses the SIMULATOR_EXISTS atomic flag to ensure that at most
// one value of type Simulator is ever made available to code outside of
// this module. Since at most one Simulator exists, and the caller is
// holding an exclusive reference to a Simulator, we know that no other
// code can be calling execute_command at the same time because they too
// would need their own exclusive reference to the same Simulator. We
// assume here that all use of libtpm2 within crosvm happens through the
// safe bindings provided by this tpm2 crate, so that the codebase
// contains no other unsafe calls to ExecuteCommand.
//
// The remaining guarantees are upheld by the signature and
// implementation of execute_command. In particular, note the lifetime
// 'a which ties the lifetime of the response slice we return to the
// caller to the lifetime of their exclusively held reference to
// Simulator. This signature looks the same to Rust as if the response
// buffer were a field inside the Simulator struct, rather than a
// statically allocated buffer inside libtpm2. As soon as the caller
// "mutates" the Simulator by performing another call to
// execute_command, the response buffer returned by the previous call is
// assumed to be invalidated and is made inaccessible by the borrow
// checker.
//
// Altogether we have guaranteed that execute_command is a safe
// abstraction around unsafe code and is entirely safe to call from
// outside of this module.
//
// Note additionally that the call to ExecuteCommand is over FFI so we
// need to know that the signature declared by tpm2-sys is
// ABI-compatible with the symbol provided by libtpm2.
unsafe {
tpm2_sys::ExecuteCommand(request_size, request, &mut response_size, &mut response);
slice::from_raw_parts(response, response_size as usize)
}
}
}
fn tpm_manufacture(first_time: bool) {
// From libtpm2 documentation:
//
// This function initializes the TPM values in preparation for the TPM's
// first use. This function will fail if previously called. The TPM can
// be re-manufactured by calling TPM_Teardown() first and then calling
// this function again.
//
// Arguments
//
// firstTime: indicates if this is the first call from main()
//
// Return value
//
// 0 = success
// 1 = manufacturing process previously performed
//
// Unsafe only because this is over FFI and we need to know that the
// signature declared by tpm2-sys is ABI-compatible with the symbol provided
// by libtpm2. There are no other invariants to uphold.
let ret: c_int = unsafe { tpm2_sys::TPM_Manufacture(first_time as c_int) };
// We expect that the TPM must not already have been manufactured. The
// SIMULATOR_EXISTS atomic flag guards calls to this function such that only
// one call can ever be performed by a process.
assert!(ret == 0);
}
fn plat_set_nv_avail() {
// From libtpm2 documentation:
//
// Set the current NV state to available. This function is for testing
// purpose only. It is not part of the platform NV logic.
//
// The "for testing purpose only" is unsettling but trunks performs the same
// call during initialization so we trust that it is okay.
//
// Unsafe only because this is over FFI and we need to know that the
// signature declared by tpm2-sys is ABI-compatible with the symbol provided
// by libtpm2. There are no other invariants to uphold.
unsafe {
tpm2_sys::_plat__SetNvAvail();
}
}
fn plat_signal_power_on() {
// From libtpm2 documentation:
//
// Signal platform power on.
//
// The libtpm2 implementation always returns 0 but does not document what
// the return value means, so we aren't checking it.
//
// Unsafe only because this is over FFI and we need to know that the
// signature declared by tpm2-sys is ABI-compatible with the symbol provided
// by libtpm2. There are no other invariants to uphold.
unsafe {
let _: c_int = tpm2_sys::_plat__Signal_PowerOn();
}
}
fn tpm_init() {
// This function is not documented in libtpm2. Trunks performs the same call
// during initialization so we trust that it is okay.
//
// Unsafe only because this is over FFI and we need to know that the
// signature declared by tpm2-sys is ABI-compatible with the symbol provided
// by libtpm2. There are no other invariants to uphold.
unsafe {
tpm2_sys::_TPM_Init();
}
}