libs: Move cstr!() into its own crate

Make the macro host_supported, for unittests.

This overshadows the homonymous crate from crates.io [1] but importing
and depending on an external crate for <10 lines of code seems overkill.

[1]: https://crates.io/crates/cstr

Bug: 308694211
Test: atest libcstr.tests
Test: m libcstr
Test: m pvmfw_bin rialto_bin vmbase_example_bin
Change-Id: If1bf37034fc004a380b3ba528b9d76393a865d3e
diff --git a/libs/cstr/Android.bp b/libs/cstr/Android.bp
new file mode 100644
index 0000000..4ea87df
--- /dev/null
+++ b/libs/cstr/Android.bp
@@ -0,0 +1,36 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library_rlib {
+    name: "libcstr",
+    crate_name: "cstr",
+    defaults: ["avf_build_flags_rust"],
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    host_supported: true,
+    prefer_rlib: true,
+    target: {
+        android: {
+            no_stdlibs: true,
+            stdlibs: [
+                "libcompiler_builtins.rust_sysroot",
+                "libcore.rust_sysroot",
+            ],
+        },
+    },
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+}
+
+rust_test {
+    name: "libcstr.tests",
+    crate_name: "libcstr_test",
+    defaults: ["avf_build_flags_rust"],
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    prefer_rlib: true,
+    rustlibs: ["libcstr"],
+}
diff --git a/libs/cstr/src/lib.rs b/libs/cstr/src/lib.rs
new file mode 100644
index 0000000..ddf20fc
--- /dev/null
+++ b/libs/cstr/src/lib.rs
@@ -0,0 +1,50 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provide a safe const-compatible no_std macro for readable &'static CStr.
+
+#![no_std]
+
+/// Create &CStr out of &str literal
+#[macro_export]
+macro_rules! cstr {
+    ($str:literal) => {{
+        const S: &str = concat!($str, "\0");
+        const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
+            Ok(v) => v,
+            Err(_) => panic!("string contains interior NUL"),
+        };
+        C
+    }};
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::ffi::CString;
+
+    #[test]
+    fn valid_input_string() {
+        let expected = CString::new("aaa").unwrap();
+        assert_eq!(cstr!("aaa"), expected.as_c_str());
+    }
+
+    #[test]
+    fn valid_empty_string() {
+        let expected = CString::new("").unwrap();
+        assert_eq!(cstr!(""), expected.as_c_str());
+    }
+
+    // As cstr!() panics at compile time, tests covering invalid inputs fail to compile!
+}
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index b889ee5..5920d5d 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -37,6 +37,7 @@
         "libcore.rust_sysroot",
     ],
     rustlibs: [
+        "libcstr",
         "liblibfdt_bindgen",
         "libzerocopy_nostd",
     ],
@@ -61,6 +62,7 @@
     ],
     prefer_rlib: true,
     rustlibs: [
+        "libcstr",
         "liblibfdt",
     ],
 }
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index b369390..6cfe5f7 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -31,15 +31,9 @@
 use core::ops::Range;
 use core::ptr;
 use core::result;
+use cstr::cstr;
 use zerocopy::AsBytes as _;
 
-// TODO(b/308694211): Use cstr!() from vmbase
-macro_rules! cstr {
-    ($str:literal) => {{
-        core::ffi::CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
-    }};
-}
-
 /// Error type corresponding to libfdt error codes.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum FdtError {
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index d76b1a4..63cbdee 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -17,23 +17,12 @@
 //! Integration tests of the library libfdt.
 
 use core::ffi::CStr;
+use cstr::cstr;
 use libfdt::{Fdt, FdtError, FdtNodeMut, Phandle};
 use std::ffi::CString;
 use std::fs;
 use std::ops::Range;
 
-// TODO(b/308694211): Use cstr!() from vmbase
-macro_rules! cstr {
-    ($str:literal) => {{
-        const S: &str = concat!($str, "\0");
-        const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
-            Ok(v) => v,
-            Err(_) => panic!("string contains interior NUL"),
-        };
-        C
-    }};
-}
-
 const TEST_TREE_WITH_ONE_MEMORY_RANGE_PATH: &str = "data/test_tree_one_memory_range.dtb";
 const TEST_TREE_WITH_MULTIPLE_MEMORY_RANGES_PATH: &str =
     "data/test_tree_multiple_memory_ranges.dtb";
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index b7b5900..b6f3940 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -16,6 +16,7 @@
         "libbssl_ffi_nostd",
         "libciborium_nostd",
         "libciborium_io_nostd",
+        "libcstr",
         "libdiced_open_dice_nostd",
         "libfdtpci",
         "libhyp",
@@ -55,6 +56,7 @@
         unit_test: true,
     },
     rustlibs: [
+        "libcstr",
         "libzeroize",
     ],
 }
