1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
//! Based on <https://github.com/alecmocatta/build_id>
//! (C) Alec Mocatta <alec@mocatta.net> under license MIT or Apache 2
use std::{
any::TypeId,
env,
fs::File,
hash::{Hash, Hasher},
io,
sync::OnceLock,
};
use uuid::Uuid;
use crate::hasher_std;
static BUILD_ID: OnceLock<Uuid> = OnceLock::new();
/// Returns a [Uuid] uniquely representing the build of the current binary.
///
/// This is intended to be used to check that different processes are indeed
/// invocations of identically laid out binaries.
///
/// As such:
/// * It is guaranteed to be identical within multiple invocations of the same
/// binary.
/// * It is guaranteed to be different across binaries with different code or
/// data segments or layout.
/// * Equality is unspecified if the binaries have identical code and data
/// segments and layout but differ immaterially (e.g. if a timestamp is included
/// in the binary at compile time).
///
/// # Examples
///
/// ```
/// # let remote_build_id = libafl_bolts::build_id::get();
/// let local_build_id = libafl_bolts::build_id::get();
/// if local_build_id == remote_build_id {
/// println!("We're running the same binary as remote!");
/// } else {
/// println!("We're running a different binary to remote");
/// }
/// ```
///
/// # Note
///
/// This looks first for linker-inserted build ID / binary UUIDs (i.e.
/// `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to
/// hashing the whole binary.
#[inline]
#[must_use]
pub fn get() -> Uuid {
*BUILD_ID.get_or_init(calculate)
}
fn from_exe<H: Hasher>(mut hasher: H) -> Result<H, ()> {
#[cfg(not(target_arch = "wasm32"))]
{
if cfg!(miri) {
return Err(());
}
let file = File::open(env::current_exe().map_err(drop)?).map_err(drop)?;
let _: u64 = io::copy(&mut &file, &mut HashWriter(&mut hasher)).map_err(drop)?;
Ok(hasher)
}
#[cfg(target_arch = "wasm32")]
{
let _ = &mut hasher;
Err(())
}
}
fn from_type_id<H: Hasher>(mut hasher: H) -> H {
fn type_id_of<T: 'static>(_: &T) -> TypeId {
TypeId::of::<T>()
}
TypeId::of::<()>().hash(&mut hasher);
TypeId::of::<u8>().hash(&mut hasher);
let a = |x: ()| x;
type_id_of(&a).hash(&mut hasher);
let b = |x: u8| x;
type_id_of(&b).hash(&mut hasher);
hasher
}
fn calculate() -> Uuid {
let hasher = hasher_std();
let hasher = from_exe(hasher.clone()).unwrap_or(hasher);
let mut hasher = from_type_id(hasher);
let mut bytes = [0; 16];
bytes[..8].copy_from_slice(&hasher.finish().to_ne_bytes());
hasher.write_u8(0);
bytes[8..].copy_from_slice(&hasher.finish().to_ne_bytes());
*uuid::Builder::from_bytes(bytes)
.set_variant(uuid::Variant::RFC4122)
.set_version(uuid::Version::Random)
.as_uuid()
}
struct HashWriter<T: Hasher>(T);
impl<T: Hasher> io::Write for HashWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf);
Ok(buf.len())
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.write(buf).map(|_| ())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}