Minidump gives every module an abstract Section, so CFGFast raises NotImplementedError on any dump with a loaded module
- 主要言語
- Python
- スター
- 485
- フォーク
- 135
- 平均マージ
- 2日 2時間
- マージ済み PR(30日)
- 15
説明
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
`Minidump.__init__` builds its regions from the two base classes in `cle/backends/region.py` directly: one `Segment` per dumped memory range (`cle/backends/minidump/__init__.py:62`) and one `Section` per loaded module (`:72`).
Neither base class can describe permissions. `Section.is_readable`, `.is_writable`, `.is_executable` and `.only_contains_uninitialized_data` all `raise NotImplementedError()`, and `Segment` inherits `Region`, which answers `True` to all three unconditionally — the same defect reported in #743.
The dump loads fine. Anything that then asks where code lives does not, because `CFGBase._executable_memory_regions` prefers `sections` when an object has them and reads `section.is_executable` (`angr/analyses/cfg/cfg_base.py:870`). The fixture already in `angr/binaries` is enough:
```python
import angr, cle
ld = cle.Loader("binaries/tests/x86/windows/jusched_x86.dmp", auto_load_libs=False)
ld.main_object.sections[0].is_executable # NotImplementedError
ld.main_object.segments[0].is_executable # True, for every one of the 173 segments
angr.Project("binaries/tests/x86/windows/jusched_x86.dmp", auto_load_libs=False).analyses.CFGFast()
# NotImplementedError, from cle/backends/region.py:190, before CFGFast does any work
```
So CFGFast cannot run on any minidump that has at least one loaded module. In a sweep of a large object corpus, 1,879 of 1,969 minidump observations end this way; the other 90 fail earlier for unrelated reasons. All 1,859 distinct dumps behind those observations reproduce it on current master.
Fixing this is not a matter of picking a subclass, because the dump does not always carry the answer.
**The permission stream is optional.** `MINIDUMP_MEMORY_INFO_LIST` holds a real `PAGE_*` value per range, but `MiniDumpWriteDump` writes it only when passed `MiniDumpWithFullMemoryInfo`. `jusched_x86.dmp` has it (251 entries: 85 `PAGE_READONLY`, 55 `PAGE_READWRITE`, 52 `PAGE_NOACCESS`, 33 `PAGE_EXECUTE_READ`), so a fix tested against the fixture alone will look complete. Of the 1,859 dumps above, 1,857 have no such stream. That number needs a caveat: 1,856 of them come from one generating harness, so it measures that harness more than it measures the world. The corpus holds only three dumps captured from real crashes, and one of those three also has no `MemoryInfoListStream`, which is enough to say the absent case is not hypothetical.
**A `Section` here is a whole loaded module** — a mapped PE image with `r-x` text beside `r--` and `rw-` data. One permission triple cannot describe it honestly whichever value it takes.
**The `Segment`s are the regions that could carry per-range permissions**, and they are the ones currently claiming `rwx`. Making the sections answer without touching the segments leaves that in place; removing the sections so the segments are consulted turns every dumped heap, stack and guard page into executable memory, which is #743 in a second backend.
Options, none of which is obviously the intended one:
1. Give `Segment` real permissions from `MemoryInfoListStream` and drop the per-module `Section`s, accepting that dumps without that stream have no executable map at all. CFGFast then recovers nothing from them rather than crashing, which is quieter but not better.
2. Keep the per-module `Section`s and read each module's own section table out of the dumped image. The module headers are mapped: `MZ` is present at `module.baseaddress` for every module of every dump checked, including the ones with no `MemoryInfoListStream`. This gives per-section permissions from the module itself rather than from the optional stream, at the cost of parsing a child PE per module.
3. Let a `Section` report that it does not know, and have `CFGBase._executable_memory_regions` fall back to the segments in that case. This needs the "unknown" state that #743 also asks for.
#682 is open against this and takes option 1. As written it does not survive a dump with no `MemoryInfoListStream`: `DumpSection` is then constructed with `protect = 0`, and `is_readable` evaluates `self.protect.value`, so `NotImplementedError` becomes `AttributeError: 'int' object has no attribute 'value'`. Loading all 1,859 dumps on that branch and querying the permissions of every section, 1,857 raise `AttributeError` and the 2 with the stream answer. It also splits each module into one section per overlapping segment, taking the fixture from 30 sections to 127 and breaking `tests/test_minidump.py`, which is the objection already raised on it.
The TE and UEFI backends had the same crash from the same base class and were fixed in #713 by giving `TESection` permissions from the PE/COFF section characteristics. That worked because a TE image carries per-section permissions. A minidump often does not, which is why this one is a report rather than the same patch again.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
評価
この issue はまだ評価されていません。