Oxidizer: Rust pseudocode generation by ltfish · Pull Request #6283 · angr/angr
* Refine how CleanupCodeRemover removes function calls to be compatible with ReturnDuplicator * Fix CFGTransformationMixin.remove_block/replace_jump_target * Fix how PatternMatchSimplifier selects arms * Add ErrorPropagationSimplifier
…ypeConstant The assertion added in 8f2e9f7 wrongly narrowed typevar to (TypeVariable, DerivedTypeVariable), but the elif branch above explicitly allows TypeConstant (e.g., Pointer64(Int8)) to flow through. This caused ~120 decompiler tests to fail with AssertionError in simple_solver.py:451. Remove the erroneous assert and broaden DerivedTypeVariable.type_var's type annotation to include TypeConstant, which reflects the actual runtime contract. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tests/rust/test_rustc_version_identification.py requires Rust FLIRT signatures to detect rustc versions via angr.rust.utils.rust_sigs. The flirt_signatures package is not on PyPI, so add it as a git source and include it in the extras dependency group (installed by default via `uv sync`), mirroring how pysoot is handled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the caller passes default decompiler options (e.g., from angr-management), options_by_class["region_simplifier"] contains simplify_ifelse (registered as a default-True region_simplifier option in decompilation_options.py). Previously it was also passed explicitly via simplify_ifelse=self._flavor != "rust", causing "TypeError: got multiple values for keyword argument 'simplify_ifelse'" every time angr-management decompiled a function. Pop simplify_ifelse from the unpacked options dict so the explicit kwarg wins unconditionally, preserving the intent of forcing if-else simplification off for the Rust flavor regardless of user options. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Function.is_prototype_guessed is a read-only @Property derived from self._prototype_source. Assigning to it raises AttributeError at runtime (no setter) and also trips pyright. Three Rust-path call sites hit this: - rust/optimization_passes/rust_calling_convention.py: use PrototypeSource.CCA_DECOMPILER (decompiler-level CCA result). - rust/optimization_passes/function_prototype_inference.py: same. - rust/analyses/type_db_loader.py: use PrototypeSource.SIGNATURES (prototypes loaded from a curated type database). Also fix a related TypedDict collision in analyses/decompiler/clinic.py where `is_prototype_guessed=False` was passed explicitly to ailment.Expr.Call alongside `**last_stmt.tags`, whose TagDict already declares is_prototype_guessed. Merge the override into a local dict before unpacking so pyright is happy and the intent is preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- clinic.py: remove duplicate `Typehoon` import (F811); the package-level re-export in angr.analyses.typehoon already provides it. - engine_ail.py: collapse nested `if` into a single conjunction (SIM102). - rust/typehoon/translator.py: merge two `startswith` calls into tuple form for RustEnum name detection (PIE810 x2). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RustcVersionIdentification relies on angr.rust.utils.rust_sigs to find the Rust FLIRT signature directory. That helper looks at the angr-management submodule path (angrmanagement/resources/flirt_signatures), but only consults `sys.modules["angrmanagement"]` — it does not import angrmanagement itself. test_rustc_version_identification.py only imported angr, so the check always failed and the analysis returned rustc_version=None, causing every SUBFAILED assertion. Import angrmanagement at the top of the test module so the signature directory is discovered. angr-management is installed in CI via install.sh, so the import succeeds there. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Top-level `import angrmanagement` broke collection on the smoketest jobs (Windows/macOS), which don't install angr-management. Switch to pytest.importorskip so environments without angrmanagement skip the module at collection time, while the main CI test jobs (where angr-management is installed) still import it and let RustcVersionIdentification find the bundled FLIRT signatures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…for Rust flavor Commit ed10e60 ("Fix some bugs") changed the Python default of PhoenixStructurer.__init__'s use_multistmtexprs from MAX_ONE_CALL to NEVER. This was inconsistent with the option registered in decompilation_options.py (which keeps MAX_ONE_CALL) and silently affected every caller that did not explicitly pass use_multistmtexprs through `decompiler_options`, because Decompiler.options_to_params only threads user-provided options and falls back to Python parameter defaults for the rest. The observable regression: tests that assert `count("goto") == 0` or expect `return` statements to appear (e.g. test_decompiling_tr_build_spec_list, test_eliminating_stack_canary_reused_stack_chk_fail_call) failed because phoenix could no longer eliminate certain gotos by folding assignments into multi-statement expressions. Revert the Python default so the C path is unchanged, and force use_multistmtexprs=NEVER at the Decompiler level when the flavor is "rust", mirroring how simplify_ifelse is handled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…binary Commit 7fa5227 added tracking of writes to the overflow return register (e.g., rdx on x64) so that 128-bit Rust return values could be detected, and unconditionally added max(overflow_retval_sizes) onto every per-block retval_size. Commit 3ffb99e correctly gated the 9..16 return-size type mapping in CallingConventionAnalysis._guess_retval_type behind is_rust_binary, but the fact collector's overflow accumulation was not gated the same way. On non-Rust binaries the overflow register is typically a scratch register, so any incidental write to it inflates retval_size past 8. _guess_retval_type then finds no matching size bucket (because the 9..16 range is gated off) and returns SimTypeBottom(label="void"), making _adjust_prototype mark the whole function as returning void. Concretely, test_decompiling_msvcrt_IsExceptionObjectToBeDestroyed's vcruntime_test.exe walks a linked list with rdx, which bumped its retval_size from 4 to 12 and wiped `return 1;` / `return 0;` from the output. Only set overflow_retreg_offset for Rust binaries so the scratch-register case no longer pollutes retval_sizes on C binaries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Commit ed2a601 ("Support Rust v0 demangling") replaced the old split-and-rejoin-dropping-the-last-node logic with a direct `rust_demangler.demangle(self.name)` call, which preserves the trailing 17-character `h<16 hex>` disambiguation hash (e.g., "std::rt::lang_start::h9b2e0b6aeda0bae0"). The C code generator then emits function names with these hashes in calls, breaking tests that expect the clean Rust name (e.g., test_function_pointer_identification asserts `"std::rt::lang_start(rust_hello_world::main"` in the output). Drop the trailing hash component when it matches Rust's format ("h" + 16 lowercase hex chars), restoring the pre-PR behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters