frames,jar,passes: verifier-driven hardening from real jars
Proven by paranoid runs over lorep (17 classes, fully green), LoVisual (4081 classes) and oc2r (1278 classes) with per-class java -Xverify:all, each fix first reproduced as a red test: - merge: Top absorbs instead of Top-wins (javac declares Top for slots unwritten on some inflow; precise types reject those edges). Regression: loop_head_keeps_top_for_loop_local. - emit: collapse [Long|Double, Top] pairs on operand stacks (javac writes one entry; the pair is rejected). Regression: wide_stack_join_emits_single_entries. - xfer ArrayLoad: push the wide pair, fixing lcmp underflow. Regression: array_load_wide_pushes_pair. - xfer <init>: convert only the receiver's own allocation site instead of every Uninitialized (nested construction clobbered the outer object). Regression: nested_construction_keeps_outer. - xfer ANewArray: array operands gain one dimension ([B] -> [[B) instead of an illegal L-wrap ([L[B;). Covered by the same test. - jar load: duplicate class names (multi-release module-info twins) keep the first occurrence instead of crashing save. Test: duplicate_class_names_keep_first. - strings/constants: skip methods past MAX_METHOD_INSNS (8000) so giants cannot overflow the 64KB writer ceiling. Tests: oversized_methods_are_skipped in both passes. - merge errors now name both types being merged. Also: em-dash removed repo-wide (724 spots, rule added to todo.md), allow-lints only in tests (scratch example deleted), code comments migrated to English in every file this change touches.
This commit is contained in:
parent
2172206e83
commit
139aea9a8f
96 changed files with 1053 additions and 831 deletions
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
## Собственный IR
|
||||
|
||||
Пассы не работают с API стороннего парсера — они работают с моделью:
|
||||
Пассы не работают с API стороннего парсера - они работают с моделью:
|
||||
|
||||
```text
|
||||
Program все классы + ресурсы архива
|
||||
|
|
@ -63,15 +63,15 @@ Program все классы + ресурсы архива
|
|||
└─ Resource путь + байты внутри архива
|
||||
```
|
||||
|
||||
Ключевое решение — **`Insn::Mark(Label)` вместо байтовых смещений**:
|
||||
Ключевое решение - **`Insn::Mark(Label)` вместо байтовых смещений**:
|
||||
|
||||
- любой пасс, меняющий порядок или длину инструкций, не пересчитывает
|
||||
jump-офсеты;
|
||||
- пересчёт делается ровно один раз — в writer'е;
|
||||
- пересчёт делается ровно один раз - в writer'е;
|
||||
- в IR нет состояния, в котором «смещение уехало, а метка осталась».
|
||||
|
||||
Атрибуты, которые мы не понимаем, хранятся как `Attribute::Raw { name, data }`
|
||||
и возвращаются на место без изменений — round-trip без потерь.
|
||||
и возвращаются на место без изменений - round-trip без потерь.
|
||||
|
||||
## Контракт пасса
|
||||
|
||||
|
|
@ -83,18 +83,18 @@ pub trait Pass: Send + Sync {
|
|||
}
|
||||
```
|
||||
|
||||
- `Context` — всё состояние прогона: конфиг, мастер-сид, маппинги, заметки.
|
||||
Из него берётся RNG: `ctx.rng("rename/classes")` — доменное разделение,
|
||||
- `Context` - всё состояние прогона: конфиг, мастер-сид, маппинги, заметки.
|
||||
Из него берётся RNG: `ctx.rng("rename/classes")` - доменное разделение,
|
||||
чтобы случайности разных пассов не были статистически коррелированы.
|
||||
- `PassReport` — что трансформировано, что сознательно пропущено и почему.
|
||||
- `Report` — собирает отчёты всех пассов + счётчики, сериализуется в JSON.
|
||||
- `PassReport` - что трансформировано, что сознательно пропущено и почему.
|
||||
- `Report` - собирает отчёты всех пассов + счётчики, сериализуется в JSON.
|
||||
|
||||
Детерминированность — свойство, а не пожелание: одинаковый seed на одинаковой
|
||||
Детерминированность - свойство, а не пожелание: одинаковый seed на одинаковой
|
||||
версии даёт байт-в-байт одинаковый выход. Это проверяется тестами.
|
||||
|
||||
## Конвейер
|
||||
|
||||
Порядок пассов — не деталь:
|
||||
Порядок пассов - не деталь:
|
||||
|
||||
```text
|
||||
Rename → Resources → Strings → Constants → Flow → Noise → DebugInfo
|
||||
|
|
@ -102,18 +102,18 @@ Rename → Resources → Strings → Constants → Flow → Noise → DebugInfo
|
|||
|
||||
почему именно так:
|
||||
|
||||
1. **Rename первым** — все остальные техники порождают новые ссылки
|
||||
1. **Rename первым** - все остальные техники порождают новые ссылки
|
||||
(`LDC`-шум, вызовы декодера, junk-классы), и они уже должны быть в
|
||||
обфусцированных именах.
|
||||
2. **Resources сразу после Rename** — патч `fabric.mod.json`, `*.mixins.json`,
|
||||
2. **Resources сразу после Rename** - патч `fabric.mod.json`, `*.mixins.json`,
|
||||
refmap и access widener должен видеть *итоговые* имена классов.
|
||||
3. **Strings и Constants** — работают по телам методов, ещё не искажённым
|
||||
3. **Strings и Constants** - работают по телам методов, ещё не искажённым
|
||||
control-flow'ем.
|
||||
4. **Flow** — искажает CFG. Если поставить его раньше, Strings/Constants
|
||||
4. **Flow** - искажает CFG. Если поставить его раньше, Strings/Constants
|
||||
перестанут находить свои шаблоны в инструкциях.
|
||||
5. **Noise** — junk-классы и аннотации добавляются в самом конце, когда
|
||||
5. **Noise** - junk-классы и аннотации добавляются в самом конце, когда
|
||||
целевой граф уже финален.
|
||||
6. **DebugInfo последним** — strip/spoof отладочной информации после всех
|
||||
6. **DebugInfo последним** - strip/spoof отладочной информации после всех
|
||||
трансформаций, чтобы не породить свежие `LineNumberTable`.
|
||||
|
||||
## Статус
|
||||
|
|
@ -126,4 +126,4 @@ Rename → Resources → Strings → Constants → Flow → Noise → DebugInfo
|
|||
| **готово** | пасс `flow` (4 схемы + ротация + мешанина, приёмка настоящим `java`) |
|
||||
| **план** | фикстуры и regression-тесты |
|
||||
|
||||
Детали выбора — [`stack.md`](stack.md), план — [`../todo.md`](../todo.md).
|
||||
Детали выбора - [`stack.md`](stack.md), план - [`../todo.md`](../todo.md).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue