Skip to content

Commit

Permalink
Report the kind of data that is leaked
Browse files Browse the repository at this point in the history
Improve leak reporting as whole by reporting if the leak comes from a string literal, a struct name, etc.
  • Loading branch information
ergrelet committed Sep 24, 2022
1 parent 9d37dbf commit 5c9caa3
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 49 deletions.
17 changes: 10 additions & 7 deletions src/information_leak/confirmed_leak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ use std::{ops::Deref, sync::Arc};

use serde::Serialize;

use super::LeakLocation;
use super::{LeakLocation, LeakedDataType};

/// Struct containing information on a piece of data that has leaked into a
/// binary file.
#[derive(Serialize)]
pub struct ConfirmedLeak {
pub leaked_information: Arc<String>,
/// Type of data leaked
pub data_type: LeakedDataType,
/// Leaked data, as represented in the source code
pub data: Arc<String>,
/// Information on where the leaked data is declared in the source code as
/// well as found in in the target binary
pub location: LeakLocation,
}

Expand Down Expand Up @@ -82,22 +87,20 @@ impl Deref for ConfirmedLeakWithUniqueValue {

impl PartialEq for ConfirmedLeakWithUniqueValue {
fn eq(&self, other: &Self) -> bool {
self.0.leaked_information == other.0.leaked_information
self.0.data == other.0.data
}
}

impl Eq for ConfirmedLeakWithUniqueValue {}

impl PartialOrd for ConfirmedLeakWithUniqueValue {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0
.leaked_information
.partial_cmp(&other.0.leaked_information)
self.0.data.partial_cmp(&other.0.data)
}
}

impl Ord for ConfirmedLeakWithUniqueValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.leaked_information.cmp(&other.0.leaked_information)
self.0.data.cmp(&other.0.data)
}
}
13 changes: 13 additions & 0 deletions src/information_leak/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,16 @@ mod potential_leak;
pub use confirmed_leak::*;
pub use leak_location::*;
pub use potential_leak::*;

use serde::Serialize;

/// Describes the kind of data that's leaked
#[derive(Debug, Serialize, Clone, Copy)]
pub enum LeakedDataType {
/// Data comes from a string literal
StringLiteral,
/// Data represents the name of a C/C++ struct
StructName,
/// Data represents the name of a C++ class
ClassName,
}
29 changes: 19 additions & 10 deletions src/information_leak/potential_leak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ use anyhow::{anyhow, Result};
use clang::{Entity, EntityKind};
use widestring::{encode_utf16, encode_utf32};

use super::SourceLocation;
use super::{LeakedDataType, SourceLocation};

/// Struct containing information on a piece of data from the source code, which
/// may leak into a binary file.
#[derive(Debug)]
pub struct PotentialLeak {
/// Leaked information, as represented in the source code
pub leaked_information: Arc<String>,
/// Type of data leaked
pub data_type: LeakedDataType,
/// Leaked data, as represented in the source code
pub data: Arc<String>,
/// Byte pattern to match (i.e., leaked information, as represented in the
/// binary file)
pub bytes: Vec<u8>,
/// Data on where the leaked information is declared in the
/// source code
/// Information on where the leaked data is declared in the source code
pub declaration_metadata: Arc<SourceLocation>,
}

Expand All @@ -41,20 +42,28 @@ impl TryFrom<Entity<'_>> for PotentialLeak {
let (_, string_content) = parse_string_literal(&leaked_information)?;

