From 432f0104d92ea12cb80e1f9f8b490e08dcc6e6d3 Mon Sep 17 00:00:00 2001 From: Brandon Falk Date: Wed, 14 Jan 2026 19:53:10 -0800 Subject: [PATCH] Fix DWARF void return type confidence Previously, functions with missing DW_AT_type (void return) were assigned confidence 0, allowing Binary Ninja's analysis to override them with incorrect inferred types (e.g., uint64_t). Per DWARF spec section 3.3.2, a missing DW_AT_type on a subprogram means void, not "unknown". However, with minimal debug info (-g1), types are omitted entirely, so we can't distinguish void from unknown. This fix: - Uses MAX_CONFIDENCE for explicit return types (was 128), matching parameter confidence for consistency - Uses MAX_CONFIDENCE for void when full debug info is present (detected by checking if any type definitions exist in the compilation unit) - Keeps confidence 0 for minimal debug info (-g1) to allow analysis to infer Tested with C (-g1, -g2) and C++ (-g1, -g2) code to verify correct behavior across debug levels and languages (C uses DW_AT_prototyped, C++ doesn't). Co-Authored-By: Claude Opus 4.5 --- .../dwarf/dwarf_import/src/dwarfdebuginfo.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index fbf7e7dfa..d2c236ff1 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -31,7 +31,7 @@ use binaryninja::{ use gimli::{DebuggingInformationEntry, Dwarf, Unit}; -use binaryninja::confidence::Conf; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::variable::{Variable, VariableSourceType}; use indexmap::{map::Values, IndexMap}; use std::{cmp::Ordering, collections::HashMap, hash::Hash}; @@ -666,8 +666,20 @@ impl DebugInfoBuilder { let return_type = function .return_type .and_then(|return_type_id| self.get_type(return_type_id)) - .map(|t| Conf::new(t.ty.clone(), 128)) - .unwrap_or_else(|| Conf::new(Type::void(), 0)); + .map(|t| Conf::new(t.ty.clone(), MAX_CONFIDENCE)) + .unwrap_or_else(|| { + // Per DWARF spec section 3.3.2: "If the subroutine or entry point is a function + // that returns a value, then its debugging information entry has a DW_AT_type + // attribute." A missing DW_AT_type means void, not unknown. + // + // To distinguish "void" from "unknown" (minimal -g1 debug info), we check if + // the compilation unit has any type definitions. At -g2+, types are always + // present (int, structs, etc.), so missing DW_AT_type definitively means void. + // At -g1, no types exist, so we use low confidence to let analysis infer. + let has_full_debug_info = !self.types.is_empty(); + let confidence = if has_full_debug_info { MAX_CONFIDENCE } else { 0 }; + Conf::new(Type::void(), confidence) + }); let parameters: Vec = function .parameters