Repository: microsoft/onefuzz
File: src/agent/coverage/src/binary.rs (Rust)

Code before:
    let debuginfo = module.debuginfo()?;

    let mut symcache = vec![];
    let mut converter = SymCacheConverter::new();
    let exe = Object::parse(module.executable_data())?;
    converter.process_object(&exe)?;
    let di = Object::parse(module.debuginfo_data())?;
    converter.process_object(&di)?;
    converter.serialize(&mut std::io::Cursor::new(&mut symcache))?;
    let symcache = SymCache::parse(&symcache)?;

    let mut offsets = BTreeSet::new();

    for function in debuginfo.functions() {
        if !allowlist.functions.is_allowed(&function.name) {
            continue;
        }

        let blocks = block::sweep_region(module, &debuginfo, function.offset, function.size)?;

        for block in &blocks {
            if let Some(location) = symcache.lookup(block.offset.0).next() {
                if let Some(file) = location.file() {
                    let path = file.full_path();

                    if allowlist.source_files.is_allowed(&path) {
                        offsets.insert(block.offset);
                    }
                }
            }
        }
    }

    let coverage = ModuleBinaryCoverage::from(offsets.into_iter());

    Ok(coverage)
}

impl AsRef<BTreeMap<Offset, Count>> for ModuleBinaryCoverage {
    fn as_ref(&self) -> &BTreeMap<Offset, Count> {
        &self.offsets
    }
}

#[cfg(test)]
mod tests;


Review comment (suggestion):
Add a comment, and apply the function allowlist here, too.

This may result in reporting artifacts around inlined calls (the `call` lines may appear unreachable). However, we don't have easy access to the full inline stack, so this is best for now (to avoid the worse artifact of allowlists "not working").

Code after:
    let debuginfo = module.debuginfo()?;

    let mut symcache = vec![];
    let mut converter = SymCacheConverter::new();
    let exe = Object::parse(module.executable_data())?;
    converter.process_object(&exe)?;
    let di = Object::parse(module.debuginfo_data())?;
    converter.process_object(&di)?;
    converter.serialize(&mut std::io::Cursor::new(&mut symcache))?;
    let symcache = SymCache::parse(&symcache)?;

    let mut offsets = BTreeSet::new();

    for function in debuginfo.functions() {
        if !allowlist.functions.is_allowed(&function.name) {
            continue;
        }

        let blocks = block::sweep_region(module, &debuginfo, function.offset, function.size)?;

        for block in &blocks {
            if let Some(location) = symcache.lookup(block.offset.0).next() {
                if let Some(file) = location.file() {
                    let path = file.full_path();

                    // Apply allowlists per block, to account for inlining. The `location` values
                    // here describe the top of the inline-inclusive call stack.
                    if !allowlist.functions.is_allowed(&path) {
                        continue;
                    }

                    if !allowlist.source_files.is_allowed(&path) {
                        continue;
                    }

                    offsets.insert(block.offset);
                }
            }
            println!();
        }
    }

    let coverage = ModuleBinaryCoverage::from(offsets.into_iter());

    Ok(coverage)
}

impl AsRef<BTreeMap<Offset, Count>> for ModuleBinaryCoverage {
    fn as_ref(&self) -> &BTreeMap<Offset, Count> {
        &self.offsets
    }