Ok(Self {
data_type: LeakedDataType::StringLiteral,
data: Arc::new(string_content.to_owned()),
bytes: string_literal_to_bytes(&leaked_information, None)?,
leaked_information: Arc::new(string_content.to_owned()),
declaration_metadata: Arc::new(SourceLocation {
file: file_location.canonicalize()?,
line: location.line as u64,
}),
})
}
EntityKind::StructDecl | EntityKind::ClassDecl => {
entity_kind @ (EntityKind::StructDecl | EntityKind::ClassDecl) => {
// Convert `EntityKind` to `LeakedDataType`
let data_type = match entity_kind {
EntityKind::StructDecl => LeakedDataType::StructName,
EntityKind::ClassDecl => LeakedDataType::ClassName,
_ => unreachable!("This entity kind should not be matched"),
};
let leaked_information = entity.get_display_name().unwrap_or_default();

Ok(Self {
data_type,
bytes: leaked_information.as_bytes().to_vec(),
leaked_information: Arc::new(leaked_information),
data: Arc::new(leaked_information),
declaration_metadata: Arc::new(SourceLocation {
file: file_location.canonicalize()?,
line: location.line as u64,
Expand All @@ -68,15 +77,15 @@ impl TryFrom<Entity<'_>> for PotentialLeak {

impl PartialEq for PotentialLeak {
fn eq(&self, other: &Self) -> bool {
self.leaked_information == other.leaked_information
self.data == other.data
}
}

impl Eq for PotentialLeak {}

impl Hash for PotentialLeak {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.leaked_information.hash(state);
self.data.hash(state);
}
}

Expand Down
21 changes: 11 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ fn filter_suppressed_artifacts_by_value(
if let Some(suppressions) = suppressions {
potential_leaks
.into_par_iter()
.filter(|leak| !suppressions.artifacts.contains(&leak.leaked_information))
.filter(|leak| !suppressions.artifacts.contains(&leak.data))
.collect()
} else {
potential_leaks
Expand Down Expand Up @@ -373,7 +373,8 @@ where
if byte_slice == leak.bytes {
// Bytes match, the leak is confirmed
confirmed_leaks.insert(SortedConfirmedLeak::from(ConfirmedLeak {
leaked_information: leak.leaked_information.clone(),
data_type: leak.data_type,
data: leak.data.clone(),
location: information_leak::LeakLocation {
source: leak.declaration_metadata.clone(),
binary: BinaryLocation {
Expand Down Expand Up @@ -459,8 +460,8 @@ mod tests {

// Check extracted string literals
assert!(potential_leaks.iter().enumerate().all(|(i, leak)| {
println!("{:?}", leak.leaked_information);
*leak.leaked_information == expected_string_literals[i]
println!("{:?}", leak.data);
*leak.data == expected_string_literals[i]
}));
assert_eq!(expected_string_literals.len(), potential_leaks.len());
}
Expand Down Expand Up @@ -515,8 +516,8 @@ mod tests {

// Check extracted string literals
assert!(potential_leaks.iter().enumerate().all(|(i, leak)| {
println!("{:?}", leak.leaked_information);
*leak.leaked_information == expected_string_literals[i]
println!("{:?}", leak.data);
*leak.data == expected_string_literals[i]
}));
assert_eq!(expected_string_literals.len(), potential_leaks.len());
}
Expand Down Expand Up @@ -571,8 +572,8 @@ mod tests {

// Check extracted string literals
assert!(confirmed_leaks.iter().enumerate().all(|(i, leak)| {
println!("{:?}", leak.leaked_information);
*leak.leaked_information == expected_string_literals[i]
println!("{:?}", leak.data);
*leak.data == expected_string_literals[i]
}));
assert_eq!(confirmed_leaks.len(), expected_string_literals.len());
}
Expand Down Expand Up @@ -642,8 +643,8 @@ mod tests {

// Check extracted string literals
assert!(confirmed_leaks.iter().enumerate().all(|(i, leak)| {
println!("{:?}", leak.leaked_information);
*leak.leaked_information == expected_string_literals[i]
println!("{:?}", leak.data);
*leak.data == expected_string_literals[i]
}));
assert_eq!(confirmed_leaks.len(), expected_string_literals.len());
}
Expand Down
77 changes: 55 additions & 22 deletions src/reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeSet;
use anyhow::Result;
use serde::Serialize;

use crate::information_leak::ConfirmedLeak;
use crate::information_leak::{ConfirmedLeak, LeakedDataType};

const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const REPORT_FORMAT_VERSION: u32 = 1;
Expand All @@ -21,7 +21,7 @@ struct ReportVersion {
}

pub fn dump_confirmed_leaks<W, SortedConfirmedLeak>(
mut writer: W,
writer: W,
confirmed_leaks: BTreeSet<SortedConfirmedLeak>,
json: bool,
) -> Result<()>
Expand All @@ -30,28 +30,61 @@ where
SortedConfirmedLeak: Into<ConfirmedLeak> + Ord + Eq + Serialize,
{
if json {
let report = JsonReport {
version: ReportVersion {
executable: PKG_VERSION.into(),
format: REPORT_FORMAT_VERSION,
},
leaks: confirmed_leaks,
};
serde_json::to_writer(writer, &report)?;
dump_confirmed_leaks_as_json(writer, confirmed_leaks)
} else {
for leak in confirmed_leaks {
let leak: ConfirmedLeak = leak.into();
writeln!(
&mut writer,
"{} leaked at offset 0x{:x} in \"{}\" [declared at {}:{}]",
leak.leaked_information,
leak.location.binary.offset,
leak.location.binary.file.display(),
leak.location.source.file.display(),
leak.location.source.line,
)?;
}
dump_confirmed_leaks_as_text(writer, confirmed_leaks)
}
}

fn dump_confirmed_leaks_as_json<W, SortedConfirmedLeak>(
writer: W,
confirmed_leaks: BTreeSet<SortedConfirmedLeak>,
) -> Result<()>
where
W: std::io::Write,
SortedConfirmedLeak: Into<ConfirmedLeak> + Ord + Eq + Serialize,
{
let report = JsonReport {
version: ReportVersion {
executable: PKG_VERSION.into(),
format: REPORT_FORMAT_VERSION,
},
leaks: confirmed_leaks,
};

Ok(serde_json::to_writer(writer, &report)?)
}

fn dump_confirmed_leaks_as_text<W, SortedConfirmedLeak>(
mut writer: W,
confirmed_leaks: BTreeSet<SortedConfirmedLeak>,
) -> Result<()>
where
W: std::io::Write,
SortedConfirmedLeak: Into<ConfirmedLeak> + Ord + Eq + Serialize,
{
for leak in confirmed_leaks {
let leak: ConfirmedLeak = leak.into();
writeln!(
&mut writer,
"\"{}\" ({}) leaked at offset 0x{:x} in \"{}\" [declared at {}:{}]",
leak.data,
display_leaked_data_type(leak.data_type),
leak.location.binary.offset,
leak.location.binary.file.display(),
leak.location.source.file.display(),
leak.location.source.line,
)?;
}

Ok(())
}

/// Returns a text representation of `LeakedDataType`
fn display_leaked_data_type(data_type: LeakedDataType) -> String {
match data_type {
LeakedDataType::StringLiteral => "string literal".to_string(),
LeakedDataType::StructName => "struct name".to_string(),
LeakedDataType::ClassName => "class name".to_string(),
}
}

0 comments on commit 5c9caa3

Please sign in to comment.