Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions differ.cc
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,19 @@ double GetSimilarityScore(const CallGraph& call_graph1,
const CallGraph& call_graph2,
const Histogram& histogram, const Counts& counts) {
double similarity = 0;
similarity +=
0.35 * counts[Counts::kFlowGraphEdgeMatchesNonLibrary] /
(std::max(1.0,
0.5 * (counts[Counts::kFlowGraphEdgesPrimaryNonLibrary] +
counts[Counts::kFlowGraphEdgesSecondaryNonLibrary])));
const uint64_t edges_primary =
counts[Counts::kFlowGraphEdgesPrimaryNonLibrary];
const uint64_t edges_secondary =
counts[Counts::kFlowGraphEdgesSecondaryNonLibrary];
if (edges_primary == 0 && edges_secondary == 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this change correctly addresses the issue for the CallGraph similarity score, the same problem also exists in the FlowGraph overload of GetSimilarityScore (defined at line 490). If two functions have some differences in basic blocks or instructions (so they do not trigger the return 1.0 shortcut on line 518) but both have zero flow graph edges, the edge term (which contributes 55% of the score) will be evaluated as 0.55 * 0 / 1.0 = 0. This heavily penalizes the similarity score of single-block functions. Consider applying a similar check in the FlowGraph overload of GetSimilarityScore to avoid capping the score when both flow graphs have zero edges.

// Neither side has any edges, so there is nothing here that could fail to
// match. Without this, such binaries are capped at 0.65 no matter how well
// everything else lines up.
similarity += 0.35;
} else {
similarity += 0.35 * counts[Counts::kFlowGraphEdgeMatchesNonLibrary] /
(std::max(1.0, 0.5 * (edges_primary + edges_secondary)));
}
similarity +=
0.25 * counts[Counts::kBasicBlockMatchesNonLibrary] /
(std::max(1.0, 0.5 * (counts[Counts::kBasicBlocksPrimaryNonLibrary] +
Expand Down
34 changes: 34 additions & 0 deletions differ_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,39 @@ INSTANTIATE_TEST_SUITE_P(
.set_truth("bindiff/fixtures/mydoom/"
"Mydoom-vc_orig_vs_Mydoom-vc_optz.truth")));

// A binary can have no flow graph edges at all, e.g. if every function is a
// single basic block without calls. Both sides then have nothing to disagree
// about, so the edge term should count as a match instead of capping the score.
TEST(SimilarityScoreTest, NoFlowGraphEdgesOnEitherSide) {
Counts counts;
counts[Counts::kFunctionsPrimaryNonLibrary] = 1;
counts[Counts::kFunctionsSecondaryNonLibrary] = 1;
counts[Counts::kFunctionMatchesNonLibrary] = 1;
counts[Counts::kBasicBlocksPrimaryNonLibrary] = 1;
counts[Counts::kBasicBlocksSecondaryNonLibrary] = 1;
counts[Counts::kBasicBlockMatchesNonLibrary] = 1;
counts[Counts::kInstructionsPrimaryNonLibrary] = 4;
counts[Counts::kInstructionsSecondaryNonLibrary] = 4;
counts[Counts::kInstructionMatchesNonLibrary] = 4;

const CallGraph call_graph; // Empty, MD index 0 on both sides
const Histogram histogram{{MatchingStep::kFunctionCallReferenceName, 1}};
Confidences confidences;
const double confidence = GetConfidence(histogram, &confidences);
ASSERT_GT(confidence, 0.0);

// Everything that can match does match, so the score before confidence is
// applied should be 1.0 and not 0.65.
EXPECT_NEAR(GetSimilarityScore(call_graph, call_graph, histogram, counts),
confidence, 1e-9);

// Edges on one side only is a genuine difference in control flow, so the
// edge term has to stay at zero there.
Counts one_sided = counts;
one_sided[Counts::kFlowGraphEdgesPrimaryNonLibrary] = 1;
EXPECT_NEAR(GetSimilarityScore(call_graph, call_graph, histogram, one_sided),
0.65 * confidence, 1e-9);
}

} // namespace
} // namespace security::bindiff
Loading