angr / angr/cle

MachOSymbol.is_function is always False, so CFGFast never seeds from a Mach-O symbol table

Abierto
#769 2 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
485
Forks
135
Merge medio
2 d 2 h
PR fusionados (30 d)
15

Descripción

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

`MachOSymbol.is_function` returns `False` for every symbol, unconditionally:

```python
@property
def is_function(self):
# Incompatibility to CLE
log.debug("It is not possible to decide wether a symbol is a function or not for MachOSymbols")
return False
```

That contradicts the type the same class assigns. `SymbolTableSymbol.__init__` sets `self._type = SymbolType.TYPE_FUNCTION_OR_OBJECT` for exported, stab and local symbols, and `Symbol.is_function` on the base class answers `True` for exactly that type — but the override wins, so a symbol reports `TYPE_FUNCTION_OR_OBJECT` and `is_function == False` at the same time.

It is visible on this project's own fixtures:

```python
import cle
mo = cle.Loader("angr/binaries/tests/aarch64/macho_lib_loading/FrameWorkApp.app_15/FrameWorkApp",
auto_load_libs=False).main_object
sum(1 for s in mo.symbols if str(s.type).endswith("TYPE_FUNCTION_OR_OBJECT")) # 143
sum(1 for s in mo.symbols if s.is_function) # 0
```

Every Mach-O fixture I checked behaves the same way — `dyld_ios15.macho`, `macho_nop_stub`, both `FrameWorkApp` variants and their `dynamicLibrary` frameworks — typed symbols on one side, zero on the other.

The consequence is downstream: `CFGFast` seeds functions from `symbol.is_function`, so **no Mach-O binary is ever seeded from its symbol table**. On one aarch64 Mach-O shared library I measured — digest `a1fcc20929f610de966b4667bdf5dcaf437275b0a43d6388ad5682996b1cffc8`, a Homebrew-installed dylib — the file records 1,311 function starts and angr recovers 638. Of the 673 it misses, **629 are never decoded at all**: no block covers them, because with `force_complete_scan=False` nothing reaches them and nothing seeds them. That object carries no `LC_FUNCTION_STARTS` table and no `.eh_frame`, so its symbol table is the only thing that records where its code begins.

The comment is fair as far as it goes — Mach-O's `nlist` does not separate code from data the way ELF's `STT_FUNC` does. But the decision is not undecidable: a symbol whose `n_type` says `N_SECT` and whose section is executable is a function to a useful approximation, and it can be checked against the bytes. angr/cle#754 and angr/angr#6861 already take that route for `LC_FUNCTION_STARTS`, with a byte-level code test on the angr side, so the machinery for doing this carefully exists. What they do not cover is an object with no function-starts table, which is where the symbol table is all there is.

Worth pairing with a real caution rather than seeding naively: GHC emits `_dsp_`-prefixed info tables into `__TEXT,__text` in a section carrying `S_ATTR_PURE_INSTRUCTIONS`, so "in an executable section" alone will seed data as functions. Any fix here needs the same code test.

Happy to open a pull request if you would like this pursued, and to say which direction you prefer.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.