@@ -90,6 +92,7 @@
     },
     prefer_rlib: true,
     rustlibs: [
+        "libcstr",
         "liblibfdt",
         "liblog_rust",
         "libpvmfw_fdt_template",
diff --git a/pvmfw/src/bootargs.rs b/pvmfw/src/bootargs.rs
index a089a67..aacd8e0 100644
--- a/pvmfw/src/bootargs.rs
+++ b/pvmfw/src/bootargs.rs
@@ -108,19 +108,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-
-    // TODO(b/308694211): Use cstr!() from vmbase
-    macro_rules! cstr {
-        ($str:literal) => {{
-            const S: &str = concat!($str, "\0");
-            const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes())
-            {
-                Ok(v) => v,
-                Err(_) => panic!("string contains interior NUL"),
-            };
-            C
-        }};
-    }
+    use cstr::cstr;
 
     fn check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>) {
         let actual = BootArgsIterator::new(raw);
diff --git a/pvmfw/src/crypto.rs b/pvmfw/src/crypto.rs
index 2b3d921..8f31553 100644
--- a/pvmfw/src/crypto.rs
+++ b/pvmfw/src/crypto.rs
@@ -33,7 +33,7 @@
 use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
 use bssl_ffi::EVP_AEAD;
 use bssl_ffi::EVP_AEAD_CTX;
-use vmbase::cstr;
+use cstr::cstr;
 
 #[derive(Debug)]
 pub struct Error {
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index cc31f34..112c24c 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -17,12 +17,12 @@
 use core::ffi::c_void;
 use core::mem::size_of;
 use core::slice;
+use cstr::cstr;
 use diced_open_dice::{
     bcc_format_config_descriptor, bcc_handover_main_flow, hash, Config, DiceConfigValues, DiceMode,
     Hash, InputValues, HIDDEN_SIZE,
 };
 use pvmfw_avb::{DebugLevel, Digest, VerifiedBootData};
-use vmbase::cstr;
 use vmbase::memory::flushed_zeroize;
 
 fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 7655614..4fe2c34 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -28,6 +28,7 @@
 use core::fmt;
 use core::mem::size_of;
 use core::ops::Range;
+use cstr::cstr;
 use fdtpci::PciMemoryFlags;
 use fdtpci::PciRangeType;
 use libfdt::AddressRange;
@@ -41,7 +42,6 @@
 use log::info;
 use log::warn;
 use tinyvec::ArrayVec;
-use vmbase::cstr;
 use vmbase::fdt::SwiotlbInfo;
 use vmbase::layout::{crosvm::MEM_START, MAX_VIRT_ADDR};
 use vmbase::memory::SIZE_4KB;
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 326f6fc..12d6e7b 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -13,6 +13,7 @@
         "libbssl_ffi_nostd",
         "libciborium_io_nostd",
         "libciborium_nostd",
+        "libcstr",
         "libdiced_open_dice_nostd",
         "libdiced_sample_inputs_nostd",
         "libhyp",
diff --git a/rialto/src/fdt.rs b/rialto/src/fdt.rs
index 8bb40c3..09cdd36 100644
--- a/rialto/src/fdt.rs
+++ b/rialto/src/fdt.rs
@@ -15,8 +15,8 @@
 //! High-level FDT functions.
 
 use core::ops::Range;
+use cstr::cstr;
 use libfdt::{Fdt, FdtError};
-use vmbase::cstr;
 
 /// Reads the DICE data range from the given `fdt`.
 pub fn read_dice_range_from(fdt: &Fdt) -> libfdt::Result<Range<usize>> {
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index b2b1549..e682773 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -76,6 +76,7 @@
     rustlibs: [
         "libaarch64_paging",
         "libbuddy_system_allocator",
+        "libcstr",
         "libfdtpci",
         "libhyp",
         "liblibfdt",
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index ae1a593..fe9de44 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -9,6 +9,7 @@
     srcs: ["src/main.rs"],
     rustlibs: [
         "libaarch64_paging",
+        "libcstr",
         "libdiced_open_dice_nostd",
         "libfdtpci",
         "liblibfdt",
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index ebd981c..6f513ee 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -28,11 +28,12 @@
 use aarch64_paging::paging::MemoryRegion;
 use aarch64_paging::MapError;
 use alloc::{vec, vec::Vec};
+use cstr::cstr;
 use fdtpci::PciInfo;
 use libfdt::Fdt;
 use log::{debug, error, info, trace, warn, LevelFilter};
 use vmbase::{
-    bionic, configure_heap, cstr,
+    bionic, configure_heap,
     layout::{dtb_range, rodata_range, scratch_range, text_range},
     linker, logger, main,
     memory::{PageTable, SIZE_64KB},
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
index f8db1fe..a049616 100644
--- a/vmbase/src/bionic.rs
+++ b/vmbase/src/bionic.rs
@@ -22,11 +22,12 @@
 use core::str;
 
 use crate::console;
-use crate::cstr;
 use crate::eprintln;
 use crate::rand::fill_with_entropy;
 use crate::read_sysreg;
 
+use cstr::cstr;
+
 const EOF: c_int = -1;
 const EIO: c_int = 5;
 
diff --git a/vmbase/src/fdt.rs b/vmbase/src/fdt.rs
index 537ca03..4101f7e 100644
--- a/vmbase/src/fdt.rs
+++ b/vmbase/src/fdt.rs
@@ -14,8 +14,8 @@
 
 //! High-level FDT functions.
 
-use crate::cstr;
 use core::ops::Range;
+use cstr::cstr;
 use libfdt::{self, Fdt, FdtError};
 
 /// Represents information about a SWIOTLB buffer.
diff --git a/vmbase/src/util.rs b/vmbase/src/util.rs
index 25586bc..8c230a1 100644
--- a/vmbase/src/util.rs
+++ b/vmbase/src/util.rs
@@ -16,19 +16,6 @@
 
 use core::ops::Range;
 
-/// Create &CStr out of &str literal
-#[macro_export]
-macro_rules! cstr {
-    ($str:literal) => {{
-        const S: &str = concat!($str, "\0");
-        const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
-            Ok(v) => v,
-            Err(_) => panic!("string contains interior NUL"),
-        };
-        C
-    }};
-}
-
 /// Flatten [[T; N]] into &[T]
 /// TODO: use slice::flatten when it graduates from experimental
 pub fn flatten<T, const N: usize>(original: &[[T; N]]) -> &[T] {