Skip to content

Add a GnuTLS certtool decompiler fixture - #207

Open
zardus wants to merge 2 commits into
masterfrom
feature/adopt-binaries-6745
Open

Add a GnuTLS certtool decompiler fixture#207
zardus wants to merge 2 commits into
masterfrom
feature/adopt-binaries-6745

Conversation

@zardus

@zardus zardus commented Aug 27, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Problem

The nested-switch decompiler regression in the linked angr consumer needs the real GnuTLS certtool function print_info at 0x1d658. The binaries baseline has no fixture for that production control flow.

Root cause

The reproducer existed only as a public DecBench ELF with debug paths unsuitable for a compact shared fixture. The consumer cannot download or build it during testing.

Fix

Add tests/x86_64/decompiler/gnutls_certtool_O0 after stripping only debug sections. Record the pinned DecBench source, exact transformation, source and fixture hashes, and GPLv3-or-later license beside it.

Testing

GNU Binutils 2.46 objcopy --strip-debug reproduces the committed fixture byte-for-byte, and artifact inspection confirms the expected ELF and symbol. The validation record carries the exact evidence and content-identical rebase proof.

Validation: #207 (comment)

sync: angr/angr#6745

session: sharpen

@zardus

zardus commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head b2ad8d76e3cbd4b2824d6494e31ba400600981b4 against baseline a87538bc248531d2a1d22434143e8b9752459bac.

  • Exact PR shape: the own-merge-base/head scan verifies the added compiler-produced fixture and its load-bearing format/address shape: ELF64 x86-64 executable; print_info is present at link 0x1d658 (mapped 0x41d658).
  • Provenance/reproduction: GNU Binutils 2.46 objcopy --strip-debug was rerun twice in separate fresh directories; both outputs byte-compare with each other and with the committed bytes.
  • Input/native identity: committed SHA-256 8796fa118358142d7abd5c239a25e78063be225410afafdbb6659344f3e06a2a; rustylib mapped from the isolated consumer worktrees on both arms, SHA-256 d02d517eceb3e81de4a96330678444f0faf2c3b065953f0cb8e1813583a0380f.
  • Real consumer: Decompiler: preserve outer-switch exits through nested switches angr#6745 at baseline b0feae57a9e401292e03a6f9ffbd4b388d172bf5 and head 5913b51ef3da0c0628c8feca79fc7fb87a805f64 loaded the committed fixture and produced the complete output linked in this PR's output record.
  • Repeat/order control: A/B/B/A, ASLR disabled and PYTHONHASHSEED=0; same-arm output is byte-identical and the cross-arm delta is nonempty.
  • Local scope: this recapture validates fixture bytes, fixture shape, and the actual consumer path only. It does not claim a fresh full-workspace gate.
  • Hosted status: fresh hosted dependency validation for this exact binaries head has not been observed in this recapture; it remains pending and no hosted success is claimed here.

@zardus

zardus commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Exact fixture-shape and consumer-output record for #207.

  • Fixture comparison: own merge base a87538bc248531d2a1d22434143e8b9752459bac versus exact head b2ad8d76e3cbd4b2824d6494e31ba400600981b4. The target path/shape is absent on the merge base and present on the head. ELF64 x86-64 executable; print_info is present at link 0x1d658 (mapped 0x41d658).
  • Fixture identity: SHA-256 8796fa118358142d7abd5c239a25e78063be225410afafdbb6659344f3e06a2a; source provenance: DecBench revision e5eb576d66ee36793b800a4dd45e291e0add4472, O0/gnutls/certtool. The complete readelf/objdump scan is retained with the exact script. The recorded recipe (GNU Binutils 2.46 objcopy --strip-debug) was rerun twice in fresh directories; both runs are byte-identical to the committed fixture.
  • Actual consumer: Decompiler: preserve outer-switch exits through nested switches angr#6745, exact baseline b0feae57a9e401292e03a6f9ffbd4b388d172bf5 versus exact head 5913b51ef3da0c0628c8feca79fc7fb87a805f64; target print_info; configuration full / SAILR.
  • Native identity: rustylib mapped from the isolated consumer worktrees on both arms, SHA-256 d02d517eceb3e81de4a96330678444f0faf2c3b065953f0cb8e1813583a0380f.
  • Determinism: fresh A/B/B/A order, ASLR disabled and PYTHONHASHSEED=0; each same-arm pair is byte-identical and every base/head comparison below is nonempty. Absolute checkout prefixes in tracebacks are normalized to <workspace> for publication; raw output is retained unchanged.

Beforeprint_info

