Papers
Topics
Authors
Recent
Search
2000 character limit reached

SBridge: Source-to-Binary Function Similarity

Updated 6 July 2026
  • SBridge is a source-to-binary function-similarity method that decomposes functions into control blocks, capturing key structural features.
  • It employs both vector-based and rule-based block matching to achieve high accuracy, reporting 75.13% Recall@1 on 3,904 binaries.
  • The approach effectively manages challenges like inlining, stripping, and varying optimizations, enhancing code-reuse analysis for vulnerability detection.

SBridge is a source-to-binary function-similarity method that identifies functions in binaries that are similar to given source-code functions. It was introduced to support code-reuse analysis in security settings, particularly for detecting propagated vulnerabilities, and is designed for the cross-domain setting in which source code is available but binaries may be stripped, compiled under different architectures or optimization levels, and affected by function inlining. Its central representation is the control block: a function is segmented into meaningful units such as conditionals, loops, string elements, and function calls, and similarity is computed at the control-block level rather than only from whole-function structure or string literals. On 3,904 real-world C/C++ binaries from BinKit, SBridge reported 75.13% recall@1 and 80.98% recall@5 in identifying binary functions identical to input source functions, despite approximately 40% of binary functions being inlined (Yang et al., 26 Jun 2026).

1. Problem setting and design rationale

SBridge addresses the problem: given a set of source-code functions, identify the binary functions in a target binary that are most similar to them (Yang et al., 26 Jun 2026). The setting is motivated by the observation that source code is easier to collect and analyze directly without compilation, whereas binary-to-binary comparison, although feasible, is less practical as a primary reference workflow.

The source-to-binary problem is difficult for three reasons. First, compilation removes or distorts source-level context such as variable names, types, and code structure; in stripped binaries, symbol tables such as .symtab and .strtab are removed, so function-name-based matching is unavailable. Second, the same source function can compile into substantially different binaries depending on architecture, compiler, version, optimization, and symbol stripping. Third, modern compilers often inline callees into callers, so one source function may map to multiple binary functions, or multiple source functions may collapse into one binary function (Yang et al., 26 Jun 2026).

The motivating example in the paper is GNU csplit compiled for ARM32 with Clang -O2 and stripped symbols, where parse_patterns and extract_regexp are inlined into main, and decompiled output loses names and rewrites literals. Existing approaches are described as relying primarily on string literals or structural similarities between entire functions, which fails to capture detailed code behavior and yields many false alarms. SBridge was proposed to replace whole-function comparison with a finer cross-domain unit that survives compilation gaps more effectively (Yang et al., 26 Jun 2026).

2. Control blocks as the cross-domain representation

SBridge defines a control block as “a fundamental code unit within a function that encapsulates key functional features and represents distinct structural components, such as loops, if-else blocks, and embedded string elements” (Yang et al., 26 Jun 2026). The method extracts control blocks from both source code and decompiled binary code, then performs block matching across domains.

The extraction scheme uses two branching perspectives. Internal branching includes Condition, Else-Condition, and Loop. External branching includes LibcFunction, CalleeFunction, and RecurFunction. A separate String category captures string literals. Nested structures are extracted hierarchically: an outer block and an inner block are each extracted separately, each with its own key feature and block content (Yang et al., 26 Jun 2026).

Block type Key feature Block content
C1 Condition condition expression code in the conditional state
C2 Else-Condition none code in the else branch
C3 Loop condition expression code in loop body
C4 String string literal none
C5 LibcFunction (Func. name, #Params) parameter information
C6 CalleeFunction (Func. name, #Params) parameter information
C7 RecurFunction (Func. name, #Params) parameter information

This design makes control blocks the operational “bridge” between source and binary code. Rather than attempting to preserve the full source syntax, SBridge preserves distinct structural components that the paper treats as essential functional features. A plausible implication is that the representation is intended to remain informative even when decompilation changes statement ordering, variable naming, or superficial syntax.

3. Block-level similarity computation

For internal branching blocks—Condition, Else-Condition, and Loop—SBridge uses feature vectors rather than raw syntax (Yang et al., 26 Jun 2026). The vector contains static components at indices 0–363 and dynamic components at indices 364–n, and all entries are binary (0 or 1) so that existence is emphasized over frequency. The features include local variable used, parameter variable used, operator used, C library function used, unique user-defined function count match, and unique string / number / function references. The operator space is grouped into 14 operator groups, including equOpr, comOpr, addOpr, subOpr, mulOpr, divOpr, modOpr, notOpr, xorOpr, shiftOpr, logAndOrOpr, bitAndOrOpr, accOpr, infOpr, and assignOpr.

Before comparing two internal branching blocks, SBridge aligns feature spaces: if one vector has dynamically added entries absent in the other, those entries are appended to the other vector with value 0. Cosine similarity is then computed for the key feature, denoted \phi_k, and for the block content, denoted \phi_b. For two internal branching blocks B_{I_1} and B_{I_2}, the final similarity is

Φb(BI1,BI2)=(ϕk+ϕb)/2.\Phi_b(B_{I_1}, B_{I_2}) = (\phi_k + \phi_b)/2.

For Else-Condition blocks, which have no key feature, only block-content similarity is used (Yang et al., 26 Jun 2026).

For external branching blocks—LibcFunction, CalleeFunction, and RecurFunction—SBridge uses a rule-based comparison. It first checks the key feature: function name and number of parameters. If both match, then \Phi_b = 1. If names do not match, especially in stripped binaries, the method falls back to parameter comparison: if both parameter values and types match, \Phi_b = 1; if only types match, \Phi_b = 0.5; otherwise, \Phi_b = 0 (Yang et al., 26 Jun 2026). The paper notes that libc calls can be optimized into substitutes, so SBridge groups semantically substitutable libc functions together using documentation such as LLVM SimplifyLibCalls and GNU libc manuals; it includes 346 standard C library functions and groups examples such as printf with puts.

For String blocks, matching is exact: if string literals match, \Phi_b = 1; otherwise, \Phi_b = 0 (Yang et al., 26 Jun 2026). In the paper’s motivating example, this block-centric scheme produced similarity 0.875 for a case where conventional token cosine similarity was 0.3640 and Levenshtein-based similarity was 0.4863, which the authors use to argue that block matching is more robust to cross-domain rewriting.

4. Whole-function similarity, preprocessing, and inlining-aware matching

SBridge combines block matches into a whole-function score. For source code, it preprocesses source files so that macro expansions and header includes are reflected, then extracts function bodies using parsers such as Ctags and Joern. For binary code, it uses reverse-engineering tools such as Ghidra and relies on C-like pseudocode rather than raw assembly so that loops and conditionals can be extracted more reliably (Yang et al., 26 Jun 2026).

Normalization removes comments, converts ASCII and hexadecimal characters to decimal representations, replaces variable names with LVAR, and replaces parameter names with PARAM (Yang et al., 26 Jun 2026). These operations are used to reduce compilation-induced noise without making the representation entirely symbolic.

The initial function similarity is defined by the fraction of source blocks that find a sufficiently similar binary block:

Φf(fs,fb)={BsfsBbfb,  Φb(Bs,Bb)θb}{Bsfs}.\Phi_f(f_s, f_b) = \dfrac{|\{\,B_s \in f_s \mid \exists\, B_b \in f_b,\;\Phi_b(B_s, B_b) \geq \theta_b\}|}{|\{\,B_s \in f_s\}|}.

This formulation is asymmetric: it asks how much of the source function appears in the binary function. That asymmetry is important for inlining, because a binary function may contain code from several source functions (Yang et al., 26 Jun 2026).

SBridge then applies a length-based weighting

wlength=1.01.0+log(#blocks(fb)/#blocks(fs)+1.0),w_{\mathrm{length}} = \dfrac{1.0}{1.0 + \log \big(\#\mathrm{blocks}(f_b)/\#\mathrm{blocks}(f_s) + 1.0 \big)},

and the final similarity is

Φ(fs,fb)=wlengthΦf.\Phi(f_s, f_b) = w_{\mathrm{length}} \cdot \Phi_f.

If the binary function is much larger than the source and inlining is unlikely, the score is penalized; if inlining is likely, SBridge fixes w_length = 1 so that length differences do not hurt the score (Yang et al., 26 Jun 2026).

Inlining likelihood is determined from the source call graph. SBridge matches functions top-down via DFS. If the source function has child nodes and the binary function has no external calls (C5 blocks), the paper treats that as suggesting inlining. If the source has child nodes and the binary also has external calls, inlining is treated as less likely (Yang et al., 26 Jun 2026). This inlining-aware weighting is one of the method’s main departures from whole-function structural similarity.

The paper reports that control blocks cover 82.66% of source lines and 73.77% of binary lines on the evaluated dataset (Yang et al., 26 Jun 2026). This suggests that the representation is intended to capture most of the code that remains discriminative after compilation and decompilation.

5. Experimental protocol and source-to-binary accuracy

The evaluation uses the BCSA benchmark from BinKit and selects GNU Coreutils v9.0 and Inetutils v2.4, the two GNU software packages with the most binaries (Yang et al., 26 Jun 2026). The study starts from 122 target packages. Each binary is compiled under 32 configurations spanning 4 architectures (ARM32, ARM64, x86, x64), 2 compilers ([GCC](https://www.emergentmind.com/topics/granular-concept-circuit-gcc) 9.4.0, Clang 13.0), 2 optimization levels (-O0, -O2), and 2 symbol-management options (strip, no_strip). This yields 3,904 binaries and 624,192 functions. The source-side input contains 1,618 source functions.

Accuracy is evaluated with Recall@1, Recall@5, and MRR. For the accuracy study, the paper assumes that -O0 binaries are not inlined, which is used to construct ground truth consistently across tools (Yang et al., 26 Jun 2026).

Method Recall@1 Recall@5
MRT-OAST 0.1206 0.2789
BinaryAI 0.4331 0.5020
SBridge 0.7513 0.8098

The corresponding MRR values are 0.2052 for MRT-OAST, 0.4653 for BinaryAI, and 0.8040 for SBridge (Yang et al., 26 Jun 2026). The paper highlights that SBridge attains 6.23× higher Recall@1 than MRT-OAST and 1.73× higher Recall@1 than BinaryAI.

Across architectures, SBridge reports Recall@1 / Recall@5 of 0.7082 / 0.7787 on ARM32, 0.7751 / 0.8325 on ARM64, 0.7394 / 0.7921 on x86, and 0.7827 / 0.8358 on x64 (Yang et al., 26 Jun 2026). Across compilers, results are 0.7537 / 0.8097 for GCC and 0.7490 / 0.8099 for Clang. Optimization has a larger effect: -O0 gives 0.8496 / 0.8754, whereas -O2 gives 0.6531 / 0.7441. Symbol stripping also degrades performance but does not eliminate it: no_strip gives 0.7909 / 0.8470 and strip gives 0.7119 / 0.7725 (Yang et al., 26 Jun 2026).

Threshold sensitivity is studied by varying \theta_b from 0.1 to 0.9. According to the paper, values that are too high miss similar blocks and reduce recall, whereas values below 0.6 allow unrelated blocks to be treated as similar and reduce Unique@1. The chosen operating point is

θb=0.7,\theta_b = 0.7,

described as a balance between high recall and good discrimination (Yang et al., 26 Jun 2026).

The implementation is written in F#, is about 4.1K LOC, and uses Ctags, the Joern parser, and Ghidra (Yang et al., 26 Jun 2026). Matching performance is reported as an average of 1.9 seconds per source function against one binary, with a median of 0.692 seconds, and 96.8% of cases finishing in under 10 seconds.

6. Inlining behavior, vulnerability-detection use, and limitations

Inlining-specific experiments are central because the paper presents inlining as a primary cause of source-to-binary mismatch (Yang et al., 26 Jun 2026). For Coreutils at -O2, Clang no_strip has inline rate 54.12%, with Recall@1 0.5981 and Recall@5 0.7315; Clang strip gives 0.5624 and 0.6712; GCC no_strip gives 0.5930 and 0.7343; GCC strip gives 0.5284 and 0.6595. For Inetutils at -O2, Clang no_strip has inline rate 11.33%, with Recall@1 0.7297 and Recall@5 0.7613; Clang strip gives 0.6441 and 0.7162; GCC no_strip gives 0.5879 and 0.6593; GCC strip gives 0.6319 and 0.6593. In the adapted inlined-function setting, the paper reports that BinaryAI achieved less than 0.01 Recall@1 (Yang et al., 26 Jun 2026). This is the main empirical basis for the claim that control-block matching is comparatively robust to inlining.

SBridge is also applied to 1-day vulnerability detection on four OSS projects—tcpdump, OpenSSL, LibXML2, and FFmpeg—covering 26 CVEs and binaries compiled under 8 configurations: 2 compilers, 2 optimizations, and 2 symbol settings (Yang et al., 26 Jun 2026). The procedure locates the binary function most similar to the vulnerable source function, extracts vulnerable blocks containing deleted patch lines and patched blocks containing added patch lines, maps them to blocks in the binary function, and computes the highest vulnerable score \alpha and patched score \beta. If

αβ,\alpha \geq \beta,

the binary is classified as vulnerable (Yang et al., 26 Jun 2026).

In that setting, React reports precision 0.7287, recall 0.2938, and F1 0.4187, whereas SBridge reports precision 0.5949, recall 0.4218, and F1 0.4936 (Yang et al., 26 Jun 2026). The result indicates a higher-recall, higher-F1 operating point for SBridge, at the cost of more false positives than React.

The paper identifies several limitations. Decompilation quality directly affects accuracy. Aggressive -O2 transformations reduce performance. Stripping hurts slightly because some syntactic signals remain useful. Exceptionally long decompiled functions can produce incorrect mappings even with length filtering. The method is not fully semantic and future work is suggested to incorporate features such as data flow. The evaluation assumes that -O0 binaries are not inlined. Accuracy may vary with reverse-engineering tools because experiments used Ghidra. The dataset scope is restricted to the two most binary-rich BinKit packages and may not represent all software ecosystems (Yang et al., 26 Jun 2026).

Taken together, these results position SBridge as a block-centric cross-domain matching method rather than a generic syntax matcher. Its main claim is not that source and binary code can be compared directly at the whole-function level, but that matching becomes substantially more precise when functions are decomposed into control blocks whose key features and auxiliary contents survive compilation, stripping, and especially function inlining (Yang et al., 26 Jun 2026).

Definition Search Book Streamline Icon: https://streamlinehq.com
References (1)

Topic to Video (Beta)

No one has generated a video about this topic yet.

Whiteboard

No one has generated a whiteboard explanation for this topic yet.

Follow Topic

Get notified by email when new papers are published related to SBridge.