Exact consumer baseline
Traceback (most recent call last):
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-medium/capture_decompile.py", line 126, in <module>
    result = project.analyses[Decompiler].prep(fail_fast=True)(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/yvwk46zd4zvbwzqwhpd8kddpz14zda49-python3-3.12.13/lib/python3.12/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/analysis.py", line 249, in wrapper
    oself.__init__(*args, **kwargs)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/decompiler.py", line 241, in __init__
    self._decompile_with_cache()
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/decompiler.py", line 287, in _decompile_with_cache
    self._decompile()
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/utils/timing.py", line 73, in timed_func
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/decompiler.py", line 523, in _decompile
    rs = self.project.analyses[RecursiveStructurer].prep(kb=self.kb, fail_fast=self._fail_fast)(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/yvwk46zd4zvbwzqwhpd8kddpz14zda49-python3-3.12.13/lib/python3.12/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/analysis.py", line 249, in wrapper
    oself.__init__(*args, **kwargs)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/recursive_structurer.py", line 53, in __init__
    self._analyze()
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/recursive_structurer.py", line 60, in _analyze
    self._structure_overlay_tree()
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/recursive_structurer.py", line 100, in _structure_overlay_tree
    st: StructurerBase = self.project.analyses[self.structurer_cls].prep(
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/yvwk46zd4zvbwzqwhpd8kddpz14zda49-python3-3.12.13/lib/python3.12/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/analysis.py", line 249, in wrapper
    oself.__init__(*args, **kwargs)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/sailr.py", line 35, in __init__
    super().__init__(
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 151, in __init__
    self._analyze()
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 189, in _analyze
    progressed = self._analyze_acyclic()
                 ^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 1199, in _analyze_acyclic
    any_matches_this_iteration = self._match_acyclic_schemas(
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 1236, in _match_acyclic_schemas
    matched = self._match_acyclic_switch_cases(graph, full_graph, node)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 1286, in _match_acyclic_switch_cases
    r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/phoenix.py", line 1654, in _match_acyclic_switch_cases_address_loaded_from_memory
    self._switch_handle_gotos(cases, node_default, switch_end_addr)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/structurer_base.py", line 255, in _switch_handle_gotos
    walker.walk(case_node)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 63, in walk
    return self._handle(sequence)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 72, in _handle
    return handler(node, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 100, in _handle_Sequence
    new_node = self._handle(node_, parent=node, index=i)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 72, in _handle
    return handler(node, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/structurer_base.py", line 245, in _handle_SwitchCase
    return walker._handle_SwitchCase(node, parent=parent, index=index, label=label)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 156, in _handle_SwitchCase
    new_default_node = self._handle(node.default_node, parent=node, index=0, label="default")
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/sequence_walker.py", line 72, in _handle
    return handler(node, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/structuring/structurer_base.py", line 227, in _rewrite_gotos
    insert_node(parent, "after", break_node, index)
  File "<workspace>/scratch/apply-latest-skills-pr-audit-20260828/output-regeneration-heavy/worktrees/6745-base/angr/analyses/decompiler/utils.py", line 504, in insert_node
    raise TypeError(
TypeError: Unsupported label value "None". Must be one of the following: switch_expr, case, default.

Afterprint_info

Exact consumer head
typedef struct struct_0 {
    unsigned long long field_0;
} struct_0;

extern long long g_446b2e;
extern struct_0 *GNUTLS_3_4::gnutls_free;
extern void GLIBC_2.2.5::stdout;

unsigned int print_info(long long a0, unsigned int a1, unsigned int a2)
{
    long long v12;  // rax
    long long v13;  // rax
    long long v22;  // rax
    long long v23;  // rax
    long long v14;  // rax
    long long v15;  // rax
    char v0;  // [bp-0x184]
    unsigned int v1;  // [bp-0x180]
    unsigned int v2;  // [bp-0x178]
    unsigned int v3;  // [bp-0x174]
    unsigned long v4;  // [bp-0x170]
    long long v5;  // [bp-0x168], Other Possible Types: unsigned long long
    long long v6;  // [bp-0x160]
    unsigned long v7;  // [bp-0x158]
    unsigned int v8;  // [bp-0x150]
    char v9;  // [bp-0x148]
    char v10;  // [bp-0x118]

    v4 = 33;
    v5 = gnutls_session_get_desc(a0);
    log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Description: %s\n", v5, "- Description: %s\n");
    GNUTLS_3_4::gnutls_free(v5);
    v5 = 0;
    gnutls_session_get_id(a0, &v9, &v4, &v9);
    if (v4)
    {
        v12 = raw_to_string(&v9, v4, v4);
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Session ID: %s\n", v12, "- Session ID: %s\n");
    }
    v1 = gnutls_kx_get(a0);
    gnutls_auth_get_type(a0);
    switch ((unsigned int)gnutls_auth_get_type(a0))
    {
    case 1:
        v7 = 0x100;
        if (!gnutls_server_name_get(a0, &v10, &v7, &v0, 0))
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Given server name[%d]: %s\n", v0, &v10);
        if ((char)a2 & 2 && !gnutls_certificate_get_ours(a0))
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- No certificate was sent to peer\n", "- No certificate was sent to peer\n");
        if ((char)a2 & 1)
            print_cert_info(a0, a1, a2 & 1, a1);
        switch (v1)
        {
        case 2: case 3:
            ::0x41d06e::print_dh_info(a0, "Ephemeral ", a1, "Ephemeral ");
            break;
        case 12: case 13:
            print_ecdh_info(a0, "Ephemeral ", a1, "Ephemeral ");
            break;
        default:
            goto LABEL_41da8c;
        }
        break;
    case 2:
        if (v1 == 11)
        {
            print_ecdh_info(a0, "Anonymous ", a1, "Anonymous ");
            break;
        }
        else
        {
            ::0x41d06e::print_dh_info(a0, "Anonymous ", a1, "Anonymous ");
            break;
        }
    case 3:
        if (gnutls_srp_server_get_username(a0))
        {
            v13 = gnutls_srp_server_get_username(a0);
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- SRP authentication. Connected as '%s'\n", v13, "- SRP authentication. Connected as '%s'\n");
        }
        break;
    case 4:
        if (gnutls_psk_client_get_hint(a0))
        {
            v14 = gnutls_psk_client_get_hint(a0);
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- PSK authentication. PSK hint '%s'\n", v14, "- PSK authentication. PSK hint '%s'\n");
        }
        if (gnutls_psk_server_get_username(a0))
        {
            v15 = gnutls_psk_server_get_username(a0);
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- PSK authentication. Connected as '%s'\n", v15, "- PSK authentication. Connected as '%s'\n");
        }
        if (v1 == 10)
            ::0x41d06e::print_dh_info(a0, "Ephemeral ", a1, "Ephemeral ");
        if (v1 == 14)
            print_ecdh_info(a0, "Ephemeral ", a1, "Ephemeral ");
        break;
    case 5:
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- TLS/IA authentication\n", "- TLS/IA authentication\n");
        break;
    }
LABEL_41da8c:
    if (a1)
    {
        v2 = gnutls_protocol_get_version(a0);
        v6 = (!gnutls_protocol_get_name(v2) ? "Unknown" : gnutls_protocol_get_name(v2));
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Version: %s\n", v6, "- Version: %s\n");
        if (v2 <= 4)
        {
            v6 = (!gnutls_kx_get_name(v1) ? "Unknown" : gnutls_kx_get_name(v1));
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Key Exchange: %s\n", v6, "- Key Exchange: %s\n");
        }
        if (gnutls_sign_algorithm_get(a0))
        {
            v6 = (!gnutls_sign_get_name(gnutls_sign_algorithm_get(a0) & 0xffffffff) ? "Unknown" : gnutls_sign_get_name(gnutls_sign_algorithm_get(a0) & 0xffffffff));
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Server Signature: %s\n", v6, "- Server Signature: %s\n");
        }
        if (gnutls_sign_algorithm_get_client(a0))
        {
            v6 = (!gnutls_sign_get_name(gnutls_sign_algorithm_get_client(a0) & 0xffffffff) ? "Unknown" : gnutls_sign_get_name(gnutls_sign_algorithm_get_client(a0) & 0xffffffff));
            log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Client Signature: %s\n", v6, "- Client Signature: %s\n");
        }
        v6 = (!gnutls_cipher_get_name(gnutls_cipher_get(a0) & 0xffffffff) ? "Unknown" : gnutls_cipher_get_name(gnutls_cipher_get(a0) & 0xffffffff));
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Cipher: %s\n", v6, "- Cipher: %s\n");
        v6 = (!gnutls_mac_get_name(gnutls_mac_get(a0) & 0xffffffff) ? "Unknown" : gnutls_mac_get_name(gnutls_mac_get(a0) & 0xffffffff));
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- MAC: %s\n", v6, "- MAC: %s\n");
    }
    log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Options:", "- Options:");
    if (gnutls_session_ext_master_secret_status(a0))
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), " extended master secret,", " extended master secret,");
    if (gnutls_safe_renegotiation_status(a0))
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), " safe renegotiation,", " safe renegotiation,");
    if (gnutls_session_etm_status(a0))
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), " EtM,", " EtM,");
    if (gnutls_ocsp_status_request_is_checked(a0, 1))
    {
        v22 = (!gnutls_ocsp_status_request_is_checked(a0, 0) ? "[ignored]" : &g_446b2e);
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), " OCSP status request%s,", v22, *((long long *)&GLIBC_2.2.5::stdout));
    }
    log_msg(*((long long *)&GLIBC_2.2.5::stdout), "\n", "\n");
    v3 = gnutls_srtp_get_selected_profile(a0, &v0, &v0);
    if (!v3)
    {
        v23 = gnutls_srtp_get_profile_name(v0);
        log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- SRTP profile: %s\n", v23, "- SRTP profile: %s\n");
    }
    v3 = gnutls_alpn_get_selected_protocol(a0, &v7, &v7);
    if (v3)
    {
        print_channel_bindings(a0, a1, a1);
        fflush(*((long long *)&GLIBC_2.2.5::stdout));
        return 0;
    }
    log_msg(*((long long *)&GLIBC_2.2.5::stdout), "- Application protocol: %.*s\n", v8, v7);
    print_channel_bindings(a0, a1, a1);
    fflush(*((long long *)&GLIBC_2.2.5::stdout));
    return 0;
}

@zardus
zardus force-pushed the feature/adopt-binaries-6745 branch from f60e2b3 to b2ad8d7 Compare August 28, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant