009: Magisk ship pack through US-S5-1 + US-S4 OpenSSH/Jelly/Gallery2.

Release-keys/AVB, CVE clean+hi batches, Yama/slabrand, Pro branding,
bundled apps, and AXP-class product surface docs/scripts.
This commit is contained in:
sunfish-admin committed 2026-10-05 10:42:00 +02:00
1 parent fd793b9471
commit deaaf42caa
129 files changed
+11769 -141

No files matched your search

+4
View File
@@ -48,3 +48,7 @@ aosp-webview*.apk
# 004 proprietary / large prebuilts (host-only) # 004 proprietary / large prebuilts (host-only)
vendor/sunfish-a16/prebuilt/ vendor/sunfish-a16/prebuilt/
*.apk *.apk
# Local AXP OTA extracts (payload.bin ~1GB+)
AXP.OS-*/
*.bin
+22
View File
@@ -25,6 +25,28 @@ unless the owner explicitly asks.
Magisk, microG, and related projects — prefer these over secondary blogs Magisk, microG, and related projects — prefer these over secondary blogs
or guesses. KernelSU docs are reference-only here (optional recycle). or guesses. KernelSU docs are reference-only here (optional recycle).
## Clone AXP first (mandatory)
This track is an **AXP.OS → LOS23/A16** resurrection (~clone). Default method:
1. Find how AXP / EXTENDROM / GOLDEN did it **before inventing a local policy**:
shipped PRO/GOLDEN zips (`payload.bin` → partition images),
`/srv/rom/references/axp/` + EXTENDROM on `rom-dev01`, GOLDEN `adb`
oracle, and real AXP product trees — **not** only Lineage-looking
`upstream_*` mirrors on disroot.
2. **Copy that method** into this tree (same flags, same packaging shape, same
virgin/activation UX where applicable).
3. Only invent or diverge when LOS 23.2, Android 16, or a newer dependency
**breaks** the AXP path — then document why and the minimal fix.
Do **not** redesign Magisk, microG, Phonesky, DNS, connectivity, hardening
package sets, or other AXP-class features “our way” for convenience.
KernelSU/SusFS (`007`) stays **standby** — never mix into Magisk ship boots.
Magisk specifically: EXTENDROM `EXTENDROM_PREROOT_BOOT` (boot patch, Magisk
**not** a `PRODUCT_PACKAGE`, dormant until Additional Setup). See
`docs/009-magisk-preinstall-runbook.md`.
## Working rules ## Working rules
- Do not invent unresolved technical decisions; investigate them in the relevant - Do not invent unresolved technical decisions; investigate them in the relevant
+14 -8
View File
@@ -66,8 +66,13 @@ Checked packages (package-level; process expand not fully visible in shots):
Visible unchecked (partial): Aegis, Amarok, Amaze, Aosmium. Visible unchecked (partial): Aegis, Amarok, Amaze, Aosmium.
Owner notes: most DenyList banking apps still pass; **Yuh fails** for ~weeks. Owner notes (2026-09-13): most DenyList banking apps still pass; **Yuh failed**
Extra Magia DenyList/host checks + Island uninstall/reclone did **not** fix Yuh. for ~weeks. Extra Magia DenyList/host checks + Island uninstall/reclone did
**not** fix Yuh at that time.
**Owner update 2026-10-02:** Yuh works again on GOLDEN after enabling Vector’s
**bootloader spoofer** module (`zygisk_vector` / Vector manager). DenyList alone
was never enough for that HA path.
## Compatibility / attestation (observational) ## Compatibility / attestation (observational)
@@ -75,14 +80,15 @@ Extra Magia DenyList/host checks + Island uninstall/reclone did **not** fix Yuh.
|-------|--------|----------| |-------|--------|----------|
| Swissquote | **PASS** (owner report) | Still used; on DenyList | | Swissquote | **PASS** (owner report) | Still used; on DenyList |
| Other DenyList banks (N26, DKB, …) | **PASS** (owner: “casi todos”) | UI DenyList | | Other DenyList banks (N26, DKB, …) | **PASS** (owner: “casi todos”) | UI DenyList |
| Yuh | **FAIL** — hardware attestation / “certificate is invalid” | Owner UI + logcat ~21:51–21:52 | | Yuh | **PASS again** (owner 2026-10-02) via Vector bootloader spoofer | Was **FAIL** 2026-09-13 (HA / “certificate is invalid”) |
| Play Integrity token path | Client path still runs | `Finsky requestIntegrityToken() finished for com.yuh`; DroidGuard `pia_attest_e1` for `com.yuh` | | Play Integrity token path | Client path still runs | `Finsky requestIntegrityToken() finished for com.yuh`; DroidGuard `pia_attest_e1` for `com.yuh` |
| KeyMint / TEE | Errors concurrent with Yuh attempt | `keystore2`: `HARDWARE_TYPE_UNAVAILABLE` (IRPC / `TRUSTED_ENVIRONMENT`); `addRngEntropy` → `DEAD_OBJECT` / KeyMint `-1000` | | KeyMint / TEE | Errors concurrent with 2026-09-13 Yuh FAIL | `keystore2`: `HARDWARE_TYPE_UNAVAILABLE` (IRPC / `TRUSTED_ENVIRONMENT`); `addRngEntropy` → `DEAD_OBJECT` / KeyMint `-1000` |
Interpretation for later `006`/`007` (not a GOLDEN fix): DenyList alone is Interpretation for Gate E / LAB: GOLDEN banking stack = Magisk DenyList +
insufficient for Yuh’s hardware-attestation path; KeyMint/TEE remote-provisioning `playintegrityfix` + `teesim` + **Vector bootloader spoofer** (owner-proven for
errors correlate with the failure window. Project production root path remains Yuh). Ship ROM still does **not** bake those modules (`009-magisk-preinstall-runbook`);
**KernelSU-Next (+ SusFS) or ReSukiSU evaluation** — Magisk/Magia is oracle-only. owner installs post-flash. Wrong PIF prints (e.g. `comet` CANARY on sunfish LAB)
break the same surface. Magisk/Magia remains the AXP oracle path for `009`.
Call recording: GOLDEN Secure dump shows native EXTENDROM Call recording: GOLDEN Secure dump shows native EXTENDROM
`extendrom_call_recording=1` (see `008` — port `er.sh` F_CALLREC, not Magisk `extendrom_call_recording=1` (see `008` — port `er.sh` F_CALLREC, not Magisk
+66
View File
@@ -0,0 +1,66 @@
# 009 — Remaining path to AXP / Graphene-class ship (post clean-429)
Non-normative roadmap after LAB **BOOT_OK** on Stage1+combo4+FORTIFY+Magisk+
**CVE clean-offset 429** (denylist `CVE-2022-49980`), kernel `#47`
(`20261001T180955Z-axp-stage1-combo4-cve-clean`). Authoritative requirements
remain `specs/009-axp-los23-port/spec.md` + constitution.
Goal (owner): **almost all** applicable AXP.OS / Graphene-class behaviour on
LOS 23.2 sunfish Magisk track — not a blind dump, not bootloader lock.
## Already landed (kernel / Magisk core)
| Layer | Status |
|-------|--------|
| Stage1 Graphene/AH | Ship |
| Combo4 + Clang FORTIFY | Ship |
| Magisk preroot EXTENDROM-class | Ship |
| CVE clean-offset 429 | Ship (LAB OK) |
| `CVE-2022-49980` | Denylist until lock-scope port |
| High-offset CVE (~73) | Quarantine — triage started |
Recover/bisect baseline: `20260929T123401Z-axp-stage1-combo4` (`#32`).
## Kernel remaining (toward “AXP CVE / Graphene depth”)
1. **High-offset quarantine** — KEEP batches 1–3 **shipped** (`151944Z`).
nf_tables leftovers still skipped. Next kernel depth: **US-S5-1** defconfig
(Yama + `SLAB_FREELIST_RANDOM`), not more bulk CVE.
2. **`CVE-2022-49980` proper port** — keep UAF fix without recursive
`udc_lock` (mainline lock-scope series). Optional; denylist is safe.
3. Failed Stage1 probes (allocsize/ro/slab) — low priority vs high-offset.
4. Graphene modern techniques / `hardened_malloc` — backlog (`docs/hardening-backlog.md`);
evaluate after CVE depth is stable.
## Userspace / product remaining (AXP surface)
From `docs/009-capability-matrix.md` / Gate G (still open or partial):
| Item | Notes |
|------|--------|
| Release keys + AVB (unlocked) | US-S2/S3 — leave test-keys |
| Private DNS / connectivity check | Patches exist; keep AXP-class, no axp.os endpoints |
| Call-rec / FLAG_SECURE | `008` — selectable unlock |
| Bundled apps (Aurora, Droidify, gallery, …) | Packaging |
| Disable-app UX on A16 | Restore classic Disable |
| Hosts / DNS privacy helpers | Magisk module vs ROM-native TBD |
| WebView | LOS default (Aosmium exception) |
| Banking / Island | Deferred |
| Bootloader lock | Wont |
| KernelSU swap | Optional later; do not mix Magisk ship boots |
## Suggested execution order (default if owner has no preference)
1. Finish high-offset **relevance filter** → small apply batches (≤10) with
strict applicator + LAB boot each batch.
2. Wire **release keys** bacon of current ship pack; keep Magisk/preroot OK.
3. Land remaining **Settings/UX** AXP clones (DNS, connectivity, disable-app)
if not already on LAB zip.
4. Optional: 49980 lock-scope port; Graphene userspace / hardened_malloc spikes.
## Do-not
- Reflash full-503 / old kh-cve zips.
- Bulk-apply high-offset without manual review.
- Mix KernelSU into Magisk ship boots.
- Treat “almost AXP/Graphene” as requiring locked bootloader.
+54
View File
@@ -0,0 +1,54 @@
# AXP.OS PRO sunfish product/app surface — 20260324 zip
Oracle: `AXP.OS-22.2-20260324-PRO-sunfish.zip` (`payload.bin` → partitions).
Extracted 2026-10-02 (local `payload-dumper-go`). Authoritative for **debloat**
vs Lineage product defaults — not the disroot `upstream_*` lineage mirror.
## `product/app` (present)
Aperture, ApertureLensLauncher, Backgrounds, Camelot, DeskClock,
ExactCalculator, LatinIME, LocalContactsBackup, ModuleMetadata, PhotoTable,
Recorder, ThemesStub, Twelve, messaging, uimremoteclient, uimremoteserver
## `product/app` (absent vs typical LOS product)
| LOS / our prior ship | AXP PRO |
|----------------------|---------|
| Jelly | **absent** (browser stub on system_ext) |
| Gallery2 | **absent** (Fossify only) |
| Etar | **absent** (uses `Etar_ER` on system_ext) |
| AuroraStore / Droidify / Fossify | not on product — on **system_ext** |
| VZWAPNLib / PixelCameraServices* | absent here (device packs may differ) |
## `product/priv-app`
Contacts, Dialer, SettingsIntelligence
## `product/bin`
**No** `/bin` tree (no sshd/ssh/rsync). `etc/init/init.openssh.rc` **present**.
## `system_ext/app` (bundled AXP extras)
AuroraStore, Etar_ER, F-Droid, FossifyGallery_FD, GsfProxy_GH,
MicrogGmsCore_GH_presigned, Profiles, AccessibilityMenu,
QtiTelephonyService, SimpleSettingsConfig,
`noDefaultBrowserWebView`, `noeSpeakNG`
## `system_ext/priv-app` (selected)
AOSLauncher (NeoLauncher — **wont** for 009), AOSmium_webview64
(**exception** — we ship LOS WebView), OpenEUICC_AXP-OS, Seedvault,
TrebuchetQuickStep, Updater, Lineage*, Settings, SystemUI, ims, …
## 009 mapping
| AXP | Our ship policy |
|-----|-----------------|
| AuroraStore | product `AuroraStore` (already) |
| F-Droid | **Droidify** substitute (owner) |
| FossifyGallery_FD | product `FossifyGallery` + override Gallery2 |
| noDefaultBrowserWebView | **US-S4-2**: drop Jelly |
| Gallery2 absent | **US-S4-2**: ensure Gallery2 gone (override alone left dir) |
| NeoLauncher / Aosmium | wont / exception — keep Launcher3 + LOS WebView |
| OpenSSH bins | US-S4-1 done |
+58
View File
@@ -0,0 +1,58 @@
# 009 AXP capability matrix (resurrection track)
Maps GOLDEN/`002` + `docs/axp-pro-parity.md` → ship status for **Magisk AXP
clone on LOS23**. Authoritative requirements remain `specs/009-axp-los23-port/spec.md`.
Status: `required` | `exception` | `deferred` | `impossible`
| category | golden_ref | status | notes |
|----------|------------|--------|-------|
| Base LOS 23.2 sunfish | `003` | required | Vanilla baseline before AXP integrate |
| microG + GSF | `002` / `004` | required | Newest stable |
| Phonesky (project patched) | `002` / `004` | required | Host artifact; FakeStore forbidden |
| Selectable signature spoofing | `005` / GOLDEN `allow_signature_fake` | required | Restricted; DO toggle |
| Selectable call-recording unlock | `008` / GOLDEN `extendrom_call_recording` | required | EXTENDROM key |
| Magisk preroot + DenyList-class | GOLDEN Magisk 29.0 / Magia | required | Newest Magisk; EXTENDROM-class boot patch |
| Bundled usability apps (Aurora, Droidify, gallery, …) | `002` / parity | required | Newest workable; **Droidify** (light + many default repos); **no NeoLauncher** |
| Private DNS presets (AXP-class provider list) | AXP Settings | required | Clone AXP private-DNS entries; no AXP OTA/repos |
| Internet connectivity / INTERNET special permission UX | AXP/Graphene-class | required | Keep behaviour; **do not** integrate `axp.os` endpoints/branding |
| Disable app (classic) vs Archive | AOSP/LOS A16 Archive | required | Restore usable **Disable** on A16; Archive alone disliked by owner |
| DNS/hosts privacy helpers | parity / GOLDEN modules `hosts` | required | If in AXP surface; Magisk module vs ROM-native TBD |
| Userspace hardening / AVB (unlocked) | parity `011`–`012` pulled into `009` | required | Staged; gaps documented; no bootloader lock |
| Release / definitive signing keys | GOLDEN / plan `/srv/rom/secrets` | required | Ship off test-keys; Phonesky same identity |
| Bootloader lock / relock | IOdes-class | wont | AXP/LOS unlocked norm; Magisk preroot assumes unlocked |
| Kernel update + hardening | parity / FR-017 | required | LOS23 sunfish kernel + attributable patches |
| WebView | GOLDEN Aosmium | exception | LOS23 WebView default; chooser unlocked; no Aosmium |
| Banking / Island / Swissquote / Yuh | `002` Gate E | deferred | Owner future (2026-09-17); not MVP |
| Localization / location hardening AXP vs LOS | owner complaint | deferred | FR-018 — **do not extra-cap** vs LOS for ship (2026-09-18); no MVP toggle; compare doc later only |
| NeoLauncher | AXP default | wont | Owner: do not integrate; LOS Launcher3 |
| F-Droid client (`org.fdroid.fdroid`) | GOLDEN | substitute | Prefer **Droidify** |
| AXP external market repos / AXP OTA servers | AXP EOL infra | wont | Servers down / EOL — do not wire |
| Sideloadable system package refresh (microG, Magisk, Phonesky, …) | owner cadence ~2–3 mo | required | FR-019 — same-signer / PRESIGNED / Magisk update path; no OTA |
| KernelSU / SusFS | `007` | deferred | Out of ship; optional recycle FR-016 |
| AXP backup ROM feature | parity | impossible / wont | External NeoBackup only |
| FLAG_SECURE global override | project `008` | deferred | Nice-to-have; not AXP hard gate |
## Bundled packages observed on GOLDEN (2026-09-17 `pm list packages -s`)
| package | role | matrix |
|---------|------|--------|
| `com.aurora.store` | Aurora | required (pin newest) |
| `com.looker.droidify` | Droidify (F-Droid client) | required — preferred over `org.fdroid.fdroid` |
| `org.fdroid.fdroid` | F-Droid | substitute / skip unless packaging forces it |
| `org.fossify.gallery` | Fossify Gallery | required |
| `com.stevesoltys.seedvault` | Seedvault | often los-provides |
| `com.android.launcher3` | Launcher | los-provides — **no NeoLauncher** |
| `org.axpos.aosmium` / `_wv` | Aosmium | exception — do not ship; LOS WebView |
| `com.google.android.gsf` | GSF | required |
| `com.android.vending` | Phonesky | required (project artifact) |
User-installed (not system): Island, banks, Magisk manager (hidden pkg), etc.
## Build oracle note
Prefer AXP **build tree + docs** on `rom-dev01` (see axpos.org developer
building / EXTENDROM preroot) to learn Magisk/product packaging. GOLDEN `adb`
is for runtime behaviour, not the first compile reference. Host currently has
rendered docs under `/srv/rom/references/axp/archive/…`; full AXP `build`
checkout still to stage.
+171
View File
@@ -0,0 +1,171 @@
# 009 CVE bisect plan (after full CVE Google-logo hang)
**Baseline GOOD:** Stage1 + combo4 + Clang FORTIFY
Artifact: `20260929T123401Z-axp-stage1-combo4` (LAB recovered 2026-09-29 via
`fastboot flash boot` of that pack’s Magisk `boot.img`).
**BAD:** Stage1+combo4+FORTIFY + full `cve-strict.list` (503)
Artifact: `20260929T181525Z-axp-stage1-combo4-cve` — compiles, wlan CRC OK,
**hangs at Google logo**. **Do not** re-flash the full 503 set.
## Audit before more flashes (2026-09-29 evening)
Owner review correctly flagged the applicator and asked for reject/partial
review **before** bisect flashes.
| Check | Result |
|-------|--------|
| Patch `FAIL` count on BAD build | **0** (`OK=503`) — no `.rej` from that run |
| Continue-on-fail + delete `.rej` | **Procedural defect** (fixed in `apply_cve_strict.sh`) — would hide incomplete applies on future drift |
| GNU patch “Succeeded with fuzz≥2” | **0** |
| Hunks with **\|offset\| ≥ 200 lines** | **168** of 503 — strong mis-apply risk even when `patch` exits 0 |
| \|offset\| ≥ 200 list | `cve-high-offset-ge200.txt` (**73 unique** paths in report extract; full 168 in host fuzz report) |
| Host report | `/srv/rom/artifacts/009-axp-los23-port/cve-stage/apply-cve-strict-20260929T173037Z-fuzz-report.txt` |
Logo hang alone does **not** identify a CVE id; “Additional Setup” is unrelated.
USB loss after sideload does **not** mean the zip was not installed.
### Applicator fix (`apply_cve_strict.sh`)
- Default: **abort** on first FAIL (no partial tree).
- Keep `*.rej` / `*.orig` under `CVE_REJECT_DIR` (no silent delete).
- **QUALITY_FAIL** if fuzz ≥ `CVE_MAX_FUZZ` (default 1) or
\|offset\| ≥ `CVE_MAX_ABS_OFFSET` (default **200**).
- Forensic only: `CVE_STRICT_CONTINUE=1`.
H1 bacon (`009-bacon-cve-h1`) **stopped** — do not flash that half until a
strict re-apply (offsets gated) produces a clean tree.
## Next method (revised)
1. Build allowlist = `cve-strict.list` **minus** high-offset (≥200) suspects
(and existing denylist).
2. Re-apply with new applicator (must finish with QUALITY_FAIL=0 / abort).
3. Bacon that “clean-offset” set → LAB boot test (**fastboot flash boot** only;
recover with Stage1 boot if needed).
4. If still BAD → classic binary bisect on the **clean** list only.
5. Separately, quarantine high-offset patches for manual review / port
(do not bulk-apply with huge offsets).
## Flash rules (LAB)
1. sunfish recovery is in `boot` — bad kernel ⇒ Recovery looks like logo loop.
2. Unbrick: `fastboot -s 11231JEC207477 flash boot <GOOD boot.img>` → reboot.
**Always** pin LAB serial. Never flash “any” fastboot device (GOLDEN risk).
3. No reboot-loop automation. No unscoped recover watchers.
4. One reboot after a deliberate flash.
## Diagnostic limits (owner review 2026-09-30)
- A BAD half **reproduces** the hang; it does **not** prove a single CVE is the
root cause (interactions / order / mis-apply semantics still possible).
- Applicator had **0 FAIL** on the original full set; abort/offset gates were
still required, but they did **not** clear this logo hang.
- Bisect flashes are **`boot.img` only** on `boot_a` (serial-pinned). No slot /
download / flash miss seen on H1a2: hang tracks what H1a2 changes vs Stage1
base. **No** demonstrated need to re-sideload full zip or wipe data for this
bisect.
- “CRC wlan OK” so far = host-side `kmalloc_caches` only (kernel
`Module.symvers` vs pack `wlan.ko`). Does **not** check all symbols nor the
on-device installed module (adb pull denied without root). Narrows one
suspicion; does **not** rule out module ABI skew.
- Next interpretation: if H1a2a boots, still test H1a2b. If **both** boot alone
but H1a2 fails → interaction/dependency, not a single-patch proof.
- **Applicability (owner 2026-10-01):** AXP A15 success ≠ correct port to
LOS23/A16 sunfish. Per [kernel CVE process](https://cdn.kernel.org/doc/html/latest/process/cve.html),
each CVE must be judged for *this* tree/config/modules — not bulk-trusted.
H1a2b includes useful fixes (USB gadget UAF, QC audio DSP bounds, F2FS
state, QRTR) but also arch/drivers off in our defconfig (x86/AMD, s390,
CAN/ATA/SAS/kprobes/brcmfmac, …). These are mostly **bugfixes**, not
transversal hardening like FORTIFY/allocator. Concrete hypothesis (unproven):
`CVE-2022-49980` (`udc_lock` on gadget events) — check whether this tree
already holds that lock on the same paths (possible deadlock). Also re-audit
F2FS patches vs exact sunfish F2FS version/deps. **In H1a2b1b list**
(not H1a2b1a).
### Code review: CVE-2022-49980 vs msm-4.14 (2026-10-01)
Patch only wraps `udc->driver` read in `usb_udc_uevent()` with
`mutex_lock(&udc_lock)`. On **our** `msm-4.14` `udc/core.c` (base
`28f9290ae067`, pre-CVE apply), `kobject_uevent()` is still invoked **while
holding `udc_lock`**:
- `usb_gadget_remove_driver()` → `KOBJ_CHANGE` (called from
`usb_del_gadget_udc` / `usb_gadget_unregister_driver` under lock)
- `udc_bind_to_driver()` → `KOBJ_CHANGE` (called from
`usb_gadget_probe_driver` under lock)
`dev_uevent` / `class->dev_uevent` run **synchronously** inside
`kobject_uevent`. `udc_lock` is a non-recursive `mutex`. Applying 49980 alone
⇒ same thread: lock → uevent → `usb_udc_uevent` → lock again ⇒ **deadlock**.
This matches community reports that backporting CVE-2022-49980 to older trees
(e.g. 5.10.y) introduces deadlock; mainline later needed **lock-scope**
changes (unbind/bind outside `udc_lock`), not the one-liner alone.
sunfish_defconfig: `CONFIG_USB_GADGET=y`, rich `CONFIG_USB_CONFIGFS_*` (ADB /
MTP / etc.) — gadget bind/unbind at boot is plausible. Still **hypothesis**
until H1a2b1b (or a 49980-only build) is LAB-tested; do **not** ship 49980
without the companion lock-order fix (or omit it from the allowlist).
**2026-10-01 evening:** 49980-only LAB = BAD (logo). Deadlock paths on this
tree include both (a) `device_add` / uevent under `udc_lock` at UDC register
and (b) `kobject_uevent` from remove/bind while lock held. Serial console /
pstore stack not captured yet — would confirm hang site; denylist stands.
## GPS
FR-018: no location lockdown.
## Status
- [x] Full CVE = BAD (logo); documented do-not-reflash
- [x] Audit apply log (0 FAIL, many huge offsets)
- [x] Fix applicator abort + keep rejects + offset/fuzz gate
- [x] Stop premature H1 bacon (full-503 halves archived `*.full503.bak`)
- [x] Clean-offset allowlist bacon + LAB boot
Artifact `20260929T203844Z-axp-stage1-combo4-cve-clean` (430 patches,
QUALITY_FAIL=0) — **BAD** 2026-09-29T20:42Z: `fastboot flash boot`
OK on `boot_a`, then Google-logo hang / USB dark (~4+ min). Recover =
Stage1 `20260929T123401Z-axp-stage1-combo4` boot only.
- [x] Clean H1 (215) bacon + LAB boot — artifact
`20260929T213532Z-axp-stage1-combo4-cve-h1` — **BAD** 2026-09-30
(logo). H1 **reproduces**; H2 untested; not a single-CVE ID yet.
- [x] H1a (108) — `20260930T075006Z-…-cve-h1a` **BAD** (logo). H1a
**reproduces**; interaction possible; module coherence not yet checked
for CVE boots.
- [x] H1a1 (54) — `20260930T084048Z-…-cve-h1a1` **BOOT_OK** 2026-09-30T19:21Z
(kernel `#38`, slot `_a`). Build-tree `wlan`/`kmalloc_caches` CRC
`0xe7ccd971` matches Stage1 Gate G (device pull of `wlan.ko` denied
without root; intra-pack CRC OK). H1a1 set alone does **not** reproduce
the hang.
- [x] H1a2 (54) — `20260930T200752Z-…-cve-h1a2` **BAD** (owner: Google logo
after flash; reproduces). H1a1 GOOD + H1a2 BAD ⇒ hang associated with
H1a2 set (still not a single id; module CRC intra-pack was Stage1-class
on prior boots).
- [x] H1a2a (27) — `20260930T213106Z-…-cve-h1a2a` **BOOT_OK** 2026-09-30T21:36Z
(kernel `#40`). Host `kmalloc_caches` match. Alone does **not** reproduce.
- [x] H1a2b (27) — `20260930T222148Z-…-cve-h1a2b` **BAD** (logo alone).
H1a2a GOOD + H1a2b BAD ⇒ hang associated with H1a2b set (not proven
single patch). Note: owner phrasing “combi” = this half failed on LAB;
interaction check already satisfied by testing halves separately.
- [x] H1a2b1 (14) — `20260930T231324Z-…-cve-h1a2b1` **BAD** (logo). Includes
`CVE-2022-49980` (udc_lock hypothesis). Set of 14 reproduces.
- [x] H1a2b1a (7) — `20261001T001808Z-…-cve-h1a2b1a` **BOOT_OK** 2026-10-01
(kernel `#43`). No `CVE-2022-49980` in this half.
- [x] H1a2b1b (7, includes `CVE-2022-49980`) —
`20261001T010705Z-…-cve-h1a2b1b` **BAD** 2026-10-01 (logo). H1a2b1a
GOOD + H1a2b1b BAD ⇒ hang associated with this 7-set (49980 still
unproven alone).
- [x] **49980-only** — `20261001T160836Z-…-cve-49980-only` **BAD** 2026-10-01
(logo). Single-patch reproduce on LAB. Added to `cve-denylist.txt`.
Do not re-apply without lock-scope companion fix.
- [x] H1a2b1b **without** 49980 (6) —
`20261001T170442Z-…-cve-h1a2b1b-no49980` **BOOT_OK** 2026-10-01
(kernel `#46`, LAB `11231JEC207477`). Confirms 49980 was the **sole**
hang trigger in the H1a2b→…→H1a2b1b BAD lineage (other six alone OK).
- [x] Clean-offset **minus 49980** (429) —
`20261001T180955Z-axp-stage1-combo4-cve-clean` **BOOT_OK** 2026-10-01
(kernel `#47`). List md5 `e05056f6…`; apply OK=429. Prior BAD 430 pack
`20260929T203844Z-…` kept. **49980 was the only logo hang** found in the
clean-offset set so far. 15m poll loop stopped after flash.
@@ -0,0 +1,83 @@
# high-offset triage 2026-10-01T18:18:58+00:00
# list=/srv/rom/src/lineage/vendor/sunfish-a16/patches/009-kernel-hardening/cve-high-offset-ge200.txt count=73
mmfs, CVE-2020-29372/^5.7/0001.patch mm: check that mm is still valid in madvise() mm/madvise.c,
other, CVE-2021-47209/^5.15/0001.patch sched/fair: Prevent dead task groups from regaining cfs_rq's kernel/sched/autogroup.c,kernel/sched/core.c,kernel/sched/fair.c,kernel/sched/rt.c,kernel/sched/sched.h,
net, CVE-2021-47299/^5.13/0001.patch xdp, net: Fix use-after-free in bpf_xdp_link_release net/core/dev.c,
offarch, CVE-2022-48825/^5.17/0001.patch scsi: qedf: Add stag_work to all the vports drivers/scsi/qedf/qedf_main.c,
net, CVE-2022-48969/^6.1/0001.patch xen-netfront: Fix NULL sring after live migration drivers/net/xen-netfront.c,
net, CVE-2022-49086/ANY/0001.patch net: openvswitch: fix leak of nested actions net/openvswitch/flow_netlink.c,
net, CVE-2022-49341/ANY/0001.patch bpf, arm64: Clear prog->jited_len along prog->jited arch/arm64/net/bpf_jit_comp.c,
mmfs, CVE-2022-49412/ANY/0001.patch bfq: Avoid merging queues with different parents block/bfq-iosched.c,
other, CVE-2022-49641/ANY/0001.patch sysctl: Fix data races in proc_douintvec(). kernel/sysctl.c,
net, CVE-2022-49687/ANY/0001.patch virtio_net: fix xdp_rxq_info bug after suspend/resume drivers/net/virtio_net.c,
net, CVE-2022-49695/ANY/0001.patch igb: fix a use-after-free issue in igb_clean_tx_ring drivers/net/ethernet/intel/igb/igb_main.c,
other, CVE-2022-49715/ANY/0001.patch irqchip/gic-v3: Fix refcount leak in gic_populate_ppi_partitions drivers/irqchip/irq-gic-v3.c,
usb, CVE-2022-50246/ANY/0001.patch usb: typec: tcpci: fix of node refcount leak in tcpci_register_port() drivers/usb/typec/tcpm/tcpci.c,
other, CVE-2022-50304/ANY/0001.patch mtd: core: fix possible resource leak in init_mtd() drivers/mtd/mtdcore.c,
net, CVE-2023-3777/^6.5/0001.patch netfilter: nf_tables: skip bound chain on rule flush net/netfilter/nf_tables_api.c,
other, CVE-2023-52486/^6.7/0001.patch drm: Don't unref the same fb many times by mistake due to deadlock drivers/gpu/drm/drm_plane.c,
other, CVE-2023-52676/^6.7/0001.patch bpf: Guard stack limits against 32bit overflow kernel/bpf/verifier.c,
other, CVE-2023-52707/^6.2/0001.patch sched/psi: Fix use-after-free in ep_remove_wait_queue() kernel/sched/psi.c,
other, CVE-2023-52847/^6.6/0001.patch media: bttv: fix use after free error due to btv->timeout timer drivers/media/pci/bt8xx/bttv-driver.c,
other, CVE-2023-53594/ANY/0001.patch driver core: fix resource leak in device_add() drivers/base/core.c,
net, CVE-2023-53669/ANY/0001.patch tcp: fix skb_copy_ubufs() vs BIG TCP net/core/skbuff.c,
other, CVE-2023-53709/ANY/0001.patch ring-buffer: Handle race between rb_move_tail and rb_check_pages kernel/trace/ring_buffer.c,
offarch, CVE-2024-2201/^6.9/0001.patch x86/bugs: Change commas to semicolons in 'spectre_v2' sysfs file arch/x86/kernel/cpu/bugs.c,
offarch, CVE-2024-24855/^6.4/0001.patch scsi: lpfc: Fix a possible data race in lpfc_unregister_fcf_rescan() drivers/scsi/lpfc/lpfc_hbadisc.c,
net, CVE-2024-26643/^6.8/0001.patch netfilter: nf_tables: mark set as dead when unbinding anonymous set net/netfilter/nf_tables_api.c,
other, CVE-2024-26900/^6.8/0001.patch md: fix kmemleak of rdev->serial drivers/md/md.c,
net, CVE-2024-39296/^6.9/0001.patch bonding: fix oops during rmmod drivers/net/bonding/bond_main.c,
mmfs, CVE-2024-40917/^6.10/0001.patch memblock: make memblock_set_node() also warn about use of mm/memblock.c,
net, CVE-2024-40923/^6.10/0001.patch vmxnet3: disable rx data ring on dma allocation failure drivers/net/vmxnet3/vmxnet3_drv.c,
net, CVE-2024-40954/^6.10/0001.patch net: do not leave a dangling sk pointer, when socket creation fails net/core/sock.c,
mmfs, CVE-2024-41014/^6.10/0001.patch xfs: add bounds checking to xlog_recover_process_data fs/xfs/xfs_log_recover.c,
net, CVE-2024-43879/^6.10/0001.patch wifi: cfg80211: handle 2x996 RU allocation in net/wireless/util.c,
net, CVE-2024-46754/^6.10/0001.patch bpf: Remove tst_run from lwt_seg6local_prog_ops. net/core/filter.c,
offarch, CVE-2024-46830/^6.11/0001.patch KVM: x86: Acquire kvm->srcu when handling KVM_SET_VCPU_EVENTS arch/x86/kvm/x86.c,
net, CVE-2024-50186/^6.12/0001.patch net: explicitly clear the sk pointer, when pf->create fails net/socket.c,
mmfs, CVE-2024-50273/^6.11/0001.patch btrfs: reinitialize delayed ref list after deleting it from the list fs/btrfs/delayed-ref.c,
mmfs, CVE-2024-53066/^6.12/0001.patch nfs: Fix KMSAN warning in decode_getfattr_attrs() fs/nfs/inode.c,
mmfs, CVE-2024-53173/ANY/0002.patch NFSv4.0: Fix a use-after-free problem in the asynchronous open() fs/nfs/nfs4proc.c,
mmfs, CVE-2024-53217/ANY/0002.patch NFSD: Prevent NULL dereference in nfsd4_process_cb_update() fs/nfsd/nfs4callback.c,
net, CVE-2024-53240/ANY/0001.patch xen/netfront: fix crash when removing device drivers/net/xen-netfront.c,
mmfs, CVE-2024-56558/ANY/0001.patch nfsd: make sure exp active before svc_export_show fs/nfsd/export.c,
other, CVE-2024-56569/ANY/0001.patch ftrace: Fix regression with module command in stack_trace_filter kernel/trace/ftrace.c,
mmfs, CVE-2024-56596/ANY/0002.patch jfs: fix array-index-out-of-bounds in jfs_readdir fs/jfs/jfs_dtree.c,
mmfs, CVE-2024-56598/ANY/0002.patch jfs: array-index-out-of-bounds fix in dtReadFirst fs/jfs/jfs_dtree.c,
net, CVE-2024-56605/ANY/0002.patch Bluetooth: L2CAP: do not leave dangling sk pointer on error in net/bluetooth/l2cap_sock.c,
mmfs, CVE-2024-56779/ANY/0002.patch nfsd: fix nfs4_openowner leak when concurrent nfsd4_open occur fs/nfsd/nfs4state.c,
offarch, CVE-2024-57849/ANY/0002.patch s390/cpum_sf: Handle CPU hotplug remove during sampling arch/s390/kernel/perf_cpum_sf.c,
net, CVE-2024-58009/ANY/0001.patch Bluetooth: L2CAP: handle NULL sock pointer in l2cap_sock_alloc net/bluetooth/l2cap_sock.c,
other, CVE-2024-58083/ANY/0001.patch KVM: Explicitly verify target vCPU is online in kvm_get_vcpu() include/linux/kvm_host.h,
mmfs, CVE-2025-21671/ANY/0001.patch zram: fix potential UAF of zram table drivers/block/zram/zram_drv.c,
net, CVE-2025-21909/ANY/0001.patch wifi: nl80211: reject cooked mode if it is set along with other flags net/wireless/nl80211.c,
net, CVE-2025-37789/ANY/0001.patch net: openvswitch: fix nested key length validation in the set() net/openvswitch/flow_netlink.c,
net, CVE-2025-37795/ANY/0001.patch wifi: mac80211: Update skb's control block key in net/mac80211/tx.c,
mmfs, CVE-2025-37839/ANY/0001.patch jbd2: remove wrong sb->s_sequence check fs/jbd2/journal.c,
other, CVE-2025-37923/ANY/0001.patch tracing: Fix oob write in trace_seq_to_buffer() kernel/trace/trace.c,
net, CVE-2025-37932/ANY/0001.patch sch_htb: make htb_qlen_notify() idempotent net/sched/sch_htb.c,
other, CVE-2025-38034/ANY/0001.patch btrfs: correct the order of prelim_ref arguments in btrfs__prelim_ref include/trace/events/btrfs.h,
other, CVE-2025-38044/ANY/0001.patch media: cx231xx: set device_caps for 417 drivers/media/usb/cx231xx/cx231xx-417.c,
net, CVE-2025-38200/ANY/0001.patch i40e: fix MMIO write access to an invalid page in i40e_clear_hw drivers/net/ethernet/intel/i40e/i40e_common.c,
mmfs, CVE-2025-38204/ANY/0001.patch jfs: fix array-index-out-of-bounds read in add_missing_indices fs/jfs/jfs_dtree.c,
mmfs, CVE-2025-38218/ANY/0001.patch f2fs: fix to do sanity check on sit_bitmap_size fs/f2fs/super.c,
other, CVE-2025-38285/ANY/0001.patch bpf: Fix WARN() in get_bpf_raw_tp_regs kernel/trace/bpf_trace.c,
net, CVE-2025-38473/ANY/0001.patch Bluetooth: Fix null-ptr-deref in l2cap_sock_resume_cb() net/bluetooth/l2cap_sock.c,
other, CVE-2025-38495/ANY/0001.patch HID: core: ensure the allocated report buffer can contain the drivers/hid/hid-core.c,
mmfs, CVE-2025-38498/ANY/0002.patch do_change_type(): refuse to operate on unmounted/not ours mounts fs/namespace.c,
other, CVE-2025-39742/ANY/0001.patch RDMA: hfi1: fix possible divide-by-zero in find_hw_thread_mask() drivers/infiniband/hw/hfi1/affinity.c,
audio, CVE-2025-39751/ANY/0001.patch ALSA: hda/ca0132: Fix buffer overflow in add_tuning_control sound/pci/hda/patch_ca0132.c,
other, CVE-2025-39824/ANY/0001.patch HID: asus: fix UAF via HID_CLAIMED_INPUT validation drivers/hid/hid-asus.c,
net, CVE-2025-39847/ANY/0001.patch ppp: fix memory leak in pad_compress_skb drivers/net/ppp/ppp_generic.c,
net, CVE-2025-39874/ANY/0001.patch macsec: sync features on RTM_NEWLINK drivers/net/macsec.c,
other, CVE-2025-39998/ANY/0001.patch scsi: target: target_core_configfs: Add length check to avoid buffer drivers/target/target_core_configfs.c,
net, CVE-2025-40078/ANY/0001.patch bpf: Explicitly check accesses to bpf_sock_addr net/core/filter.c,
mmfs, CVE-2025-40080/ANY/0001.patch nbd: restrict sockets to TCP and UDP drivers/block/nbd.c,
--- counts ---
27 net,
21 other,
18 mmfs,
5 offarch,
1 usb,
1 audio,
1 --- counts ---
+123
View File
@@ -0,0 +1,123 @@
# FORTIFY: auditoría del build 20260926T183144Z
Revisión de solo lectura en rom-dev01. Sin compilaciones ni flasheos nuevos.
El dueño confirma arranque de ksync-slab y fallo del último FORTIFY.
## Artefactos y empaquetado
Raíz remota: `/srv/rom/artifacts/009-axp-los23-port/`.
- Referencia: `20260926T171215Z-bisect-ksync-slab-only`.
- Fallido: `20260926T183144Z-bisect-ksync-fortify-only/boot-make.img`.
- SHA-256 registrado del fallido:
`89734e6a34f99bfdc2d334e06e1ff49da46dac311139b7dc54f25ae7af0f0641`.
- Ambos ramdisks desempaquetados tienen SHA-256
`c9ebabdcec875c896530214c5f2988e30a6bc37754855e4cba4863692d7db519`.
- Ambos DTB tienen SHA-256
`2fca15709ff6d9f48fea938952439e8d00c2b998c73e7a216172ae3965b07566`.
- El header del FORTIFY conserva Android 16 y parche `2026-09`.
- El script usa breakfast y exige TARGET_RELEASE=bp4a.
El problema anterior de cambiar la release/ramdisk no aparece en esta comparación.
La config embebida del fallido tiene FORTIFY=y, SLAB_FREELIST_HARDENED=n;
la referencia slab tiene las dos opciones al revés. Ambos conservan LTO y CFI.
## Hallazgo principal: la exclusión de Clang es ineficaz
En `/srv/rom/src/lineage/kernel/google/msm-4.14/security/Kconfig:190-195`:
```text
config FORTIFY_SOURCE
bool "Harden common str/mem functions against buffer overflows"
depends on ARCH_HAS_FORTIFY_SOURCE
# https://bugs.llvm.org/show_bug.cgi?id=50322
# https://bugs.llvm.org/show_bug.cgi?id=41459
depends on !CC_IS_CLANG
```
`git grep` no encuentra una definición de `CC_IS_CLANG` en ese árbol.
La config generada tampoco contiene CONFIG_CC_IS_CLANG. La restricción usa
un símbolo no definido y por ello no detecta el compilador real.
`obj/KERNEL_OBJ/include/generated/compile.h` identifica **Clang 21.0.0,
r563880c, LLD 21**, no GCC. El commit que introdujo la restricción es
`eb4c4383950d`, «fortify: Explicitly disable Clang support».
Esto demuestra un defecto de integración/configuración. No demuestra por sí
solo la instrucción concreta que provoca el fallo de arranque. Un Clang más
nuevo no sustituye automáticamente las adaptaciones del código FORTIFY.
Referencia primaria de las adaptaciones upstream: Kees Cook,
[fortify: Add Clang support, v7 8/8](https://lists.openwall.net/linux-kernel/2022/02/08/1547).
La serie adapta wrappers y atributos de tamaño de objeto; no consiste en
añadir cuerpos BUG a símbolos sin resolver. El parche AXP
`0008-Graphene-Kernel_Hardening-fortify/4.14/0004.patch`, inspeccionado, añade
comprobaciones intra-object; no aporta por sí solo ese soporte Clang.
## Segundo defecto: el error de enlace se convirtió en código ejecutable
Primer intento, log remoto
`/srv/rom/artifacts/detach-009-bisect-ksync-fortify-20260926T172516Z.log`:
```text
ERROR: "__read_overflow2" [techpack/audio/dsp/q6_dlkm.lto.ko] undefined!
ERROR: "__read_overflow" [drivers/staging/qcacld-3.0/wlan.lto.ko] undefined!
```
El script `scripts/009-bisect-ksync-one.sh` añade después a `lib/string.c`
cuatro funciones exportadas (`__read_overflow`, `__read_overflow2`,
`__read_overflow3`, `__write_overflow`) con cuerpo `BUG()`.
Así consigue enlazar, pero no resuelve por qué el compilador emite llamadas
a esos helpers, declarados para diagnóstico en `include/linux/string.h:241`.
El objeto generado `techpack/audio/dsp/q6_dlkm.ko`, desensamblado, contiene:
```text
rtac_remove_voice + 0x1d0, offset 0x673ec:
R_AARCH64_CALL26 __read_overflow2
```
El código asociado incluye la copia de elementos en `shift_voice_devices`
(`techpack/audio/dsp/rtac.c:638-644`). Falta resolver si el diagnóstico indica
un acceso real fuera de límites o un problema de los wrappers/compilador.
No convertirlo en un no-op ni aceptar BUG como solución.
**Límite importante:** el ensayo despliega solo boot y conserva los módulos
vendor previos. Esta llamada existe en el módulo recién compilado, pero no
se ha demostrado que ese módulo se cargue en LAB. Tampoco se encontraron
llamadas directas desde funciones ordinarias de vmlinux a los cuatro stubs
en la búsqueda realizada (sí sus entradas CFI y sus bucles internos).
Por tanto, no se atribuye el fallo de arranque específicamente a ese stub.
Las llamadas normales a `fortify_panic` son parte de FORTIFY y su presencia
no prueba que ninguna se ejecute durante el arranque fallido.
## Siguiente paso justificado
1. Conservar ksync-slab como referencia que arranca. No encadenar más opciones
sobre el build FORTIFY fallido.
2. Tratar FORTIFY como un port con dependencias: estudiar la implementación
AXP y adaptar soporte Clang al kernel 4.14, incluyendo detección de compilador
y pruebas de los wrappers. Corregir la detección sola bloquearía esta
opción; no equivaldría a implementar FORTIFY.
3. Retirar los stubs BUG de la receta candidata y resolver los diagnósticos
de audio/WLAN, validando la causa de cada llamada. Mantener LTO/CFI como
referencia; desactivarlos globalmente introduce otra variable y pierde
protecciones ya funcionales.
4. Archivar .config, diff, vmlinux y Module.symvers por build. Comprobar
compatibilidad con los módulos realmente desplegados, no solo uname.
5. Obtener una traza del intento fallido que distinga FORTIFY, CFI, carga de
módulos y montaje de datos. En esta revisión ADB no detecta teléfonos,
por lo que no se han leído registros actuales de LAB.
Conclusión: hay un defecto concreto de habilitación y un arreglo de enlace
inadecuado. La causa exacta en ejecución sigue pendiente; no anunciar otro
boot como reparado únicamente porque vuelva a compilar.
Plan de port (siguiente documento): `docs/009-fortify-clang-port.md`.
## Logs LAB post-fallo (2026-09-26, Cursor)
Teléfono en fastboot tras FORTIFY. Reflasheo slab-only (`boot_a`/`boot_b`) y reboot.
`/sys/fs/pstore/` vacío (0 ficheros); sin `console-ramoops` ni `last_kmsg`.
Misma ausencia de traza que en hangs Stage1/CVE previos. No hay dmesg del intento fallido.
Artefacto local vacío: `.local-artifacts/009-fortify-crash-20260926/`.
@@ -0,0 +1,87 @@
# FORTIFY bounds: enlace correcto, arranque fallido
Artefacto remoto: `20260926T223246Z-bisect-ksync-fortify-bounds` bajo
`/srv/rom/artifacts/009-axp-los23-port/`.
El dueño informa logo Google, reinicios y finalmente fastboot. El proceso
de build terminó exit=0 a las 22:32:52 UTC del 26 de septiembre.
## Verificaciones realizadas
- Los dos parches de límites están en las fuentes actuales. Audio/WLAN
ya no tienen los símbolos sin resolver `__read_overflow*` en sus objetos
LTO. Sí usan `fortify_panic`, que el kernel define y exporta normalmente.
- `lib/string.c` no tiene modificaciones/stubs; `vmlinux` no contiene los
cuatro helpers artificiales de overflow de los intentos anteriores.
- El boot descargado tiene SHA-256
`ba5ac99eb2873dac4c3c8b108753bb89e5aa7a7d71972361e32b39f6ed9ef136`,
igual al registro remoto.
- Desempaquetado local correcto: Android 16, parche 2026-09, header v2.
El kernel extraído tiene SHA-256
`99348e4f21a46c3bf95dde3a97995cd97375a6792427c5d497b3296fa0c0a906`,
idéntico al kernel nuevo y al kernel verificado en el servidor.
- Los ramdisks en los directorios remotos work/check del fallido y combo4
tienen el mismo SHA-256. Se conserva el método ksync.
## Error ignorado por el empaquetador
El log muestra que el último `magiskboot unpack` (línea 91 del script)
terminó con `Illegal instruction (core dumped)`. El `|| true` permitió
continuar y declarar DONE. Es un defecto de verificación que debe corregirse.
No demuestra que la imagen sea inválida: la copia con el mismo hash se ha
desempaquetado correctamente en el portátil y contiene el kernel esperado.
No confundir el fallo de la herramienta host con una traza del teléfono.
## Estado de LAB durante esta revisión
ADB disponible como usuario shell, sin privilegios root. LAB ya ejecuta
combo4, kernel #16, `sys.boot_completed=1`. Pstore vacío. Propiedades de
motivo de arranque: ro.boot.bootreason=reboot, sys.boot.reason=bootloader.
No identifican el fallo previo. El tramo de logcat consultado no contiene
una traza FORTIFY/CFI del intento fallido.
Intentos de leer módulos instalados en vendor devolvieron Permission denied.
Por tanto, no se ha podido comparar su ABI/CRC con el kernel fallido.
No se ha ejecutado adb root, cambiado Magisk, reiniciado ni flasheado LAB.
## Prueba adicional de eficacia de wrappers
Se extrajo el bloque FORTIFY del string.h actual a un harness temporal,
con sus wrappers y macros, tipos auxiliares y libc host. Clang r563880c,
`-O2 -flto -fuse-ld=lld`. No se añadieron stubs overflow. El handler de
fortify del harness termina con código 77 para observar el rechazo.
Directorio remoto: `/tmp/axp-fortify-runtime-9t4hrer_`.
13 casos dieron el resultado esperado:
- strlen/strnlen sobre una cadena válida, strcpy válido;
- memcpy, memmove, memset, memcmp y strncpy: tamaño 8 sobre buffers de 8
aceptado, tamaño dinámico 9 rechazado por fortify_panic.
**Límites:** ejecución host x86_64, no kernel ARM64; no prueba CFI del kernel,
wrappers restantes, todos los call sites, módulos ni arranque. El caso memcmp
usa el mismo buffer a ambos lados; no es una prueba exhaustiva de comparación.
Estas pruebas muestran protección efectiva en los casos probados, no que el
port completo sea correcto.
## Lo que falta para atribuir el fallo
La corrección de límites resolvió el enlace, que era su alcance probado.
No se ha identificado la instrucción que detiene el arranque. Encontrar
llamadas a fortify_panic en el binario (incluso en funciones de inicio) es
normal y no demuestra que se ejecuten durante el fallo.
Prioridad: preparar una captura de fallo utilizable antes del próximo ensayo.
Comprobar en la referencia funcional el acceso y funcionamiento de pstore/
ramoops, incluido el mecanismo del dispositivo que procesa sus registros.
Un arranque de diagnóstico puede instrumentar fortify_panic para registrar
caller/backtrace conservando su comportamiento de fallo; también debe poder
distinguir una caída CFI, un watchdog y un fallo de módulos. Esa instrumentación
sin un canal persistente/serial verificado no garantiza obtener evidencia.
Conservar combo4 como referencia aceptada. No habilitar FORTIFY para ship,
ni atribuir el fallo a un controlador concreto, ni reintroducir stubs/no-ops
a partir del solo síntoma del logo. Revisar después compatibilidad de módulos
y los call sites identificados por una traza real.
Plan de captura (siguiente documento): `docs/009-fortify-diag-capture-plan.md`.
+98
View File
@@ -0,0 +1,98 @@
# FORTIFY + Clang en msm-4.14 (plan de port)
Documento de preparación. No es un parche listo. Relacionado:
`docs/009-fortify-boot-audit-20260926.md` (diagnóstico del fallo LAB).
## Hechos ya cerrados
| Hecho | Evidencia |
|-------|-----------|
| Packaging ksync OK | Ramdisk/DTB idénticos a slab; patch `2026-09` |
| FORTIFY one-CONFIG tumba arranque | LAB → reinicios → fastboot; pstore vacío |
| DMESG / SLAB / INIT_ON_ALLOC / INIT_ON_FREE solos OK | Bisect ksync 2026-09-26 |
| `depends on !CC_IS_CLANG` es inútil aquí | `CC_IS_CLANG` **no existe** en msm-4.14; el build usa Clang 21 + LTO/CFI |
| Stubs `__read_overflow*` + `BUG()` | Anti-patrón: enlazan pero dejan llamadas reales (p.ej. `q6_dlkm` → `__read_overflow2`). **No reutilizar** |
| AXP `fortify/4.14/0004.patch` | Solo intra-object (`FORTIFY_SOURCE_STRICT_STRING` / Micay). **No** aporta soporte Clang |
## Qué hace falta (upstream, no inventado)
Serie Kees Cook *fortify: Add Clang support* (v7, 2022-02), mergeada en kernels
modernos. Idea central:
1. Atributos Clang: `__pass_object_size`, `__overloadable`, `__diagnose_as`
(en `compiler_attributes.h`).
2. Wrappers FORTIFY adaptados para que `__builtin_object_size(p, 1)` funcione
desde inlines (en kernels nuevos: `include/linux/fortify-string.h`).
3. Kconfig: `depends on !CC_IS_CLANG || CLANG_VERSION >= 120001` (y exclusiones
arch específicas). Clang 21 cumple la versión; falta el código.
Referencias:
- Cover: <https://lists.openwall.net/linux-hardening/2022/02/08/7>
- Commit ilustrativo: `281d0c962752` (*fortify: Add Clang support*)
- Antecedente del disable: `a52f8a59aef4` / bugs LLVM 41459, 50322
En **4.14** el FORTIFY vive todavía dentro de `include/linux/string.h` (no hay
`fortify-string.h`). El backport no es un cherry-pick limpio: hay que adaptar
los wrappers al layout 4.14 o traer la separación de headers con cuidado.
## Detección de compilador (paso previo pequeño)
Sin `CC_IS_CLANG` / `CLANG_VERSION` en Kconfig, cualquier `depends on !CC_IS_CLANG`
es no-op. Antes o junto al port de wrappers:
1. Backport mínimo de los símbolos Kconfig que usan árboles Android/comunes
modernos (`CC_IS_CLANG`, `CLANG_VERSION`) **o** equivalencia documentada
vía `scripts/Kconfig.include` / Makefile del árbol.
2. Con eso solo + `depends on !CC_IS_CLANG`: FORTIFY queda **apagado** con
Clang — correcto, pero no da hardening. Sirve como red de seguridad para
no volver a activarlo “por accidente”.
## Camino de implementación propuesto (orden)
1. **Quitar stubs** de `009-bisect-ksync-one.sh` (o dejarlos detrás de un
flag explícito `ALLOW_FORTIFY_BUG_STUBS=1` no usado por defecto). Un
FORTIFY que no enlace sin stubs = señal de que el port está incompleto.
2. **Introducir detección Clang** en Kconfig (paso pequeño, testeable).
3. **Port wrappers Clang** al `string.h` 4.14 (o split a `fortify-string.h`
si el diff queda más limpio), tomando la serie Kees como fuente, no stubs.
4. **Build ksync** solo `CONFIG_FORTIFY_SOURCE=y` (sin STRICT_STRING al
principio). Criterio de éxito: modpost **sin** undefined
`__read_overflow*` y sin exportar cuerpos `BUG()`.
5. Si aparecen llamadas residuales en audio/WLAN: tratar cada una como bug
de tamaño/objeto o fallo del wrapper — no enmascarar.
6. Flash LAB (mismo método ksync / GOODMAKE). Solo entonces valorar
`FORTIFY_SOURCE_STRICT_STRING` (AXP 0004) encima.
7. Reintroducir FORTIFY en el combo defconfig OK (DMESG+SLAB+INIT_ON_*).
Fuera de alcance de este port: desactivar LTO/CFI globales “para que compile”
(introduce otra variable y pierde protecciones ya validadas).
## Qué no es “clonar AXP” aquí
AXP aporta el parche Graphene de **strict string** sobre un FORTIFY que ya
debe ser válido con el toolchain del producto. En LOS23/sunfish el toolchain
es Clang+LTO; el vacío es el **soporte Clang de FORTIFY**, no el 0004.
Hasta tener (3)+(4), no marcar FR-017 FORTIFY como listo.
Nota (búsqueda host): el `0008-…-fortify/5.15/0006.patch` AXP (*rewrite
fortify source for 5.15 gki*) también es strict-string sobre
`fortify-string.h` ya existente — no sustituye la serie Kees de Clang.
## Estado
- **LAB combo4 OK** (2026-09-26): `#16`, DMESG+SLAB+INIT_ON_ALLOC+INIT_ON_FREE, sin FORTIFY.
- Stubs BUG gated off en ksync (`ALLOW_FORTIFY_BUG_STUBS`).
- Port en curso: `vendor/.../fortify-clang/apply_fortify_clang.py`.
- **fortify-pos0 FAIL 2026-09-26T21:54Z:** modpost
`__read_overflow*` undefined en `q6_dlkm.lto.ko` / `wlan.lto.ko`.
**vmlinux/Image.lz4 sí generaron** (sin esos U en el núcleo). El port POS0
no basta aún para módulos LTO; no reintroducir stubs.
Siguiente: localizar call sites (p.ej. rtac) o `__NO_FORTIFY` puntual /
más del port Kees — no ACCEPT hasta harness + link limpio.
- **Ensayo 2026-09-26T22:06Z:** POS0 + bounds (`rtac`/`lim_process_fils`) en
job `009-bisect-ksync-fortify-bounds`. Log:
`detach-009-bisect-ksync-fortify-bounds-20260926T220634Z.log`.
**Build OK / LAB FAIL:** logo Google → reboot. Reflasheo a combo4.
Bounds resolvieron el link; no bastan para arrancar. FORTIFY sigue
aparcado para ship hasta traza o port más completo + harness.
+240
View File
@@ -0,0 +1,240 @@
# Plan: captura verificable del fallo FORTIFY
> Revisión posterior: [auditoría del diagnóstico del 27/09](009-fortify-diag-review-20260927.md).
> El «canal VERIFICADO» de abajo solo demuestra parte de la persistencia:
> SysRq-x puede dejar la misma marca que se observa tras SysRq-c. Además, el
> escritor no funciona antes de ramoops_probe. La ausencia de AXPDIAG1 tras
> fortify-diag **no descarta fortify_panic**, ni demuestra watchdog. Se conserva
> abajo el historial original de las pruebas.
Parte de `docs/009-fortify-bounds-boot-failure-20260927.md`.
Referencia LAB: combo4 `#16` (sin FORTIFY). No flashear diagnóstico hasta
tener canal de captura comprobado en esa referencia.
## Hallazgos en combo4 (2026-09-27, solo lectura)
| Señal | Valor |
|-------|--------|
| boot_completed | 1 |
| pstore montado | sí, vacío |
| last_kmsg / debugfs | no accesibles como shell |
| Magisk / su | no activos (virgin AXP) |
| cmdline | `console=null`, `androidboot.ramdump=disabled` |
| config | `CONFIG_PSTORE=y`, `PSTORE_RAM`, `PSTORE_CONSOLE`, `ACCESS_RAMOOPS` |
| bootreason tras recover | `reboot` / `bootloader` (no identifica el panic FORTIFY) |
Conclusión: el kernel **tiene** pstore/ramoops, pero LAB no entrega traza
útil tras el reboot a fastboot. Cambiar más CONFIGs sin canal de evidencia
repite el bucle anterior.
## Objetivo del arranque de diagnóstico
Distinguir, con registro persistente o serial:
1. `fortify_panic` (caller + backtrace)
2. fallo CFI
3. watchdog / softlockup
4. fallo de carga de módulos / ABI
Sin eso, no atribuir el logo Google a audio/WLAN ni a FORTIFY genérico.
## Pasos (orden)
1. **Canal de captura en combo4 (sin FORTIFY)**
- Confirmar cómo Pixel 4a/sunfish expone ramoops tras panic
(`ACCESS_RAMOOPS`, partición `klog`/`logfs`, `ramdump=disabled`).
- Probar un panic controlado mínimo en un boot **solo diagnóstico**
(no ship) y verificar que shell/root puede leer
`/sys/fs/pstore/console-ramoops*` tras el siguiente arranque bueno.
- Alternativa: UART/serial o `androidboot.console` no-null si el lab lo
permite. Si el canal falla, no flashear FORTIFY otra vez.
2. **Instrumentar `fortify_panic`** (solo en árbol de ensayo)
- Antes del `BUG()`: `pr_emerg` con `%pS` del return address + stack.
- Mantener el panic (no convertir en no-op).
- Misma receta ksync (GOODMAKE + kernel FORTIFY+POS0+bounds).
3. **Marcas CFI / módulos**
- Si el log muestra CFI: seguir esa vía.
- Si aparece durante `insmod`/init de vendor: comparar CRC/ABI de
módulos (requiere root o dump desde recovery).
- Si watchdog sin fortify/CFI: otra clase de hang.
4. **Empaquetador**
- Quitar el `|| true` que traga el SIGILL de `magiskboot` en VERIFY, o
verificar el header en el laptop con el mismo SHA. Defecto de script,
no causa del reboot (imagen unpack OK en laptop).
5. **Solo después** de una traza atribuible: cambio mínimo (call site,
excepción justificada, o más port). No stubs. No ship FORTIFY.
## Estado 2026-09-27 (tras Rooted debugging)
- `adb root` OK.
- `vendor.ramoops.decryption.error=1` en boot normal.
- Causa 1: `/vendor/bin/ramoops` hardcodea
`/sys/devices/soc/.../size` pero el kernel expone
`/sys/devices/platform/soc/...` (y vía `/sys/class/misc/.../size`).
Binario parcheado en `/data/local/tmp/ramoops.fix` para class paths.
- Causa 2: tras eso, kernel log `ramoops: Failed to set key, -22` (EINVAL)
al escribir AES en sysfs. pstore sigue vacío. Remount pstore denegado por SELinux.
- Claves RSA sí existen en `/data/vendor_ce/0/ramoops/{priv,pub}.pem`.
- `boot_debug/crash.txt` es basura userspace antigua, no kernel FORTIFY.
- Build `fortify-diag` (panic instrumentado) sigue en curso.
## Captura post fortify-diag (2026-09-27 ~01:36)
Tras flash diag → fallo → recover combo4 `#16` + `adb root`:
- pstore vacío; `boot_debug/kernel.txt` vacío; sin `FORTIFY_DIAG` en claro.
- Dump `/dev/access-ramoops` (4 MiB): entropía alta (cifrado); 0 strings útiles.
- Decrypt sigue fallando (`Failed to set key, -22`).
Prueba de canal con **SysRq-c** en combo4:
- Bootloader registró `androidboot.bootreason=kernel_panic` (panic real).
- **Aun así** pstore vacío y alt cifrado sin plaintext.
Conclusión: en este LAB un `kernel_panic` **no** produce traza legible vía pstore
hasta arreglar el pipeline AES/ramoops. El síntoma FORTIFY (antes del SysRq)
había dejado `bootreason=reboot`/`bootloader`, no `kernel_panic` — hipótesis
abierta: reset/wdog distinto de `fortify_panic`, o el bootloader reescribe el
motivo al pasar por fastboot.
Siguiente trabajo de captura: diag **sin cifrado** (pstore/ramoops plaintext)
o arreglo del setkey; no más flashes FORTIFY hasta tener canal verificado con
SysRq → fichero legible en pstore.
## Captura plaintext (2026-09-27 ~01:45)
Hallazgos en combo4 tras Rooted debugging + reboot sin unlock:
| Señal | Valor |
|-------|--------|
| `dumpsys user` | `RUNNING_LOCKED`, Unlock time unknown |
| `sys.user.0.ce_available` | vacío |
| `/data/vendor_ce/0/ramoops` | `Required key not available` |
| `mount \| grep pstore` | vacío tras `on boot` umount |
| Remount manual `pstore` + `use_alt=0` | monta OK, **sigue sin ficheros** |
| `/dev/pmsg0` + reboot | no aparece `pmsg-ramoops-*` |
| SysRq-c | `bootreason=kernel_panic`, alt cifrado, primary vacío |
Causa de sistema (no solo FORTIFY):
1. `init-ramoops.rc` hace `umount /sys/fs/pstore` en `on boot` y solo remonta
cuando `vendor.ramoops.decrypted=true` (tras CE unlock + `ramoops -D`).
2. Binario `/vendor/bin/ramoops` usa paths sysfs pre-`platform/` → size=-1;
con binario parcheado a class paths, CE locked → sin `priv.pem`.
3. Writes de pstore van al **primary**; el carveout que sobrevive/lee userspace
tras decrypt es el **alt** cifrado (`/dev/access-ramoops`).
4. `ramoops_init_prz` hace `persistent_ram_zap(primary)` tras `save_old` en
console/pmsg — el pipeline asume encrypt-on-shutdown a alt.
Canal de evidencia (sin CE / sin AES): patch diag
`ramoops_axp_plaintext_alt` — en DMESG oops/panic escribe magic `AXPDIAG1` +
cuerpo en claro al **alt** (ioremap). Verificación LAB:
```text
dd if=/dev/access-ramoops bs=1M count=4 | strings | grep -E 'AXPDIAG1|Kernel panic'
```
Build canal (sin FORTIFY): `scripts/009-bisect-ksync-pstore-alt-plain.sh`
(`PSTORE_ALT_PLAIN=1`). Probar SysRq → magic legible **antes** de otro flash
FORTIFY.
### Iteración 2 (ioremap en panic)
- Alt carveout **sí** persiste: write userspace `AXPUSER1` → reboot → sigue ahí.
- Build `20260927T001522Z` con mirror vía `ioremap_wc` **en** `ramoops_pstore_write`
(contexto panic/atomic): SysRq → `bootreason=kernel_panic` pero **sin** `AXPDIAG1`
(ioremap no es safe ahí; write silencioso no-op).
- Fix: pre-map en `ramoops_probe` (`axp_alt_va`) + solo `memcpy_toio`/`wmb` en panic.
Rebuild: job `009-bisect-ksync-pstore-alt-plain2`.
- plain2: map OK + SysRq `kernel_panic`, sin `AXPDIAG1` (aliasing WC vs `ioremap`).
- plain3: `AXPDIAG0-probe` legible tras boot (kernel→alt OK). Tras SysRq solo
`AXPDIAG0` — el hook DMESG pstore no corre en este panic path.
- plain4: `panic_notifier` + `AXPDIAG1-panic-notifier`, pero el boot siguiente
reescribía `AXPDIAG0` encima. Fix: no clobber si head=`AXPDIAG1`.
Rebuild: `pstore-alt-plain5`.
### Canal VERIFICADO (plain7, `20260927T024949Z`)
| Prueba | Resultado |
|--------|-----------|
| `echo x > /proc/sysrq-trigger` (sin panic) | `AXPDIAG1-sysrq-fixed` en `/dev/access-ramoops` |
| sysrq-x + `adb reboot` | `AXP alt keeps prior AXPDIAG1 capture` |
| sysrq-x + sysrq-c → `bootreason=kernel_panic` | magic `AXPDIAG1` sobrevive (cuerpo a veces truncado por reset) |
| Metadata `/dev/access-metadata` tras panic | reescrita (claves AES); no usar |
Lectura post-fallo (root, no hace falta unlock CE):
```text
adb root
dd if=/dev/access-ramoops bs=1M count=4 | strings | grep AXPDIAG
dmesg | grep AXP
```
## v4 canal (post review v3 2026-09-27)
Ver `docs/009-fortify-diag-v3-review-20260927.md`. v3 obsoleto: `ioremap`
tras `paging_init` (antes de slab/vmalloc) puede tumbar el control sin
FORTIFY; attrs early cached vs IO; milestones compartían slots con el fallo.
`apply_fortify_diag.py` **v4**:
- Early: `early_ioremap` (FIXMAP_PAGE_IO) antes de `parse_early_param`; se
mantiene hasta después de `mm_init`. Permanente: `ioremap` **solo** tras
`mm_init`. Handler nunca mapea.
- Layout página 0: fallo @0 (+ mirrors); milestones @2048 (zonas separadas).
- Adoptar fallo previo solo si registro **completo** (header + `\\n` + mirror).
- Ciego residual: pre-`early_map`; ventana breve al cambiar early→perm si el
VA se publica mal (v4 publica permanente antes de retirar early).
Validación control (sin FORTIFY) — media/tardía; no prueba pre-`early_map`:
0. Wipe zona fallo: `dd if=/dev/zero of=/dev/access-ramoops bs=4K count=1`
1. Flash `pstore-alt-plain` v4, boot, `adb root`.
2. Milestones: `strings | grep AXPDIAG0` → `...,early_map` / `perm_map` /
`mm_init` (offset distinto; pueden coexistir con un AXPDIAG1).
3. **Solo** SysRq-c (sin `x`).
4. Recover combo4 → `AXPDIAG1,C,<attempt>,0x...,sysrq-c` completo.
5. Solo entonces fortify-diag v4.
### Review resultados v4
Ver `docs/009-fortify-v4-capture-results-review-20260927.md`,
`docs/009-fortify-v4-clean-tests-verification-20260927.md`, y artefactos:
- `.local-artifacts/009-v4-clean-c-20260927T190738Z/`
- `.local-artifacts/009-v4-x-persist-20260927T191143Z/`
- `.local-artifacts/009-v4-twophase-x-then-c-20260927T191809Z/` (adb
readback then panic: pre-verified X lost by P3)
### LAB 2026-09-27 (plain-v4 `20260927T084531Z`) — hechos vs hipótesis
**Demostrado**
- Writer de first-failure en vivo: SysRq-x →
`AXPDIAG1,X,20260927T084531Z,0x...,sysrq-x` @0 (+ mirrors).
- Milestone slot @2048 recibe `AXPDIAG0,M,...,panic_reg` (último stage;
sobrescribe stages previos en el mismo offset).
- Tras SysRq-c (arranque sin `x` previo) + recover combo4: **no** hay
`AXPDIAG1` en ninguno de los 8 slots de fallo; @0 es opaco.
**No demostrado / correcciones**
- **No** hay, en el código revisado, una rutina de *cifrado del alt al
entrar en panic*. Sí hay camino de descifrado del alt. «Desactivar
encrypt en el kernel» no apunta aún a una función identificada; no usar
eso como siguiente paso.
- Que `panic_reg` aparezca @2048 tras combo4 **no** prueba zona segura ni
supervivencia al panic: combo4 no escribe `AXPDIAG*`, pero un arranque
intermedio de **v4** regenera el mismo `panic_reg` con el mismo attempt
id. Hace falta attempt id distinto por boot, o wipe + leer sin otro boot
v4, antes de atribuir persistencia.
- El latch `axp_first_latch` es **solo RAM**: un wipe del dump no lo
resetea. Si en el mismo arranque se hace SysRq-x y luego SysRq-c, `c`
no escribe. Las pruebas `c`-only válidas requieren wipe **y reboot**
(o imagen nueva) sin `x` previo en ese boot.
- Causa de @0 opaco tras `c` (sobrescritura, fallo de escritura, otro
productor): **abierta**.
+119
View File
@@ -0,0 +1,119 @@
# FORTIFY diagnostic review — 2026-09-27
## Conclusion
The boot failure remains unresolved. The latest capture does **not** exclude
`fortify_panic`, and does not establish a watchdog reset. The diagnostic channel
has an early-boot blind spot and its documented panic validation admits a stale
marker. Fix these experimental controls before interpreting another empty dump.
Review covered the Cursor transcript, local patch scripts and capture plan,
remote kernel sources/build artifacts, saved diag2 dump and installed LAB
modules. No flashing, rebooting, live source changes or new builds were performed
in this review.
## Experiment history and what it establishes
1. Original defconfig bisects also changed bp4a to bp2a (security patch and
platform version regression). Those experiments did not isolate hardening.
2. Corrected ksync packaging retained GOODMAKE's boot environment. Combo4 boots
with DMESG restriction, slab freelist hardening and allocation/free clearing.
3. FORTIFY required a Clang port. Exported overflow BUG stubs were removed;
pass_object_size was aligned to the wrappers' object-size mode 0.
4. The audio/WLAN overflow-helper link failures were localized to specific
functions. Explicit array-capacity guards resolved the targeted compilation
failures. Successful linking does not establish runtime correctness of the
port, or prove those drivers caused the subsequent boot failure.
5. Bounds FORTIFY boots still failed. Diagnostics then added caller printing,
stack dumping and a plaintext marker in the alternate ramoops region.
6. Plain-channel iterations tested mapping, probe overwriting, a panic notifier
and SysRq writes. These are useful channel experiments, but the recorded
plain7 result is insufficient to validate fresh panic-time writes.
7. Latest diag2 (`20260927T032419Z-bisect-ksync-fortify-diag`) failed to boot;
recovery used combo4. No readable failure record has been obtained.
## Confirmed diagnostic defects
### 1. Capture starts too late to exclude an early FORTIFY failure
`apply_fortify_diag.py` maps the alternate region only at the successful end of
`ramoops_probe`. Remote `fs/pstore/ram.c:1270` performs the mapping;
`ramoops_init` is a `postcore_initcall` at line 1365. The writer immediately
returns if `axp_alt_va` is null (patch script lines 105–106).
Remote `init/main.c` calls `setup_arch` at 565 and `parse_early_param` at 579,
before `rest_init` at 738 and the later `do_basic_setup` / `do_initcalls` path.
Disassembly of the current FORTIFY vmlinux contains branches to fortify_panic:
```text
parse_early_param: ffffff8012290974 -> ffffff8011ac186c
start_kernel: ffffff8012290fb8 -> ffffff8011ac186c
setup_arch: ffffff8012296750 -> ffffff8011ac186c
```
These are possible failure paths, **not evidence that any executed**. They
demonstrate that an instrumented FORTIFY failure can occur while the persistent
writer is unavailable. A SysRq test from a fully booted system cannot validate
that interval.
### 2. Plain7's positive control can accept an old marker
The capture plan records `sysrq-x + sysrq-c` followed by survival of the magic
`AXPDIAG1`, sometimes without the complete body. SysRq-x already writes that
magic. The next probe explicitly preserves it. Therefore magic survival alone
does not prove that SysRq-c or the panic notifier wrote anything new.
Require a distinct per-attempt identifier and a complete panic-specific record,
different from the pre-panic marker. Validate the exact failed-boot → fastboot
→ rescue-image → dump sequence, not just a normal reboot into plain7.
### 3. The durable record follows operations that may fail
The injected `fortify_panic` first calls `pr_emerg` and `dump_stack`, then writes
the persistent mark (patch script lines 36–39). A failure or recursion in those
operations could prevent the mark. The durable body contains only the helper
name, not the caller address printed to the unavailable console. Subsequent
panic-notifier and pstore writes also target the same slots and can overwrite
the first record.
Record the first failure before formatted output; include a raw caller address,
event type and attempt identifier. Preserve the first record. Use a minimal
writer independent of the fortified helpers under investigation. This still
requires a correctly reserved/mapped region available at the tested boot stage;
moving the call alone does not fix defect 1.
## Additional checks
- Latest local boot SHA-256 matches the remote published boot:
`a5be43617770205bb9a2d2ad5989ca566ad9713134e7210c585f02fd7bdea554`.
- Current build configuration enables FORTIFY, Clang LTO, Clang CFI and
MODVERSIONS.
- Read-only comparison of all 41 installed vendor `.ko` files against current
remote `Module.symvers`: **3,182 version entries, zero CRC mismatches, zero
missing symbols**. This checks symbol version compatibility only; it does not
prove all runtime ABI/CFI behavior is correct or identify the crashing code.
- LAB was recovered and running combo4 kernel #16 with `sys.boot_completed=1`.
- Saved diag2 `access-ramoops.bin` is 4 MiB and contains none of `AXPDIAG0`,
`AXPDIAG1`, `FORTIFY`, `DBGC` or `Oops`. Measured byte entropy is approximately
7.99996 bits/byte. This does not uniquely distinguish encryption, overwritten
contents or other high-entropy data, nor prove no panic occurred.
- `bootreason=reboot` observed after rescue is not a preserved trace of the
original failure and cannot exclude a panic in the earlier attempt.
## Next experiment requirements
1. Preserve combo4 and archive each diagnostic patch, config, vmlinux and hashes.
2. Validate fresh, distinguishable failure records with an otherwise booting
control. Test persistence across the same recovery sequence used in practice.
3. Cover the early interval explicitly: use a board-supported early output or
correctly reserved early persistent writer, with initialization milestones.
Do not insert ordinary ioremap calls into arbitrary early/panic context.
4. Validate an intentionally triggered diagnostic event at the boot stage being
tested. A late SysRq control alone is insufficient.
5. Only then repeat FORTIFY with a first-failure record containing a caller and
symbolize it using the exact vmlinux. If no reliable early channel is
achievable, use controlled localization experiments and label them as such;
do not promote them to a working or complete hardening port.
Do not add overflow stubs, suppress fortify_panic or declare watchdog/CFI the
cause solely because the current capture is empty.
@@ -0,0 +1,60 @@
# Diagnostic v2 review — 2026-09-27
Reviewed local `apply_fortify_diag.py` and generated remote
`kernel/axp_diag_capture.c` with attempt `20260927T083401Z`, while the plain-v2
control build was running. No live sources or build jobs were changed.
The direction is useful: record before printing, include event/caller/build
identifier, and validate SysRq-c without first writing SysRq-x. However, this
implementation is not yet a validated early-boot recorder.
## Required corrections
1. **Do not map from the failure handler.** `axp_diag_mark_event` calls
`axp_diag_map`, which calls ordinary `ioremap` if no mapping exists. This
reintroduces mapping in panic/early context. In this tree ARM64 ioremap uses
`get_vm_area_caller` and `ioremap_page_range`; mm_init initializes the slab
allocator and vmalloc. Mapping after successful mm_init is a reasonable
later-stage control, but the failure handler must not try to compensate for
an earlier failure with that same ordinary mapping path.
2. **The previously identified early interval is still uncovered.** Remote
`init/main.c` calls setup_arch at 565 and parse_early_param at 579; mm_init
and the new hook are at 600–604. A successful late SysRq test does not
validate a failure in either earlier function or inside mm_init itself.
3. **First-failure retention is not guaranteed.** `axp_first_done` is a plain
integer tested before recording and set only after the writes. Concurrent
CPUs or recursion can both pass the test. The event writer does not inspect
an existing persistent record, despite the stated promise to preserve one;
only the milestone writer does. Use a deliberate atomic/reentrancy protocol
and an explicit policy for records from previous boots. A build timestamp
distinguishes builds, not repeated attempts with the same image.
4. **Milestone format contains an embedded NUL.** The literal `AXPDIAG0,M,`
is 10 characters, but memcpy copies 11 and advances by 11. This copies the
string terminator before the attempt identifier. Consequently `strings |
grep AXPDIAG0` loses the attempt/stage from the same line. Use the literal's
length excluding its terminator (for example sizeof(literal)-1).
## Validation scope
A complete new `C` record surviving the actual combo4 recovery sequence would
be useful evidence for late panic capture. It would not establish early-boot
coverage. Require the exact expected identifier and complete record; define
how previous records are archived/cleared before repeating the same image.
Keep the first failure separate from milestones and subsequent notifier events.
The capture implementation still includes linux/string.h and memcpy calls;
verify the final ARM64/LTO generated writer does not depend on the FORTIFY
failure path under test. `no_sanitize("cfi")` alone does not disable FORTIFY.
Do not interpret an empty capture from this v2 as proof against fortify_panic.
## Status
Local `apply_fortify_diag.py` updated to **v3** addressing items 1–4
(no map-from-handler; early+perm maps in `setup_arch`; atomic latch + durable
head check; milestone `sizeof(lit)-1`). Residual pre-`early_map` blind spot
remains documented in the capture plan. plain-v2 build is obsolete — rebuild
control as plain-v3 before flash.
@@ -0,0 +1,68 @@
# Diagnostic v3 review — 2026-09-27
Checked the local generator and remote generated kernel/axp_diag_capture.c,
attempt 20260927T084106Z, while plain-v3 was building. No build cancellation,
source modification on the server, or device operations were performed.
## Corrected
- Failure handler no longer maps memory.
- Event claim uses atomic_cmpxchg before composing the record.
- Event path checks a previous persistent AXPDIAG1 header.
- Milestone prefix length excludes the terminating NUL.
- Explicit byte I/O replaces memcpy on the MMIO writer path.
- Early mapping now precedes parse_early_param in ARM64 setup_arch.
## Blocking issue: permanent mapping before allocators exist
The new setup.c hook at lines 398–402 calls axp_diag_map_permanent immediately
after paging_init. This is still inside setup_arch, before start_kernel calls
mm_init. It even precedes bootmem_init in setup_arch.
axp_diag_map_permanent releases the early map and calls ordinary ioremap.
In this tree, arch/arm64/mm/ioremap.c calls get_vm_area_caller, which reaches
mm/vmalloc.c:1405 __get_vm_area_node and kzalloc_node at line 1421, followed by
alloc_vmap_area. The allocator initialization (mem_init, kmem_cache_init,
vmalloc_init) occurs later in init/main.c:mm_init.
Thus paging_init is NOT the readiness gate for ordinary ioremap. This diagnostic
can introduce its own boot failure in the no-FORTIFY control. Do not use a
failure of this control to draw conclusions about FORTIFY.
The transition needs a deliberate lifetime plan: ordinary mapping only after
its allocators are ready, and safe retirement of the early mapping respecting
early_ioremap_reset. Simply relocating one call without checking that lifetime
is insufficient. If a temporary uncovered interval is chosen, document it and
ensure failure writers cannot dereference a stale early mapping.
## Mapping attributes and persistence
early_memremap uses FIXMAP_PAGE_NORMAL, whereas the permanent ioremap and
access-ramoops use PROT_DEVICE_nGnRE. This changes memory attributes. The writer
only uses wmb and a readback; it has no explicit cache maintenance for a cached
early mapping. These operations alone do not establish that early cached writes
reach reset-surviving memory. Review compatible early I/O mapping attributes
(early_ioremap uses FIXMAP_PAGE_IO in this tree) and verify on hardware.
## Remaining first-record limitations
- Milestones check the latch but do not participate in its atomic claim. A
milestone interrupted by a failure writer can resume and overwrite the same
slots. Separate milestone storage from first-failure storage, or use a
protocol that also covers milestones and reentrancy.
- A bare eight-byte header is treated as a complete prior failure. An interrupted
write that persisted only that header can suppress later captures. Validate a
complete record/commit indication and inspect mirrors.
- The identifier remains per build, not per boot; archive/clear old captures
under an explicit test procedure before repeating the same image.
The improvements are real, but v3 is not yet ready to be described as a
validated early-capture channel. These findings concern instrumentation, not an
identified cause of the original FORTIFY boot failure.
## Status
Local generator updated to **v4**: no `ioremap` until after `mm_init`; early
map uses `early_ioremap` (IO attrs) and is kept until then; failure @0 vs
milestones @2048; adopt only complete records. plain-v3 must not be flashed —
rebuild control as plain-v4.
@@ -0,0 +1,27 @@
--- a/techpack/audio/dsp/rtac.c
+++ b/techpack/audio/dsp/rtac.c
@@ -652,6 +652,12 @@
pr_debug("%s\n", __func__);
mutex_lock(&rtac_voice_mutex);
+ if (rtac_voice_data.num_of_voice_combos > ARRAY_SIZE(rtac_voice_data.voice)) {
+ pr_err("%s: invalid voice count %u\n", __func__,
+ rtac_voice_data.num_of_voice_combos);
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+ }
/* look for device */
for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) {
--- a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_fils.c
+++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_fils.c
@@ -1092,6 +1092,9 @@
return false;
}
+ if (dot11f_ie_rsn.pmkid_count > ARRAY_SIZE(dot11f_ie_rsn.pmkid))
+ return false;
+
for (i = 0; i < dot11f_ie_rsn.pmkid_count; i++) {
if (qdf_mem_cmp(dot11f_ie_rsn.pmkid[i],
pe_session->fils_info->fils_pmkid,
+127
View File
@@ -0,0 +1,127 @@
# FORTIFY family bisection — 2026-09-27
## Why this experiment
The two-phase X-then-C experiment establishes loss of an adb-verified record
somewhere in the panic/reboot sequence. Further recovery-channel engineering
has not identified the original FORTIFY boot failure. This experiment instead
isolates the protection families while leaving panic behavior intact.
This is diagnostic-only: a boot with one family is not a completed FORTIFY port
and must not be promoted into the shipping hardening configuration.
## Independent analysis before build
- Reconstructed symbols from the saved failed bounds kernel #19, rather than
inspecting the live output overwritten by later no-FORTIFY diagnostic builds.
- Found 372 direct branches to fortify_panic, including its CFI jump-table
entry. Their presence alone does not prove execution. Inspected early
start_kernel/setup_arch checks; no demonstrated unconditional failure found.
- Recompiled init/main.c, arch/arm64/kernel/setup.c and lib/string.c in scratch
space with the actual Clang 21 flags and the ported header; this is not a
replacement for whole-kernel LTO or a device boot.
- The selector's full, memory and strings variants compile for init/main.c and
lib/string.c with those flags. No overflow stubs are added.
## Source and build isolation
`scripts/009-fortify-select-family.py` selects complete wrapper functions from
the existing port and verifies the expected inventory of 15 wrappers. Memory
keeps memset, memcpy, memmove, memscan, memcmp, memchr, memchr_inv and kmemdup.
Strings keeps strncpy, strcat, strlen, strnlen, strlcpy, strncat and strcpy.
Disabled wrappers use the existing raw declarations, not a returning panic stub.
`scripts/009-build-fortify-family-isolated.sh` archives kernel commit
28f9290ae067d3e7857126eb6312720dbe9c7c80 to its own source directory. It applies
the existing Clang port and the already-tested audio/WLAN bounds patch, then
selects a family. It never resets Cursor's live source or writes its output.
The input .config is extracted from the saved failing bounds kernel. olddefconfig
must leave it byte-identical or the job aborts. Clang r563880c, LLVM tools and
Android host build flags are reused. Kernel release suffix is preserved for
installed modules. Full module linking is required even though only boot will
be packaged. The raw new Image is injected into GOODMAKE; ramdisk and DTB stay
from GOODMAKE. Local unpack, metadata, payload and module-version checks remain
required before device use.
## First run
Remote root: `/srv/rom/artifacts/009-fortify-family-20260927/`.
Detached session: `009-codex-fortify-memory-20260927`.
Log: `/srv/rom/artifacts/detach-009-codex-fortify-memory-20260927-20260927T193643Z.log`.
Status: `/srv/rom/artifacts/detach-009-codex-fortify-memory-20260927.status`.
The memory-only build started at 19:36:43 UTC. Configuration drift check passed
(empty memory/config.diff). Build finished exit=0 at 19:58:48 UTC
(`memory/boot-make.img`).
**LAB boot (2026-09-27 ~22:00 local):** flashed both slots → `sys.boot_completed=1`
on kernel `#1` `Sun Sep 27 19:45:25 UTC 2026`. Memory-only family **boots**.
**Strings-only build:** session `009-codex-fortify-strings-20260927`, finished
exit=0 at 20:51:38 UTC (`strings/boot-make.img`).
**LAB boot (strings):** flashed both slots → returned to **fastboot** (~100s,
no `boot_completed`). Recovered combo4. Strings-only family **fails to boot**.
**Family result:** memory boots, strings does not → narrow toward string
wrappers (`strncpy`/`strcpy`/`strlen`/…), not the memcpy family. Still
diagnostic-only; not a shippable FORTIFY subset.
**Strings split (LAB):**
- `strings-copy` → **boots**
- `strings-len` (strlen, strnlen) → **boots**
- full `strings` (copy+len together) → **fails**
Implication: neither half alone is sufficient to reproduce; failure is in the
**interaction** of fortified copy + fortified len (or LTO/CFI coupling of
both). Next: smaller cross products (e.g. strcpy+strlen only), same baseline.
**Cross products (LAB):**
- `strings-strcpy-strlen` → **boots**
- `strings-copy-strlen` (5 copy + strlen) → **fails**
- `strings-copy-strnlen` → **boots** (strlen is the critical len partner)
- `strings-strlen-restcopy` (strlen+strncpy/strlcpy/strcat/strncat) → **fails**
- `strings-strlen-ncopy` (strlen+strncpy+strlcpy) → **fails**
- `strings-strlen-cat` (strlen+strcat+strncat) → **boots**
- `strings-strlen-strncpy` → **boots**
- `strings-strlen-strlcpy` → **fails**
- **Smoking gun:** fortified `strlcpy` + fortified `strlen` together.
- **Root cause:** 4.14 `strlcpy` does `ret = strlen(q)`. With Clang
`__pass_object_size`, nested fortified `strlen` panics on known-size
sources; GCC usually saw bos==-1 and skipped. Fix in
`apply_fortify_clang.py`: `strlcpy`/`strcpy` use `__underlying_strlen`.
**Fix verification (LAB 2026-09-28):**
- Rebuilt `strings-strlen-strlcpy` + full `strings` with fixed apply → both
`exit=0` (~18:24 / 18:26 UTC).
- Flashed both slots: `strlen+strlcpy` → `boot_completed=1`; full `strings`
→ `boot_completed=1` (`CONFIG_FORTIFY_SOURCE=y`, kernels `#1` 18:06 / 18:10 UTC).
- Pre-fix pair archived as `strings-strlen-strlcpy-broken/` on the host.
**Full family (LAB 2026-09-28):** rebuilt `full` (15 wrappers) with nested-strlen
fix → `exit=0` 19:31 UTC → flashed both slots → **`boot_completed=1`**
(`CONFIG_FORTIFY_SOURCE=y`, kernel `#1` 19:18 UTC). Port wrappers alone are
no longer the brick.
**combo4+FORTIFY (LAB 2026-09-28):** ksync
`20260928T201655Z-bisect-ksync-combo4-fortify-fix` → flashed both slots →
**`boot_completed=1`** with `FORTIFY` + `INIT_ON_*` + `DMESG` +
`SLAB_FREELIST_HARDENED`. Nested-strlen fix unblocked the previous ship
candidate pack.
## Interpretation
- A memory-only boot would narrow investigation toward the string wrappers or
their interaction with memory wrappers. It would not prove a specific string
operation or driver is faulty.
- A memory-only failure would keep the memory family, common port machinery and
build/runtime interactions in scope; it would not clear the string family.
- Complementary/control builds should follow the observation, changing only the
selector. If behavior is deterministic, continue by splitting the implicated
group, then its caller set. Do not restart from an unrelated build baseline.
- Preserve vmlinux, source, effective config, command, compiler version, hashes
and the exact boot result for each variant.
+89
View File
@@ -0,0 +1,89 @@
# Revisión del port FORTIFY en curso — 2026-09-26, 21:25 UTC
Revisión de solo lectura del servidor; no se han editado scripts en ejecución.
El dueño confirma combo4 arrancado. Cursor registra kernel #16, DMESG,
SLAB_FREELIST_HARDENED, INIT_ON_ALLOC y INIT_ON_FREE activos, FORTIFY apagado.
No se ha repetido aquí la inspección de LAB.
## Estado real
Job: `009-bisect-ksync-fortify-clang`, iniciado a las 21:15:03 UTC.
Log: `/srv/rom/artifacts/detach-009-bisect-ksync-fortify-clang-20260926T211503Z.log`.
A las 21:25 UTC seguía activo, con procesos clang consumiendo CPU. El log
principal no se actualizaba desde las 21:18:39; eso no implicaba bloqueo.
No había resultado final ni nuevo artefacto FORTIFY validado.
El script recibe **solo CONFIG_FORTIFY_SOURCE**. Es un ensayo aislado, no
combo4+FORTIFY; coincide con el plan local. Preserva breakfast/bp4a y el
empaquetado sobre GOODMAKE. El diff actual no modifica lib/string.c ni añade
los stubs BUG. Mantener deshabilitado ALLOW_FORTIFY_BUG_STUBS.
## Hallazgo que revisar antes de aceptar el port
El script `apply_fortify_clang.py` adapta atributos y prototipos, pero deja
consultas de tamaño incoherentes con algunos atributos:
- POS equivale a `pass_object_size(1)`.
- `strlen` y `strnlen` reciben p con POS y consultan
`__builtin_object_size(p, 0)`.
- `strlcpy`, `strncat` y `strcpy` reciben q con POS y consultan su tamaño
con tipo 0. En otros wrappers, POS0 sí coincide con las consultas tipo 0.
Esto no es un detalle estético. La información de tamaño implícita depende
del tipo. Se compiló por stdin una prueba pequeña con el mismo Clang
r563880c, también para AArch64 freestanding, sin escribir en el servidor:
```c
typedef __SIZE_TYPE__ size_t;
__attribute__((noinline))
size_t mismatch(const char *const p __attribute__((pass_object_size(1)))) {
return __builtin_object_size(p, 0);
}
__attribute__((noinline))
size_t matched(const char *const p __attribute__((pass_object_size(0)))) {
return __builtin_object_size(p, 0);
}
```
El IR optimizado de mismatch devuelve -1 (tamaño desconocido); matched
devuelve el argumento implícito de tamaño. **Límite:** es una prueba aislada
noinline del mecanismo, no una demostración de que todas las llamadas a los
wrappers always_inline reales pierdan protección. Es necesario probar los
wrappers reales con optimización/LTO, tamaños conocidos y desconocidos.
Referencia primaria:
[Clang: pass_object_size](https://clang.llvm.org/docs/AttributeReference.html#pass-object-size-pass-dynamic-object-size).
Adaptar coherentemente los atributos y la semántica de tamaño del 4.14;
no sustituir todas las consultas por tipo 1 a ciegas, porque cambia la
política whole-object/subobject y puede alterar accesos válidos del árbol.
## Otros puntos de aceptación pendientes
- El port elimina `depends on !CC_IS_CLANG` sin implementar la detección y
los límites de versión/capacidades que propone su propio plan. Los macros
de atributos tienen fallback vacío: un compilador no compatible puede
volver a habilitar un FORTIFY sin soporte. No es prueba de fallo de Clang 21,
pero la configuración aún no tiene una protección adecuada.
- `__diagnose_as` está definido pero no utilizado en los wrappers revisados.
Documentar si se omite intencionadamente y validar diagnósticos reales;
declarar el atributo no implementa su efecto.
- El empaquetador imprime propiedades/config pero varias comprobaciones
terminan en `|| true`. Mostrar el valor no garantiza que coincida con el
esperado. Antes de entregar: comparar hashes de ramdisk/DTB, metadatos,
kernel inyectado y opciones de la config embebida, fallando si difieren.
- No editar el script activo. Congelar copia, hash, argumentos, diff y
configuración por experimento para evitar repetir el incidente combo4.
## Qué debe probarse además de compilar y arrancar
Sin stubs es necesario, pero no suficiente: quitar accidentalmente una
comprobación también puede hacer desaparecer símbolos de overflow.
Usar casos válidos y desbordamientos intencionados en un harness aislado
de los wrappers reales (no provocar corrupción en LAB), comprobando el
diagnóstico o camino de fallo esperado con Clang/LTO. Verificar también
ausencia de stubs en binarios/módulos, .config efectiva y hardware básico.
Después del ensayo aislado, validar combo4+FORTIFY como artefacto distinto.
Conclusión: el enfoque mejora y el build está activo; aún hay detalles del
port y pruebas de eficacia pendientes. No equivale a un port ya aceptado.
@@ -0,0 +1,103 @@
# FORTIFY POS0: dos errores localizados y corrección candidata
Revisión del job `009-bisect-ksync-fortify-pos0`, que terminó con exit=1
el 2026-09-26 a las 21:54:28 UTC. Se leyó el log del servidor, el fragmento
aportado y la conversación reciente de Cursor de este proyecto.
## Resultado
Se reprodujeron los dos símbolos sin resolver en compilaciones aisladas de
los archivos responsables, usando los argumentos del build fallido y su
Clang 21. Añadiendo validaciones explícitas de capacidad, desaparecen ambos
en los objetos tras LTO, **sin stubs y sin desactivar FORTIFY**.
[Parche candidato](009-fortify-driver-bounds-candidate.patch).
Es un cambio local propuesto en esta revisión, no un parche upstream.
No se ha aplicado al árbol de compilación de Cursor. `git apply --check`
contra el árbol remoto actual terminó correctamente.
## Localización en los objetos reales del build fallido
Raíz: `/srv/rom/src/lineage/out/target/product/sunfish/obj/KERNEL_OBJ`.
- `techpack/audio/dsp/q6_dlkm.lto.o`: `rtac_remove_voice+0x1d0`,
relocación `R_AARCH64_CALL26 __read_overflow2`.
- `drivers/staging/qcacld-3.0/wlan.lto.o`:
`lim_process_fils_auth_frame2+0xe48`,
relocación `R_AARCH64_CALL26 __read_overflow`.
Importante: modpost etiqueta el error con `.lto.ko`, pero los objetos
intermedios disponibles son `.lto.o`. Los `.ko` antiguos que quedan en out
no deben confundirse con módulos del intento fallido.
### Audio
`techpack/audio/dsp/rtac.c` tiene capacidad para dos entradas de voz.
`rtac_remove_voice` usa un contador global para recorrer el array y llama a
`shift_voice_devices`, que copia del elemento `idx+1`. El código de alta
ya limita el número de entradas, pero ese invariante no basta para eliminar
la rama de diagnóstico en esta compilación.
El candidato valida el contador bajo `rtac_voice_mutex`, antes del recorrido.
Si supera ARRAY_SIZE(voice), registra el error, libera el mutex y retorna.
Esto evita aceptar el estado inválido; no altera el recorrido válido ni
desactiva las comprobaciones de memoria.
### Wi-Fi
`lim_process_fils.c:1095` recorre `dot11f_ie_rsn.pmkid_count` y compara
PMKID de 16 bytes. El array declarado en `dot11f.h:6725` es `pmkid[4][16]`.
El objeto LTO conserva una rama al helper de overflow cuando el contador
alcanza cinco o más sin haberse encontrado una coincidencia.
El parser `dot11f.c:6703` ya rechaza contadores mayores de cuatro. Por tanto,
esta revisión **no demuestra que exista una entrada explotable ni una lectura
real fuera de límites**. El compilador no elimina todas las ramas inválidas
en el consumidor. El candidato comprueba ARRAY_SIZE(pmkid) justo antes del
bucle y devuelve false ante un contador inválido.
## Validación realizada
Se trabajó en directorios temporales de rom-dev01, sin cambiar las fuentes
ni los objetos de salida usados por Cursor:
- `/tmp/axp-fortify-probe-bl9zsxpt`: reproducción de ambos archivos y
prueba Wi-Fi; contiene también una variante preliminar de audio no elegida.
- `/tmp/axp-fortify-probe-kkcpn4o9`: reproducción y candidato final de audio.
Método: leer la orden original de `.rtac.o.cmd` / `.lim_process_fils.o.cmd`,
conservar sus opciones de Clang/LTO/CFI e includes; cambiar únicamente la
fuente/output a copias temporales, evitar generar dependencias en out y
añadir el directorio original para resolver includes locales. Ejecutar
`ld.lld -m aarch64linux -O2 -r` sobre cada objeto bitcode y consultar
símbolos sin resolver con llvm-nm.
Resultados:
```text
audio baseline: U __read_overflow2
audio con validación del contador: ningún símbolo overflow sin resolver
wifi baseline: U __read_overflow
wifi con validación del contador: ningún símbolo overflow sin resolver
```
**Alcance:** son dos unidades de compilación y su enlace LTO aislado. No se
ha repetido el enlace completo de q6_dlkm/wlan, modpost global, ROM o boot.
No se han probado arranque, llamadas de voz, audio ni Wi-Fi en LAB. Tampoco
se sustituye con esto el harness pendiente de eficacia de los wrappers.
## Reanudación para Cursor
1. Conservar la evidencia del intento fallido y revisar/aplicar el candidato
después del reset y del port FORTIFY en la receta reproducible. El script
ksync resetea las fuentes: una edición manual previa se perdería.
2. Validar módulos completos/modpost sin `__read_overflow*`, sin nuevos
stubs, sin `__NO_FORTIFY` en estos controladores y sin apagar LTO/CFI.
3. Validar el harness de FORTIFY, la config efectiva y el empaquetado antes
de entregar un nuevo boot. Mantener combo4 como referencia que arranca.
4. Tras aceptar FORTIFY aislado, probar combo4+FORTIFY y funciones de hardware.
La conversación de Cursor proponía mirar los call sites o una exclusión
puntual con `__NO_FORTIFY`. Ya hay evidencia para intentar primero estas
validaciones de límites. Las advertencias de versionado de símbolos WLAN
son un asunto adicional; esta corrección no afirma resolverlas.
@@ -0,0 +1,99 @@
# v4 capture results review — 2026-09-27
## Result
The capture channel still has no validated panic-survival result. Do not infer
that AES selectively overwrites offset 0 while sparing offset 2048, and do not
choose a high offset as a safe region without establishing its ownership and
observed persistence. No FORTIFY-specific failure has been captured yet.
Reviewed Cursor's recorded experiment summaries, v4 generated source, the kernel
ramoops implementation, sunfish DT and init.ramoops.sh. Read the current LAB alt
region while LAB was running combo4 #16; no device writes, flashes or reboots.
Current 4 MiB capture SHA-256:
`24bbccbf4ed1271bc0950167f4a7921e2a79c534ad735d506ebb504a0353f310`.
It contains `AXPDIAG0,M,20260927T084531Z,panic_reg` at 2048, and no AXPDIAG
marker at the eight expected failure offsets 0,128,256,384,512,640,768,896.
## Unsupported steps in the current explanation
1. Random-looking bytes are not proof that the current test caused encryption.
Without a before/after snapshot from the same attempt, they can be old data,
overwritten data or ciphertext. The stored old captures are from different
experiments and cannot establish this particular transition.
2. The milestone is written again on every v4 boot, with a build-time identifier
shared by every boot of that image. Cursor reports that the first SysRq-c
returned with kernel_panic, followed by further live v4 tests. A regenerated
milestone is therefore an alternative to survival across panic. Final
recovery to combo4 does not resolve its age across the preceding boots.
3. The v4 latch is in ordinary kernel RAM. SysRq-x sets it. Clearing the alt
region with dd does not clear the latch in the running kernel. A subsequent
SysRq-c in that same boot will skip recording, even if the region looks
empty. Cursor's summary says a clean retry occurred but does not establish
the full clear/reboot/no-X sequence; this remains a confounder, not a proven
mistake in the executed sequence.
4. Current fs/pstore/ram.c exposes ramoops_decrypt_alt: it reads cxt->size,
decrypts with crypto_aead_decrypt, and copies the whole buffer back only on
success. This is not a panic-time encrypt routine. The inspected sunfish
init script invokes ramoops -D and then ramoops -g -l -c. No kernel panic
encrypt implementation was identified by these searches. Firmware ownership
remains possible, but unproven. Disabling kernel decryption would not by
itself demonstrate that an external encryption path was disabled.
5. The DT declares a 4 MiB alt ramoops reservation, not a separately reserved
diagnostic high-offset region. No offset within it is currently shown safe.
## Next useful test: preserve attribution before another rebuild
Use the existing no-FORTIFY control to establish a documented timeline. Keep
SysRq-x and SysRq-c experiments in separate boots, and archive full dumps,
kernel identity, and boot reason at each available observation point.
- Archive and clear the diagnostic region BEFORE booting the control; then
verify the region contains no complete old failure record. Do not clear only
the persistent bytes after X and assume the in-memory latch has reset.
- Run a fresh boot with no X. Archive the pre-C contents; execute only C.
Recover directly to combo4 when possible, recording any intermediate v4
boot or forced hardware reset. A forced reset is a separate experimental
condition from the automatic panic reboot.
- Compare the full before/after bytes, including mirrors and milestone area.
Do not use the same-build milestone alone as evidence of survival.
- Independently test X persistence through the SAME recovery sequence. This
distinguishes a writer that works in a live kernel from storage that survives
the recovery path. It does not replace the fresh-C test.
If fresh C still leaves no attributable record, a control-only diagnostic can
read back/validate the exact committed C record immediately before panic and
report or expose that result while observable. This separates failure to write
from later loss. It must remain a diagnostic experiment, not a swallowed panic
or a production hardening change.
Only after identifying which transition loses a verified record should the
next patch change its storage or intercept an identified writer. A separately
reserved region needs a board/firmware-compatible design; an arbitrary high
offset is not a substitute for that evidence.
## Remaining source limitation
v4 composes up to 128 bytes but caps persistence at 120; long tokens can lose
the terminating newline required by adoption. This does not explain the short
sysrq-c/sysrq-x records, but must be fixed for general panic messages.
The original FORTIFY cause remains open. Diagnostics v2/v3 had concrete
implementation defects; v4 fixes those major defects and boots, but its test
results still do not establish a working early-panic channel.
## Follow-up LAB (same evening, documented)
Clean C-only:
`.local-artifacts/009-v4-clean-c-20260927T190738Z/` — clear → v4 boot (no X)
→ C → **intermediate auto v4 reboot** (`kernel_panic`) → combo4. No
`AXPDIAG1` at T3 or T4. Fail zone 0:1024 went from all-zero (T2) to opaque
non-`AXPDIAG1` bytes (T3=T4). Latch/X confounder ruled out for this run.
X persistence (no panic):
`.local-artifacts/009-v4-x-persist-20260927T191143Z/` — `AXPDIAG1,X,...`
survives combo4 recover; fail[0:128] identical before/after.
Next: distinguish “C never committed” vs “lost across panic” with a
control-only pre-panic readback of the committed C record (see above).
@@ -0,0 +1,50 @@
# Verification of clean C and X experiments — 2026-09-27
Independently parsed the binary dumps and recorded kernel/boot properties in:
- `.local-artifacts/009-v4-clean-c-20260927T190738Z/`
- `.local-artifacts/009-v4-x-persist-20260927T191143Z/`
The clean C test removes the earlier X/latch confounder: T2 has a zero failure
zone and a fresh v4 #29 boot; T3 reports kernel_panic on an intermediate v4 boot;
T4 is combo4 #16. No complete failure record is present after C. The intermediate
boot prevents localization of loss to the panic itself versus later boot stages.
The X test demonstrates that all eight complete 55-byte X records survive the
normal recovery to combo4 unchanged. It does not include the panic transition
of the C test, so it is a recovery control rather than the same full sequence.
Precision correction: T3 and T4 are NOT identical binary dumps. Their first 128
bytes match, but 7,213 bytes differ across 4 MiB, including six bytes in the first
1 KiB. Likewise X2 and X3 have 79 differing bytes, despite their complete X
records surviving intact. These differences do not negate X persistence, but
avoid describing entire zones or dumps as identical. Their cause is not yet
established.
Next diagnostic should check a full C record (primary and mirrors, expected
event/id/caller/terminator) immediately after its writer and before panic.
The success/failure result must be observable independently of the storage
whose survival is under test. A print to a console that cannot be retrieved, or
another flag only in the same alt region, would repeat the observability problem.
A control-only two-phase arm/readback/trigger protocol can expose the record
and status through adb before deliberately entering panic; document the changed
timing and validate the actual immediate path subsequently. Keep normal FORTIFY
failure behavior intact.
This establishes a narrower investigation: the writer plus normal recovery can
work; a clean panic experiment still loses or fails to produce the record.
Selective AES overwrite and a safe alternative offset remain unproven.
## Follow-up: two-phase arm/readback/panic (same evening)
Artifact:
`.local-artifacts/009-v4-twophase-x-then-c-20260927T191809Z/`
Using existing v4 (no rebuild): SysRq-x → **adb dump confirms** eight
complete `AXPDIAG1,X` records → SysRq-c (latch no-ops C write; panic runs)
→ intermediate v4 (`kernel_panic`) → combo4.
Result: pre-verified X is **absent at P3 and P4**. Independent channel was
adb, not alt-only flags. Timing caveat: adb RTT between arm and panic; type
X not C. Narrow reading: fail-zone content verified before panic does not
survive this panic/reboot path; soft recover without panic still preserves X.
+135
View File
@@ -0,0 +1,135 @@
# Gate G / FR-017 — kernel hardening attribution (ship candidate)
**What “Gate G doc” means:** the acceptance contract asks for a written list of
**what kernel hardening we applied, what we skipped and why**, plus userspace
AVB/signing gaps (or “impossible on this stack”). This file is that list for the
**kernel** half. Userspace AVB is tracked separately below as still open.
**LAB device:** `11231JEC207477` only.
**Accepted kernel ship pack (LAB 2026-10-01):** Stage1 Graphene/AH + combo4 +
Clang FORTIFY + Magisk preroot + **CVE clean-offset 429** + **hi-batch1+2 (15)**
with `CVE-2022-49980` denylisted. Clean-only `#47` / hi-batch1 `#48` remain bisect GOODs.
Prior Stage1-only pack `20260929T123401Z-axp-stage1-combo4` remains the
**recover / bisect GOOD** baseline (kernel `#32`).
## Current accepted artifact
| Field | Value |
|--------|--------|
| Artifact | `/srv/rom/artifacts/009-axp-los23-port/20261001T201836Z-axp-stage1-combo4-cve-hi-batch2/` |
| Local flash copy | `.local-artifacts/009-flash-cve-hi-batch2/` |
| Prior hi-batch1 GOOD | `20261001T192418Z-…-cve-hi-batch1/` (`#48`) |
| Prior clean GOOD | `20261001T180955Z-…-cve-clean/` (`#47`) |
| Kernel base | LOS `msm-4.14` `@ 28f9290ae067` (`4.14.357-openela`) |
| Running LAB (boot flash) | slot `_a`, kernel `#49` `Thu Oct 1 19:49:54 UTC 2026` |
| CVE list | clean **429** + hi-batch1 (**7**) + hi-batch2 (**8**) |
| Apply quality | clean OK=429; hi OK=15 fuzz≤1 (elevated abs-offset) |
| Note | host detach `exit=141` after `bacon_exit=0` = tee SIGPIPE, not a real fail |
| Magisk | EXTENDROM-class preroot v30.7 (virgin until Additional Setup; `magiskd` OK) |
| KernelSU | absent (Magisk track) |
**Flash procedure that works:** recovery → (optional wipe) → sideload zip →
**Reboot system**. Bisect/CVE boots may use `fastboot -s 11231JEC207477 flash boot`
only when CRC/modules match the installed vendor. Do **not** flash Magisk
`boot.img` onto the other slot alone (new kernel + old vendor ⇒ `wlan` CRC mismatch).
**Build entrypoints:**
- Apply: `apply_stage1_combo4.sh` then `apply_cve_strict.sh` with
`CVE_STRICT_LIST=…/cve-clean-offset.list` (denylist includes 49980)
- Bacon: `scripts/009-bacon-axp-stage1-combo4-cve-clean.sh` (or
`009-bacon-cve-clean-post49980` host wrapper)
- Baseline-only: `scripts/009-bacon-axp-stage1-combo4.sh`
## Applied (attributable)
### A. Combo4 defconfig + Clang FORTIFY port
| Item | Source / notes |
|------|----------------|
| `CONFIG_FORTIFY_SOURCE=y` | Ship; Clang port required on this toolchain |
| `CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y` | Combo4 |
| `CONFIG_INIT_ON_FREE_DEFAULT_ON=y` | Combo4 |
| `CONFIG_SECURITY_DMESG_RESTRICT=y` | Combo4 |
| `CONFIG_SLAB_FREELIST_HARDENED=y` | Combo4 (not `SLAB_HARDENED` alone) |
| Clang FORTIFY / `__pass_object_size` | `fortify-clang/apply_fortify_clang.py` (Kees-class port adapted to 4.14 `string.h`) |
| Nested strlen fix | `strlcpy`/`strcpy` use `__underlying_strlen(q)` — LAB smoking gun 2026-09-28 |
| Driver bounds | `fortify-clang/0001-driver-bounds-rtac-fils.patch` (link/false-path) |
| `# CONFIG_KSU is not set` | Magisk track |
### B. Stage1 Graphene / AndroidHardening (30 patches)
Source tree: AXP `kernel_patches` (host `/srv/rom/references/axp/kernel_patches`).
List: `vendor/sunfish-a16/patches/009-kernel-hardening/stage1.list` via
`apply_stage1.sh`.
Includes (series names): Deny_USB, allocsize (partial), bugon, fortify
strict-string patch (AXP `0004` — **not** a substitute for the Clang port),
misc, random, ro, sanitize, slab, unused, etc. Full paths in `stage1.list`.
**LAB marker:** `/proc/sys/kernel/deny_new_usb` present (Graphene Deny USB).
**Build fix retained:** Stage1 `slab/0014` misfuzz into `is_root_cache` →
reverted in `apply_stage1_combo4.sh` when detected (`BUILD-FIXES.md`).
## Explicitly NOT in this ship pack
| Item | Why |
|------|-----|
| Full `cve-strict.list` (~503) | Logo hang; includes high-offset (≥200) and `CVE-2022-49980`. Do not reflash `20260929T181525Z-…-cve` / old `*-kh-cve`. |
| `CVE-2022-49980` (USB gadget UAF) | **Denylist.** Alone reproduces Google-logo hang on LAB (2026-10-01). One-liner takes `udc_lock` in `usb_udc_uevent` while this 4.14 tree still fires uevents under the same lock (`device_add` / bind / unbind). Needs lock-scope companion port, not bulk apply. See `docs/009-cve-bisect-plan.md`. |
| High-offset CVE set (`cve-high-offset-ge200.txt`, ~73) | Quarantine + sunfish filter (26 KEEP). **hi-batch1+2 (15) LAB BOOT_OK `#49`**. Remaining KEEP ~10 (skip `3777`/`26643` nf_tables-class). |
| LinuxIncrementals | Skipped (policy / scope) |
| Failed Stage1 probes | allocsize/0007, ro/0007+0034, slab/0010+0018 — port later |
| Location lockdown | FR-018 — **skip forever on this track**; keep GPS precise/fast (LOS defaults) |
| FORTIFY `BUG()` overflow stubs | Invalid ship fix; never reuse |
| KernelSU / SusFS | Out of Magisk ship track (optional later recycle) |
| Userspace Deblob (Widevine/IMS/CNE) | Deferred (mobile data / Widevine) |
| Bootloader lock | Wont (AXP/LOS unlocked norm) |
## Historical note (why this pack exists)
1. Stage1 / CVE / kh-full bacons of 2026-09-18 **compiled** but **did not boot** on LAB (logo hang).
2. Bisect found Clang FORTIFY + nested `strlen` inside `strlcpy` as a hard brick; combo4 without broken FORTIFY booted.
3. Fixed FORTIFY → combo4+FORTIFY Magisk bacon OK → Stage1 **re-tried on that baseline** → LAB OK 2026-09-29.
## Userspace Gate G (still open)
Kernel attribution above satisfies the **FR-017 / SC-010 kernel** bullet.
Userspace / signing work is **not** done by this kernel bacon.
### In scope (do)
| Item | Intent |
|------|--------|
| **Release / definitive signing keys** | Ship OTAs and images signed with project release keys (not AOSP test-keys). Phonesky and update identity stay on that key across releases. Secrets stay on `rom-dev01` under `/srv/rom/secrets/` — never in Git. |
| **AVB as applicable on unlocked sunfish** | Match GOLDEN/AXP-class AVB *build* posture where LOS 23.2 allows; document any gap that is impossible without locked bootloader or vendor glue we do not have. |
| **Userspace hardening** | Staged Graphene/AXP-class patches (and CVE mega on the boot-OK kernel pack) — attributable, bisectable. |
| **CVE mega (kernel)** | **Partial ship:** clean-offset 429 on Stage1+combo4+FORTIFY (LAB OK). Remaining: high-offset quarantine + proper 49980 port. Never flash `20260918*-kh-cve/full` or full-503 BAD packs. |
### Out of scope (explicit skip)
| Item | Why |
|------|-----|
| **Bootloader relock / locked-device ship** | Neither **AXP.OS** nor **LineageOS** ship a locked-bootloader product as the normal path. **IOdes** does; we are not cloning that model. LAB and end-user sunfish remain **unlocked** for Magisk preroot / sideload workflow. Do **not** treat “cannot lock bootloader” as a Gate G failure. |
Gap rule unchanged: anything still missing from the **in-scope** rows must be present **or** written down as impossible on this stack — no silent omit.
**Checklist + measured GOLDEN vs LAB:** `docs/009-hardening-signing-checklist.md`
(T040). LAB 2026-09-29 is still `userdebug`/`test-keys`/`orange`; GOLDEN is
`user`/`release-keys`/`orange` unlocked — same unlock class, missing project
keys.
## Prior known-good (fallback)
| Artifact | Role |
|----------|------|
| `20260929T033128Z-axp-combo4-fortify` | Magisk + combo4+FORTIFY **without** Stage1 |
| `20260918T143547Z-axp-full` | Pre-KH Magisk baseline (bisect GOODMAKE source) |
## Owner LAB smoke (2026-09-29)
Owner reported Magisk, microG, and related checks OK on Stage1 pack after
sideload to slot `_b`. Optional: second sideload to refresh leftover slot `_a`
(not required for daily use).
+248
View File
@@ -0,0 +1,248 @@
# 009 — Userspace hardening / signing / AVB checklist (Gate G)
**Task:** T040.
**Companion:** kernel half = `docs/009-gate-g-kernel-attribution.md`.
**Policy (2026-09-29):** project **release keys yes**; **bootloader relock no**
(AXP/LOS unlocked norm; IOdes-class lock = non-goal). See
`specs/009-axp-los23-port/research.md`.
Secrets never enter Git. Host material lives under `/srv/rom/secrets/` on
`rom-dev01` only.
---
## Measured baseline (2026-09-29)
| Prop / fact | GOLDEN (AXP Pro) | LAB (Stage1 pack) | Target |
|-------------|------------------|-------------------|--------|
| `ro.build.type` | `user` | `userdebug` | `user` for ship; `userdebug` OK while developing |
| `ro.build.tags` | `release-keys` | `test-keys` | `release-keys` |
| `ro.build.flavor` | `lineage_sunfish-user` | `lineage_sunfish-userdebug` | `…-user` at ship |
| `ro.debuggable` | `0` | `0` on LAB measure wait — typically `1` on userdebug | `0` on ship |
| `ro.secure` | `1` | (LOS default) | `1` |
| `ro.boot.flash.locked` | `0` | `0` | `0` (unlocked — intentional) |
| `ro.boot.verifiedbootstate` | `orange` | `orange` | `orange` while unlocked |
| `ro.boot.vbmeta.device_state` | `unlocked` | (same class) | unlocked |
| `ro.boot.veritymode` | `enforcing` | expect enforcing | keep |
| SELinux | Enforcing (expect) | **Enforcing** | keep |
| OTA zip cert | AXP releasekey | **AOSP `testkey.x509.pem`** (byte-match on stage1 zip) | project releasekey |
| AVB signing key in tree | AXP custom | `external/avb/test/data/testkey_rsa2048.pem` | project AVB key |
| `BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS --flags 3` | AXP sets real AVB when enabled | LOS sunfish adds `--flags 3` unless `WITH_AVB=true` | keep flags 3 (signing infra only; not runtime harden) |
LAB adb sample (device `11231JEC207477`, Stage1 sideload):
`test-keys` / `userdebug` / `orange` / `flash.locked=0` / `getenforce=Enforcing`.
GOLDEN source: `/srv/rom/references/axp/golden/2026-09-13/getprop.txt`.
---
## What “done” means for Gate G userspace
1. Ship images/OTAs verify as **`release-keys`** (not AOSP test-keys).
2. Phonesky / microG keep **their** project JKS identities under
`/srv/rom/secrets/{phonesky,microg}-signing/` (already exist).
3. AVB *build* uses project keys where LOS23/sunfish allows; device stays
**unlocked** (`orange`). Relock is **out of scope**.
4. Userspace hardening stages are attributable (patch dir + map) or explicitly
deferred/skipped with reason.
5. Gaps written down — no silent omit.
---
## Stages (do in order)
### US-S0 — Inventory (this doc) — **done 2026-09-29**
- [x] Compare GOLDEN vs LAB signing/AVB props.
- [x] Confirm stage1 zip OTA cert == AOSP testkey.
- [x] Confirm no `~/.android-certs` / platform release keys on host yet
(only Phonesky + microG JKS).
- [x] Record bootloader-lock = wont.
### US-S1 — Generate project Android signing keys — **done 2026-09-29 (host)**
Host-only. Never commit `.pk8` / private PEM.
| Key | Role |
|-----|------|
| `releasekey` | Default APK/OTA (`PRODUCT_DEFAULT_DEV_CERTIFICATE`) |
| `platform` | `platform`-signed priv-apps |
| `shared` / `media` / `networkstack` / `sdk_sandbox` / `bluetooth` / `nfc` | AOSP partition signing set |
Procedure: `scripts/009-generate-release-keys.sh` on `rom-dev01`
→ prefers `/srv/rom/secrets/android-certs/` if writable, else
`$HOME/.android-certs` (Lineage default; used 2026-09-29 because secrets/
is root-owned 750 without group write).
**Mandatory before first signed ship bacon:** encrypted/offline backup of that
directory (constitution). Public `*.x509.pem` may later be copied into Git for
docs; private material must not.
Acceptance:
- [x] `$HOME/.android-certs/{releasekey,platform,…}.{pk8,x509.pem}` + `avb.pem`
exist on `rom-dev01` (2026-09-29T16:37Z)
- [x] Host tar backup: `/home/android-build/backups/android-certs-20260929T1637Z.tar.gz`
(mode 600; SHA256 in `.sha256`) — **still copy offline/encrypted**
- [x] `releasekey` DN = `O=AXP-Resurection, OU=sunfish-a16, CN=release`
(not AOSP testkey)
- [ ] Root later: `install -d` `/srv/rom/secrets/android-certs` and move keys there
- [ ] Owner offline encrypted copy confirmed
### US-S2 — Wire release keys into bacon — **in progress**
Lineage convention: `PRODUCT_DEFAULT_DEV_CERTIFICATE` → path without suffix
(e.g. `…/releasekey`). On LOS 23 / soong, **env export alone is ignored** —
must be a product makefile `PRODUCT_` (see `vendor/sunfish-a16/signing.mk`,
inherited via `magisk.mk`). Verified `get_build_var` → project releasekey.
Keys **present** on rom-dev01 (`$HOME/.android-certs/releasekey.pk8`, 2026-09-29).
First artifact `20261001T221102Z-…-cve-hi-releasekeys` was **still AOSP testkey**
(env-only wire). Rebacon after `signing.mk` (2026-10-01T22:12Z).
CVE LAB bacons keep test-keys. First real signed bacon stays **`userdebug`**.
**LAB 2026-10-02:** first signed sideload booted Wi‑Fi OK but Bluetooth
crash-looped — zygote `selinux_android_setcontext` failed with
`seinfo=default` for `com.android.bluetooth`. Cause: APK signed with project
`certs/bluetooth.pk8` while `MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES`
stayed on AOSP `build/make/target/product/security/` (non-`com.google.android.bt`
default in `config.mk`). Fix: `PRODUCT_MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES`
in `signing.mk` (rebacon required; boot-only flash insufficient).
Acceptance:
- [ ] Zip `META-INF/com/android/otacert` ≠ AOSP testkey
- [ ] `ro.build.tags=release-keys` on LAB after **sideload** (boot-only flash insufficient)
(note: first RK sideload still showed `dev-keys` on userdebug — recheck after BT fix)
- [x] Magisk preroot + microG + Wi‑Fi **and Bluetooth** OK on Stage1+combo4+CVE-hi ship pack
(`20261002T085915Z-…-cve-hi-releasekeys`, LAB 2026-10-02: BT ON, crashed 0)
- [x] `plat_mac_permissions.xml` embeds project `bluetooth.x509.pem` (`seinfo=bluetooth`)
- [ ] Minor: LOS SetupWizard after Wi‑Fi connect can bounce to language step
(Wi‑Fi stays connected; non-blocking; note only)
- [ ] Prior test-keys image retained for bisect
(`20261001T201836Z-…-cve-hi-batch2` / `#49`, or hi-batch3 when OK)
Script: `scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh` +
`vendor/sunfish-a16/signing.mk` → `vendor/sunfish-a16/certs/releasekey`
(in-tree copy of `$HOME/.android-certs`; **gitignored** — soong rejects paths
outside `ANDROID_BUILD_TOP`).
### US-S3 — Project AVB key (unlocked device)
Today sunfish often signs with AOSP `testkey_rsa4096` (or rsa2048) and adds
`--flags 3` unless `WITH_AVB=true` (`BoardConfigLineage.mk` /
`BoardConfig-common.mk`). LAB `20261002T085915Z` vbmeta = AOSP testkey_rsa4096
+ flags 3 (not project `avb.pem`).
AXP ships custom AVB keys and documents AVB2; GOLDEN stays **unlocked/orange**.
Clone that *signing* posture, not IOdes lock.
**Bootloader / OEM unlocking (2026-10-02):** relock remains **wont** for this
Magisk track (orange + preroot). Locking with Magisk-patched boot is a brick
risk on Pixel unless the full AVB chain is enforced end-to-end — out of scope.
Keep `flash.locked=0` / `verifiedbootstate=orange`. Prefer leaving **OEM
unlocking** enabled in Developer options as a *best-effort* escape hatch for
future unlock attempts — it is **not** a guarantee of recovery, data
preservation, or rescue from every failure mode (AOSP: unlock ability is
conditional). Do **not** turn it off casually.
Wire: `vendor/sunfish-a16/BoardConfig.mk` overrides `BOARD_AVB_*` →
`vendor/sunfish-a16/certs/avb.pem` + `SHA256_RSA4096`. Leave `WITH_AVB`
unset so `--flags 3` stays (Magisk-friendly).
**What flags 3 means (libavb):** `HASHTREE_DISABLED (1) | VERIFICATION_DISABLED (2)`.
Descriptors are not enforced at boot; this is **AVB signing infrastructure**,
not “AVB-hardened verified boot”. Do not call US-S3 “AVB endurecido” while
flags stay 3. A later stage may measure `WITH_AVB=true` (flags 0) + Magisk
resign — separate acceptance.
Acceptance:
- [x] `BOARD_AVB_KEY_PATH` → project `vendor/sunfish-a16/certs/avb.pem`
- [x] Artifact gates **fail closed** on **target_files/IMAGES** (not OUT make
images): pubkey sha1 == `avb_pkmd.bin` (≠ AOSP `2597c218…`); Flags `3`;
`verify_image --key` vbmeta + `--expected_chain_partition` vbmeta_system;
Magisk boot hash matches vbmeta; `magiskboot cpio test` ≠ 0
- [x] Document clearly: runtime AVB enforce = **off** (flags 3) — not “AVB endurecido”
- [x] Device still boots unlocked; Magisk preroot intact (LAB 2026-10-02 sideload
+ boot/vbmeta reinforce; `magiskd`, `flash.locked=0`, orange)
- [x] No bootloader relock; OEM unlocking left enabled (escape hatch, not guarantee)
### Branding (no rebacon-only)
- [x] Wire `vendor/sunfish-a16/branding.mk` →
`TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro` (full Magisk track, not slim;
next bacon embeds it in `ro.lineage.version`)
- [x] Ship script publishes sibling `AXP-Resurection-Pro-*-sunfish.zip`
(hardlink/copy of Lineage bacon zip). Current artifact renamed the same way
without rebuild.
### US-S4 — Userspace hardening (first attributable pack)
See `docs/009-us-s4-userspace-hardening.md`.
- [x] Stage 0 LAB measure (Enforcing + FBE)
- [x] US-S4-1: strip Openssh/rsync/procmem packages from Lineage `common.mk`
(`apply_009_drop_openssh_packages.py`); keep `init.openssh.rc` (AXP PRO)
- [x] LAB 2026-10-02: ship `20261002T151944Z` sideloaded + reinforced;
see `docs/009-lab-acceptance-20261002-uss4.md`
- [ ] **US-S4-2** (wired, pending bacon): drop Jelly + Gallery2 (AXP PRO
product oracle — Fossify stays); `apply_009_drop_jelly_gallery2.py` +
`NoLineageJelly`; see `docs/009-axp-pro-product-apps-20260324.md`
| Candidate | Priority | Notes |
|-----------|----------|-------|
| Confirm SELinux Enforcing + encryption | **done** | LAB Enforcing + FBE |
| ptrace/Yama | **done US-S5-1** | LAB `162840Z` `CONFIG_SECURITY_YAMA=y` |
| Debug/bugreport surface trim | **done US-S4-1** | AXP PRO product oracle |
| Jelly + Gallery2 debloat | **US-S4-2 wired** | AXP PRO product; Fossify kept |
| mount flags / noexec where stock allows | early | don't break HW |
| hardened_malloc | later | Graphene/AXP; portability cost |
| Broad Graphene framework (scopes, etc.) | later / deferred | high UX cost; not Magisk-clone MVP |
Ship patches under `vendor/sunfish-a16/patches/009-axp-los23-port/hardening/` with README
attribution. One stage per bacon when risk is unclear.
### US-S5 — Kernel CVE mega / more Graphene (on boot-OK pack)
**Do not** flash old `20260918*-axp-kh-cve` / `kh-full` (logo hang).
- [x] CVE clean-offset 429 + hi-batch1–3 on Magisk ship (`151944Z` LAB OK)
- [x] **US-S5-1** LAB OK (`162840Z`): `CONFIG_SECURITY_YAMA=y` +
`CONFIG_SLAB_FREELIST_RANDOM=y` — see
`docs/009-lab-acceptance-20261002-uss5.md`
- [ ] Optional later: `CVE-2022-49980` lock-scope port; Stage1 slab FAIL
retries; `hardened_malloc`
Map updates: `docs/009-kernel-hardening-map.md` + Gate G attribution.
### US-S6 — Optional later
- [ ] Switch default lunch to `lineage_sunfish-user` for ship
- [ ] GPG-sign published SHA sums (AXP does; nice-to-have)
- [ ] LOS23 nightly rebase (vanilla + re-apply Magisk/stage1/keys)
- [ ] Bootloader lock — **wont**
---
## Host paths
| What | Path |
|------|------|
| Platform / OTA / AVB secrets | `$HOME/.android-certs/` on rom-dev01 (2026-09-29); prefer `/srv/rom/secrets/android-certs/` after root relocate |
| Certs tar backup | `/home/android-build/backups/android-certs-20260929T1637Z.tar.gz` |
| Phonesky JKS | `/srv/rom/secrets/phonesky-signing/` |
| microG JKS | `/srv/rom/secrets/microg-signing/` |
| Keygen script (repo) | `scripts/009-generate-release-keys.sh` |
| Signing README (vendor) | `vendor/sunfish-a16/patches/009-axp-los23-port/signing/README.md` |
| GOLDEN props | `/srv/rom/references/axp/golden/2026-09-13/` |
| AXP docs (mirror) | `…/axpos.org/docs/knowledge/{signatures,verifiedboot}/` |
---
## Explicit non-goals
- Bootloader relock / green verified-boot for end users.
- Replacing Phonesky/microG keys with platform keys.
- Committing private keys or passphrases to Git.
- Treating missing lock as Gate G failure.
@@ -0,0 +1,64 @@
# AXP Internet connectivity check providers (from GOLDEN Settings)
Source: `Settings.apk` on GOLDEN `11071JEC203839`
(`ConnectivityCheckPreferenceController` + `connectivity_check_*` arrays),
pulled 2026-09-18.
Artifact: `.local-artifacts/002-golden/20260918-connectivity/`
Preference: **Internet connectivity check**
(`connectivity_check_settings`) — sets global
`captive_portal_{http,https,fallback,other_fallback}_url` and
`captive_portal_mode`.
## GOLDEN UI list (as shipped)
| value | UI label | HTTP check URL |
|------:|----------|----------------|
| 0 | Disabled | *(mode=0; stock Google URLs cleared/disabled)* |
| 1 | Amazon Fire OS (US) | `http://fireoscaptiveportal.com/generate_204` |
| 2 | Cloudflare (US) | `http://cp.cloudflare.com` |
| 3 | **AXP.OS (DE)** | `http://captiveportal.axpos.org/generate_204` |
| 4 | Google (US) [default] | `http://connectivitycheck.gstatic.com/generate_204` |
| 5 | Huawei (CN) | `http://connectivitycheck.platform.hicloud.com/generate_204` |
| 6 | Kuketz (DE) | `http://captiveportal.kuketz.de` |
| 7 | Microsoft Edge (US) | `http://edge-http.microsoft.com/captiveportal/generate_204` |
| 8 | openSUSE (DE) | `http://conncheck.opensuse.org` |
| 9 | Ubuntu (UK) | `http://connectivity-check.ubuntu.com` |
Code also implements (not in GOLDEN `connectivity_check_entries`):
| value | internal name | HTTP check URL |
|------:|---------------|----------------|
| 10 | Xiaomi / MIUI | `http://connect.rom.miui.com/generate_204` |
| 11 | GrapheneOS | `http://connectivitycheck.grapheneos.network/generate_204` |
Note: value `3` constants are named `DIVESTOS_*` in smali but point at
`captiveportal.axpos.org` and are labeled **AXP.OS (DE)** in the UI.
## Clone target for LOS 23.2 (sans axp.os)
**Drop** value 3 / AXP.OS / `*.axpos.org` entirely (spec: no axp.os endpoints).
Ship UI (renumber values contiguously after drop):
| UI label | HTTP | HTTPS |
|----------|------|-------|
| Disabled | — | — |
| Amazon Fire OS (US) | `http://fireoscaptiveportal.com/generate_204` | `https://fireoscaptiveportal.com/generate_204` |
| Cloudflare (US) | `http://cp.cloudflare.com` | `https://cp.cloudflare.com` |
| Google (US) [default] | `http://connectivitycheck.gstatic.com/generate_204` | `https://www.google.com/generate_204` |
| Huawei (CN) | `http://connectivitycheck.platform.hicloud.com/generate_204` | `https://connectivitycheck.platform.hicloud.com/generate_204` |
| Kuketz (DE) | `http://captiveportal.kuketz.de` | `https://captiveportal.kuketz.de` |
| Microsoft Edge (US) | `http://edge-http.microsoft.com/captiveportal/generate_204` | same (AXP uses HTTP URL for https slot) |
| openSUSE (DE) | `http://conncheck.opensuse.org` | `https://conncheck.opensuse.org` |
| Ubuntu (UK) | `http://connectivity-check.ubuntu.com` | `https://connectivity-check.ubuntu.com` |
Optional extras already in GOLDEN controller (owner choice later): Xiaomi, GrapheneOS.
Google default also sets:
- fallback: `http://www.google.com/gen_204`
- other fallback: `http://play.googleapis.com/generate_204`
GrapheneOS (if enabled later) also sets:
- fallback: `http://grapheneos.online/gen_204`
- other: `http://grapheneos.online/generate_204`
+229
View File
@@ -0,0 +1,229 @@
# Auditoría de los fallos de arranque del kernel — 2026-09-26
Revisión de documentación local, scripts, fuentes y artefactos de `rom-dev01`.
No se ha compilado, flasheado, reiniciado ni modificado el servidor o los teléfonos.
Este documento registra evidencias y un punto de reanudación; no cambia requisitos.
## Conclusión
**Hallazgo prioritario posterior: los bisect cambian TARGET_RELEASE de bp4a
a bp2a.** La comparación binaria y el log demuestran que no se está aislando
el hardening. Ver la sección siguiente antes de construir o probar más boots.
Hay errores verificables en la preparación de los experimentos y en su trazabilidad.
Todavía no hay una traza que identifique la causa exacta del fallo de arranque.
El siguiente paso debe recuperar un experimento controlado, no volver a aplicar
todo el hardening/CVE/deblob desde cero.
## Hallazgo principal: cambio accidental de release Android en los bisect
Tras confirmar el dueño que GOODMAKE también arranca, se han desempaquetado
GOODMAKE y el boot mild y extraído sus configuraciones IKCONFIG y ramdisks.
[Evidencia y diff de configuración](009-kernel-boot-image-comparison-20260926.txt).
- GOODMAKE: cabecera con parche `2026-09`; ramdisk declara
`ro.build.version.security_patch=2026-09-01`, SDK completo `36.1`.
- MILD: cabecera con parche `2025-08`; ramdisk declara
`ro.build.version.security_patch=2025-08-05`, SDK completo `36.0`.
- DTB idéntico. Ramdisk diferente, incluyendo bibliotecas y propiedades.
- Configuración del kernel: además de DMESG/SLAB cambian VT/fbcon, watchdog
y PANIC_TIMEOUT (de 0 a 5), entre otros cambios derivados. La base buena
conservaba configuración de depuración y el bloque Kconfig de KernelSU,
aunque CONFIG_KSU estaba desactivado.
Origen confirmado en el servidor:
- `scripts/009-bacon-axp.sh:87` usa `breakfast sunfish userdebug`.
- `vendor/lineage/build/envsetup.sh` hace que breakfast lea
`vendor/lineage/vars/aosp_target_release`, cuyo valor actual es `bp4a`.
- Los bisect fuerzan `lunch lineage_sunfish-bp2a-userdebug`.
- El log `detach-009-bisect-dc-20260921T180802Z.log:53` registra literalmente
`TARGET_RELEASE ("bp4a" -> "bp2a")`.
- `build/release/flag_values/bp2a/RELEASE_PLATFORM_SECURITY_PATCH.textproto`
contiene `2025-08-05`.
- Los dos scripts nuevos del 26 de septiembre, `009-bisect-dmesg-only.sh`
y `009-bisect-slab-only.sh`, también fuerzan **bp2a en la línea 32**.
No se han detenido ni modificado sus procesos desde esta auditoría.
**Defecto demostrado:** los experimentos denominados «solo una/dos opciones»
cambian también la release Android y metadatos de seguridad del boot.
Que GOODMAKE arranque no valida la equivalencia del entorno que los recompila.
**Hipótesis causal prioritaria, aún no confirmada en LAB:** el retroceso del
nivel de parche o su inconsistencia con el sistema instalado puede interferir
con Keymaster y las claves necesarias para montar datos. AOSP documenta
[version binding](https://source.android.com/docs/security/features/keystore/version-binding):
las claves están vinculadas a versiones/niveles de parche y no se pueden
degradar libremente. Los flags AVB que desactivan verificación no constituyen
una prueba de compatibilidad de esas claves. Falta traza o prueba controlada;
no afirmar que esto explique también todos los OTA Stage1 anteriores.
**Corrección del experimento:** recuperar la release y metadatos efectivos
del build bueno; verificar cabecera, propiedades y AVB del resultado antes de
flashear. Cambiar simplemente a bp4a no basta como verificación: el archivo
bp4a actual declara `2025-12-05`, mientras GOODMAKE declara `2026-09-01`, por
lo que hay que localizar/reproducir sus overrides y no inventar una fecha.
Usar salida aislada y empezar con un control recompilado sin hardening nuevo.
No hacer factory reset como método de validación. Después aislar kernel y
módulos sin variar simultáneamente ramdisk, release o metadatos.
## Historial aportado por el dueño después de la revisión
El 26 de septiembre el dueño aporta una conversación previa:
- «Tanto con uno como el otro» el teléfono reinicia en recovery y muestra
«Try again / Factory data reset». El fragmento no identifica inequívocamente
los dos archivos por nombre/hash; no asignar este resultado a mild.
- Tras restaurar el boot GOOD de `20260918T143547Z`, confirma «estoy en el
sistema». No se relata un borrado de datos. Es evidencia de recuperación
con el boot conocido, no una prueba de corrupción permanente de `/data`.
- El agente anterior informa ADB `unauthorized` en recovery; después, ya en
sistema, informa ausencia de `su`, depuración root desactivada y ningún log
útil obtenido. No hay salidas originales que permitan verificar qué rutas
pudo leer: «no pude obtener evidencia» no equivale a «no se generó evidencia».
- La conversación termina proponiendo mild como prueba pendiente. No aporta
una respuesta posterior que confirme su ejecución ni su resultado.
Este historial matiza el registro «nunca recovery/Android»: al menos en los
intentos relatados sí se llega a recovery tras el fallo. No demuestra que el
kernel del arranque normal alcanzara una fase concreta, porque recovery es
un arranque posterior. Preparar acceso diagnóstico en el sistema GOOD antes
de otro ensayo, sin completar Magisk únicamente para obtener logs ni mezclar
su activación con el cambio de kernel. No interpretar el menú de recovery
como una indicación de que sea necesario hacer factory reset.
## 1. Imagen antigua dentro del ensayo nuevo (confirmado por SHA-256)
Raíz de artefactos remota: `/srv/rom/artifacts/009-axp-los23-port/`.
Estos dos archivos tienen el mismo SHA-256, recalculado el 26 de septiembre:
- `20260921T185024Z-bisect-defconfig-only/boot.img`
- `20260918T231654Z-axp-kh-full/boot.img`
SHA-256: `3715f9f20c8823e5a23323792600b5d76d9f25e73eed3492c28f457a751d3c51`.
El script remoto `scripts/009-bisect-defconfig-only.sh` ejecuta `mka bootimage`
y luego busca el primer `target_files_intermediates/.../IMAGES/boot.img`.
Ese segundo archivo pertenece al empaquetado anterior: el ensayo termina
conteniendo dos imágenes con kernels distintos.
El `boot-make.img` del ensayo tiene SHA-256 registrado
`f52405c6b39a14d3357efd59441c9a28128bbce982186b1d86c71b01da8846b1`.
El README sí indica usar ese archivo. No he encontrado en lo revisado un
registro de flasheo con hash que permita confirmar cuál se probó. Por tanto,
la identidad equivocada está demostrada; que se flashease la imagen equivocada,
no. El script posterior `009-bisect-defconfig-mild.sh` ya evita esta copia.
## 2. «Solo defconfig» también cambia código de FORTIFY (confirmado)
El mismo script añade a `lib/string.c` definiciones exportadas de
`__read_overflow`, `__read_overflow2`, `__read_overflow3` y `__write_overflow`,
todas con cuerpo `BUG()`. El log
`/srv/rom/artifacts/detach-009-bisect-dc-20260921T180802Z.log` confirma
`added fortify stubs`.
En las fuentes LOS inspeccionadas, `include/linux/string.h:241` y siguientes
declaran los helpers de lectura con `__compiletime_error`. Proporcionarles
un cuerpo permite resolver símbolos que impedían enlazar, pero no demuestra
que las llamadas sean correctas ni que no se ejecuten. Si se alcanza uno de
esos cuerpos se ejecuta `BUG()`. Falta investigar los sitios de llamada y su
relación con Clang/LTO; no atribuir el bootloop a ellos sin traza.
La conclusión previa «defconfig extras alone are enough to kill boot» es
demasiado fuerte: el ensayo incluye cambios C, varias opciones simultáneas y
la ambigüedad de imágenes anterior. Además cambia `SLAB_HARDENED` de Stage1
por `SLAB_FREELIST_HARDENED`, por lo que tampoco reproduce exactamente su config.
## 3. Se avanzó de etapa sin validar arranque (confirmado documentalmente)
`docs/009-kernel-hardening-map.md` ordena diferir los CVE hasta que Stage1
arranque, pero registra tres builds el 18 de septiembre: Stage1, Stage1 con
unos 500 CVE, y hardening ampliado. Los tres fallaron en LAB según el registro
del 21. Compilar correctamente no cumplía esa condición.
Stage1 ya agrupaba 30 parches y varias opciones; también se documentan restos
de modificaciones SusFS en el árbol inicial. El script de bisect posterior
sí limpia las fuentes hasta `28f9290ae067`; no confundir ambos estados.
## 4. Aplicación de parches y reparaciones no reproducibles
El `apply_stage1.sh` local usa `patch --batch` sin exigir fuzz cero. Ya existe
un caso documentado de aplicación en la función equivocada: `slab/0014` entró
en `is_root_cache` en vez de `slab_equal_or_root`.
El listado sigue incluyendo ese parche. Las correcciones de slab y FORTIFY
se describen en `BUILD-FIXES.md`, pero no están implementadas por el aplicador
local. Ejecutar la receta publicada no reproduce por sí solo el árbol corregido.
Otros defectos visibles del aplicador:
- Si falta un archivo, incrementa `fail` y continúa; al acabar puede devolver
éxito porque el último comando es `echo`.
- Ante fallo elimina `.rej` y `.orig`, perdiendo evidencia; no revierte los
hunks que se hayan aplicado antes del fallo.
- `--dry-run` prueba cada parche sobre el mismo árbol sin aplicar sus
predecesores: no valida fielmente una serie con dependencias.
En el servidor, `scripts/009-cve-compile-fix.sh` intenta seleccionar parches
para excluir por tokens extraídos del mensaje del compilador y registra el
código de salida de su reversión sin exigir que sea cero. Esto no verifica
dependencias ni garantiza que el árbol coincida con la denylist. Son defectos
del método; esta revisión no establece qué rondas concretas los ejercitaron.
## 5. Kernel y módulos: compatibilidad pendiente de demostrar
Los dos scripts de bisect generan y guardan únicamente imágenes de arranque;
sus instrucciones reutilizan la ROM `20260918T143547Z-axp-full` instalada.
No guardan ni indican desplegar un conjunto correspondiente de módulos vendor.
El código actual `include/linux/slub_def.h:120` añade el campo `random` a
`struct kmem_cache` bajo `CONFIG_SLAB_FREELIST_HARDENED`. La `.config` generada
actual contiene esa opción y `CONFIG_MODVERSIONS=y`.
Esto justifica comprobar ABI/CRC de los módulos realmente cargados y los que
van en el ramdisk/vendor antes de interpretar un ensayo de solo boot. No prueba
por sí solo incompatibilidad ni explica necesariamente los primeros tres OTA.
La misma precaución ya consta en `docs/007-susfs-source-audit.md`.
## 6. Faltan observabilidad y continuidad entre sesiones
El logo de Google no identifica por sí solo la fase exacta del bloqueo.
El pstore vacío tampoco demuestra que no hubiera traza: la auditoría de `007`
documenta un error de descifrado de ramoops en LAB. Es evidencia histórica;
no se ha vuelto a medir el estado actual del teléfono en esta revisión.
El mapa local acaba proponiendo el ensayo mild, pero este ya fue compilado:
- Artefacto: `20260921T192533Z-bisect-defconfig-mild/boot-make.img`.
- SHA-256 registrado en su log:
`6ed0351edb762bdf4bfe2dd96d7e4e2fa43ccf0ad497d7fce35ab88cd722f4f2`.
- Estado del proceso: `exit=0`, finalizado el 21 a las 19:25:33 UTC.
- Resultado de arranque: **no encontrado en los registros revisados**.
- Árbol remoto actual: HEAD `28f9290ae067d3e7857126eb6312720dbe9c7c80`;
único archivo modificado según Git: `sunfish_defconfig`, con KSU desactivado,
`SECURITY_DMESG_RESTRICT=y` y `SLAB_FREELIST_HARDENED=y`.
- `.config` actual: FORTIFY e INIT_ON_ALLOC/FREE desactivados. No sustituye
a una configuración archivada de cada build anterior.
## Punto de reanudación propuesto
1. Conservar como referencia el build `20260918T143547Z-axp-full`, cuyo
arranque está confirmado por el dueño en el mapa. Identificar por hash el
boot realmente probado en cada bisect y aclarar el resultado de mild.
2. Reproducir un control sin hardening nuevo con fuentes, herramienta de
compilación, salida aislada y módulos identificados. Validar arranque en LAB.
3. Separar las variables: una opción por ensayo. Tratar FORTIFY más sus
modificaciones C como una investigación propia; no ocultar errores de enlace
bajo la etiqueta «solo config».
4. Para cambios que afecten ABI, comprobar o desplegar un conjunto compatible
de kernel y módulos. Registrar slot, particiones, hashes y resultado.
5. Recuperar diagnóstico del fallo antes de atribuirlo a un subsistema.
No borrar datos, alternar slots o añadir parches como sustituto de evidencia.
6. Congelar cada estado probado: revisión y diff de fuentes, parches y sus
hashes, `.config` efectiva, compilador, imágenes/módulos, log y resultado.
No avanzar a CVE ni deblob hasta aceptar la etapa anterior.
Deblob no es una causa demostrada en estos ensayos: el registro de `009`
lo declara pendiente, y la retirada de IPA se omitió expresamente. No se ha
realizado aquí una auditoría exhaustiva del contenido de todos los payloads.
@@ -0,0 +1,125 @@
Comparación directa de imágenes — 2026-09-26
GOODMAKE: .local-artifacts/009-flash-20260918T143547Z/boot-make.img
MILD: rom-dev01:/srv/rom/artifacts/009-axp-los23-port/20260921T192533Z-bisect-defconfig-mild/boot-make.img
good: SHA256=fcc52ec5c7c077def20ce9d9b9ac3782475e0834817f453e845eb2c1e66d1255
header patch level: 2026-09
ro.build.version.sdk_full=36.1
ro.build.version.security_patch=2026-09-01
ro.build.version.sdk_full=36.1
ro.build.version.security_patch=2026-09-01
kernel: SHA256=5db3af0d7eb76dcd26d7fdbe7dad554d7493073bfa3495f1b49c1aa323ad6b4f
ramdisk.cpio: SHA256=a2e412792fa236a1db3416b6bf7ed374e606f88c714339319759b04f84d90067
dtb: SHA256=2fca15709ff6d9f48fea938952439e8d00c2b998c73e7a216172ae3965b07566
mild: SHA256=6ed0351edb762bdf4bfe2dd96d7e4e2fa43ccf0ad497d7fce35ab88cd722f4f2
header patch level: 2025-08
ro.build.version.sdk_full=36.0
ro.build.version.security_patch=2025-08-05
ro.build.version.sdk_full=36.0
ro.build.version.security_patch=2025-08-05
kernel: SHA256=c7829fa4751643067da5ca6e726d402771c781cfc10771201ab989d73a10cdf8
ramdisk.cpio: SHA256=17d51096168cffb1bb0d93682045203d50e3fc00783f4339e4b79adf0785b3eb
dtb: SHA256=2fca15709ff6d9f48fea938952439e8d00c2b998c73e7a216172ae3965b07566
Configuración extraída de IKCONFIG de cada kernel:
--- GOODMAKE.config
+++ MILD.config
@@ -243,7 +243,7 @@
# CONFIG_SLOB is not set
CONFIG_SLAB_MERGE_DEFAULT=y
# CONFIG_SLAB_FREELIST_RANDOM is not set
-# CONFIG_SLAB_FREELIST_HARDENED is not set
+CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_SLUB_CPU_PARTIAL=y
CONFIG_SYSTEM_DATA_VERIFICATION=y
CONFIG_PROFILING=y
@@ -2282,12 +2282,7 @@
# Character devices
#
CONFIG_TTY=y
-CONFIG_VT=y
-CONFIG_CONSOLE_TRANSLATIONS=y
-CONFIG_VT_CONSOLE=y
-CONFIG_VT_CONSOLE_SLEEP=y
-CONFIG_HW_CONSOLE=y
-CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_VT is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
@@ -3564,16 +3559,6 @@
# CONFIG_BACKLIGHT_ARCXCNN is not set
# CONFIG_VGASTATE is not set
CONFIG_HDMI=y
-
-#
-# Console display driver support
-#
-CONFIG_DUMMY_CONSOLE=y
-CONFIG_DUMMY_CONSOLE_COLUMNS=80
-CONFIG_DUMMY_CONSOLE_ROWS=25
-CONFIG_FRAMEBUFFER_CONSOLE=y
-CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
-CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
@@ -4493,7 +4478,6 @@
#
# Speakup console speech
#
-# CONFIG_SPEAKUP is not set
# CONFIG_STAGING_MEDIA is not set
#
@@ -4780,7 +4764,7 @@
CONFIG_QCOM_MEMORY_DUMP_V2=y
# CONFIG_MSM_DEBUG_LAR_UNLOCK is not set
CONFIG_QCOM_WATCHDOG_V2=y
-# CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC is not set
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QCOM_WDOG_IPI_ENABLE=y
CONFIG_QCOM_SMEM_STATE=y
CONFIG_QCOM_SMP2P=y
@@ -5331,11 +5315,6 @@
CONFIG_ESOC_MDM_DRV=y
CONFIG_ESOC_MDM_DBG_ENG=y
# CONFIG_MDM_DBG_REQ_ENG is not set
-
-#
-# KernelSU
-#
-# CONFIG_KSU is not set
#
# Firmware Drivers
@@ -5651,7 +5630,7 @@
# CONFIG_PANIC_ON_OOPS is not set
CONFIG_PANIC_ON_OOPS_VALUE=0
# CONFIG_PANIC_ON_WARN_DEFAULT_ENABLE is not set
-CONFIG_PANIC_TIMEOUT=0
+CONFIG_PANIC_TIMEOUT=5
CONFIG_SCHED_DEBUG=y
CONFIG_SCHED_INFO=y
# CONFIG_PANIC_ON_SCHED_BUG is not set
@@ -5830,7 +5809,7 @@
# CONFIG_PFT is not set
CONFIG_PFK=y
# CONFIG_PFK_WRAPPED_KEY_SUPPORTED is not set
-# CONFIG_SECURITY_DMESG_RESTRICT is not set
+CONFIG_SECURITY_DMESG_RESTRICT=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
# CONFIG_SECURITY_WRITABLE_HOOKS is not set
@@ -6139,10 +6118,6 @@
CONFIG_MPILIB=y
CONFIG_LIBFDT=y
CONFIG_OID_REGISTRY=y
-CONFIG_FONT_SUPPORT=y
-# CONFIG_FONTS is not set
-CONFIG_FONT_8x8=y
-CONFIG_FONT_8x16=y
# CONFIG_SG_SPLIT is not set
CONFIG_SG_POOL=y
CONFIG_ARCH_HAS_SG_CHAIN=y
+267
View File
@@ -0,0 +1,267 @@
# 009 kernel hardening map (FR-017)
> **Priority finding, 2026-09-26:** Bisect scripts (including the new dmesg-only
> and slab-only scripts) force **bp2a**, whereas the GOOD build used **bp4a**.
> Direct image comparison finds GOODMAKE security patch 2026-09 / SDK 36.1
> versus mild 2025-08 / SDK 36.0. GOODMAKE boots according to the owner.
> These are not isolated defconfig trials; see the audit below before testing.
> **Audit 2026-09-26:** Before resuming builds, read
> [the boot investigation](009-kernel-boot-audit-20260926.md).
> The defconfig-only experiment also added FORTIFY C stubs and its `boot.img`
> is byte-identical to the older full-hardening image; only `boot-make.img`
> represents the new build. Its reported boot failure does not yet isolate
> configuration alone. The later mild build already exists; its LAB boot
> result was not found in the reviewed records.
> Subsequent owner-provided history reports failed attempts rebooting into
> recovery (Try again / Factory data reset), followed by successful system
> boot after restoring GOOD. Thus "never recovery" below is not a universal
> description. That history still presents mild as pending; see the audit.
**Base**: LineageOS 23.2 `sunfish` / `kernel/google/msm-4.14` (non-GKI).
**Not base**: GrapheneOS A13 sunfish tree, random prebuilts, KernelSU.
## Inventory (2026-09-18, `rom-dev01`)
| Item | Value |
|------|--------|
| LOS tree | `/srv/rom/src/lineage/kernel/google/msm-4.14` |
| LOS rev | `28f9290ae067` — *Revert "BACKPORT: module: Do not offer sha224…"* |
| Version | `4.14.357-openela` |
| AXP stack | `/srv/rom/references/axp/kernel_patches` @ `df3cab2155b3` (codeberg mirror) |
| Dry-run worktree | `/srv/rom/artifacts/009-axp-los23-port/kernel-harden-dryrun/` |
| Iodé manifest | `/srv/rom/references/iode/android-manifest` (`v5-staging`) |
### LOS `sunfish_defconfig` already on
`CFI_CLANG`, `SHADOW_CALL_STACK`, `CC_STACKPROTECTOR_STRONG`, `RANDOMIZE_BASE`,
`ARM64_SW_TTBR0_PAN`, `SECURITY_PERF_EVENTS_RESTRICT`, `HARDENED_USERCOPY`(+PAGESPAN),
`BUG_ON_DATA_CORRUPTION`, `SCHED_STACK_END_CHECK`, `# CONFIG_DEVMEM is not set`,
`BPF_JIT_ALWAYS_ON`.
`# CONFIG_UNMAP_KERNEL_AT_EL0 is not set` (kept as LOS).
### LOS defconfig gaps (vs typical Graphene/AH)
Not set in `sunfish_defconfig` (may need Kconfig + patches first):
`FORTIFY_SOURCE`, `INIT_ON_ALLOC_DEFAULT_ON`, `INIT_ON_FREE_DEFAULT_ON`,
`SLAB_FREELIST_RANDOM`, `SLAB_FREELIST_HARDENED`, `SHUFFLE_PAGE_ALLOCATOR`,
`PAGE_POISONING`, `SECURITY_DMESG_RESTRICT`, `DEBUG_LIST`, `REFCOUNT_FULL`.
### AXP `kernel_patches` shape
| Series | 4.14 patches | Role |
|--------|-------------:|------|
| `0001-LinuxIncrementals` | 335 | Version bumps — **skip** (tree already 4.14.357-openela) |
| `0004`–`0006`, `0008-*`, `0009` | ~40 Graphene/AH | Stage-1 candidates (below) |
| `CVE-*` dirs | ~2301 with 4.14 | Mega CVE stage — **later**, after Stage 1 boots |
| Location-related | — | **Skip** (FR-018 policy) |
### IodéOS
`v5-staging` manifest cloned; **no** `sunfish` / `msm-4.14` kernel project in
snippets. Public gitlab device/kernel repos need auth. Treat Iodé as
**userspace privacy** for this device; no extra 4.14 kernel delta found.
## Source priority (owner 2026-09-18)
| Priority | Source | Role |
|----------|--------|------|
| 1 | **AXP** sunfish kernel / build patches | Clone first (max) |
| 2 | **LOS 23.2** msm-4.14 as shipped | Inventory; skip dupes |
| 3 | **IodéOS** (LOS fork) | Diff for extra 4.14-safe hardening/deblob |
| 4 | **GrapheneOS** | Optional: modern techniques / docs; A13 sunfish only if clearly useful |
| — | **DivestOS** standalone | **Do not hunt** (EOL). AXP-inherited commits OK via AXP clone |
## Location policy
**Do not** extra-cap location vs LOS defaults (FR-018). Skip AXP location
lockdown patches; document as skipped. Goal: **precise, fast GPS** like
stock LOS 23.2 — not AXP privacy lockdown. Deblob/CVE stages must not
reintroduce location hardening by accident.
## Sequential apply probe (Graphene/AH only)
Clean worktree @ `28f9290ae067`, patches from AXP `kernel_patches` in Divest-like
order (`4.14` then `ANY`). Result: **OK=30, SKIP_ALREADY=4, FAIL=12**.
Full machine log:
`/srv/rom/artifacts/009-axp-los23-port/kernel-harden-dryrun/apply-sequential-summary.txt`
## Patch ledger
| id | title | source | AXP? | 4.14-safe? | decision | notes |
|----|-------|--------|------|------------|----------|-------|
| KH-INC | LinuxIncrementals 4.14 (335) | AXP `0001` @ df3cab215 | Y | n/a | **skip** | Already 4.14.357-openela |
| KH-004 | Perf events restrict | AXP `0004` ANY/0001 | Y | Y | **skip** | Already in LOS (`SKIP_ALREADY` + defconfig) |
| KH-005 | Deny USB (toggle) | AXP `0005-Graphene-Deny_USB` 4.14/0003 | Y | Y | **stage1 apply** | Clean apply |
| KH-006 | wcnss buffer fixes | AXP `0006` ANY/0001–0003 | Y | ? | **skip/port later** | Wrong path on this tree (`can't find file`) |
| KH-008-as | alloc_size attrs | AXP `0008-…-allocsize` | Y | Y | **stage1 apply** | OK: 0002,0011,0016,0021; FAIL: 0007 (vmalloc.h hunk) |
| KH-008-bug | ksize BUG_ON | AXP `0008-…-bugon` 0010 | Y | Y | **stage1 apply** | OK |
| KH-008-fort | FORTIFY intra-object | AXP `0008-…-fortify` 0004 | Y | Y | **stage1 apply** | OK; then enable `CONFIG_FORTIFY_SOURCE` |
| KH-008-misc | misc Graphene | AXP `0008-…-misc` | Y | Y | **stage1 apply** | OK 0001,0005,0010,0018; SKIP 0021 + ANY/0015 |
| KH-008-rnd | brk/arg randomize | AXP `0008-…-random` | Y | Y | **stage1 apply** | All 4 OK when sequential |
| KH-008-ro | __ro_after_init / sysctl RO | AXP `0008-…-ro` | Y | partial | **stage1 apply** | OK most; FAIL 0007, 0034 (sysctl drift) |
| KH-008-san | page/slab sanitize | AXP `0008-…-sanitize` | Y | Y | **stage1 apply** | All 4 OK sequential |
| KH-008-slab | SLAB_HARDENED | AXP `0008-…-slab` | Y | partial | **stage1 apply** | OK 0006,0014; FAIL 0010,0018 |
| KH-008-un | unused-fn noise | AXP `0008-…-unused` | Y | Y | **stage1 apply** | OK (build hygiene) |
| KH-009 | rfc4941bis IPv6 | AXP `0009` | Y | partial | **skip** | 4.14/0003 already present; ANY/* fail |
| KH-CVE | CVE clean-offset 429 + denylist | AXP `CVE-*` | Y | LAB OK `#47` | **ship partial** | `cve-clean-offset.list` (ex-430−49980); high-offset ≥200 quarantined; `CVE-2022-49980` denylist (udc_lock); see `009-cve-bisect-plan.md` / `009-axp-graphene-remaining.md` |
| KH-LOC | AXP location lockdown | AXP | Y | — | **skip** | FR-018 |
| KH-IODE | Iodé kernel delta | Iodé | — | — | **skip** | No sunfish 4.14 fork found |
| KH-CFG | Hardening defconfig | LOS+AXP | — | Y | **stage1 after patches** | Flip gaps that Kconfig allows post-patch |
| KH-US-S5-1 | Yama + SLAB_FREELIST_RANDOM | Graphene/AXP class | Y | Y | **apply** | `apply_uss5_1_defconfig.sh`; LSM already listed yama |
### Stage 1 apply list (exact files)
From host `$KP=/srv/rom/references/axp/kernel_patches` — **only** these (order
matters):
```
0005-Graphene-Deny_USB/4.14/0003.patch
0008-Graphene-Kernel_Hardening-allocsize/4.14/0002.patch
0008-Graphene-Kernel_Hardening-allocsize/4.14/0011.patch
0008-Graphene-Kernel_Hardening-allocsize/4.14/0016.patch
0008-Graphene-Kernel_Hardening-allocsize/4.14/0021.patch
0008-Graphene-Kernel_Hardening-bugon/4.14/0010.patch
0008-Graphene-Kernel_Hardening-fortify/4.14/0004.patch
0008-Graphene-Kernel_Hardening-misc/4.14/0001.patch
0008-Graphene-Kernel_Hardening-misc/4.14/0005.patch
0008-Graphene-Kernel_Hardening-misc/4.14/0010.patch
0008-Graphene-Kernel_Hardening-misc/4.14/0018.patch
0008-Graphene-Kernel_Hardening-random/4.14/0002.patch
0008-Graphene-Kernel_Hardening-random/4.14/0007.patch
0008-Graphene-Kernel_Hardening-random/4.14/0012.patch
0008-Graphene-Kernel_Hardening-random/4.14/0017.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0002.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0012.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0017.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0028.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0030.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0032.patch
0008-Graphene-Kernel_Hardening-ro/4.14/0038.patch
0008-Graphene-Kernel_Hardening-sanitize/4.14/0005.patch
0008-Graphene-Kernel_Hardening-sanitize/4.14/0009.patch
0008-Graphene-Kernel_Hardening-sanitize/4.14/0015.patch
0008-Graphene-Kernel_Hardening-sanitize/4.14/0017.patch
0008-Graphene-Kernel_Hardening-slab/4.14/0006.patch
0008-Graphene-Kernel_Hardening-slab/4.14/0014.patch
0008-Graphene-Kernel_Hardening-unused/4.14/0001.patch
0008-Graphene-Kernel_Hardening-unused/4.14/0002.patch
```
Then defconfig (only options that exist after patches): at least
`CONFIG_FORTIFY_SOURCE=y`; evaluate `INIT_ON_*` / slab freelist / dmesg restrict
against Kconfig `depends on` before flipping.
## Applied / skipped summary
- **Applied (2026-09-18, live tree):** Stage 1 — 30 AXP Graphene/AH patches via
`vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1.sh` onto
`/srv/rom/src/lineage/kernel/google/msm-4.14` (Stage1 alone ~28 paths;
tree also had prior Magisk-track dirt: KSU/SusFS `# CONFIG_* is not set`
stubs + residual SusFS-touched fs/overlayfs etc. — not Stage1).
- **Defconfig (same day):** `CONFIG_FORTIFY_SOURCE=y`,
`CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y`, `CONFIG_INIT_ON_FREE_DEFAULT_ON=y`,
`CONFIG_SECURITY_DMESG_RESTRICT=y`, `CONFIG_SLAB_HARDENED=y`.
- Skipped: location lockdown (policy); Divest-as-hunt (policy); LinuxIncrementals;
KH-004 (already present); KH-006 wcnss (path miss); KH-009 ANY; Iodé kernel
(none found); CVE mega (in progress via `cve_checker`)
- Failed probe (port later): allocsize/0007, ro/0007+0034, slab/0010+0018
- **Build fix 2026-09-18:** Stage1 `slab/0014` misfuzzed into `is_root_cache`
(`return p == s` with no `p`) — reverted that stub. Intent was
`slab_equal_or_root`; LOS already has `return s == p` there.
- **Build fix 2026-09-18b:** `CONFIG_FORTIFY_SOURCE=y` + Clang LTO left
`__read_overflow*` undefined in modules (qcacld/techpack) — added `BUG()`
stubs + `EXPORT_SYMBOL` in `lib/string.c`.
- **Bacon OK (Stage1 only):**
`/srv/rom/artifacts/009-axp-los23-port/20260918T180359Z-axp-kh-stage1/`
- **CVE bacon OK:**
`/srv/rom/artifacts/009-axp-los23-port/20260918T215034Z-axp-kh-cve/`
Stage1 + ~500 CVE patches that compile. ~34 incomplete series on denylist
(`cve-stage/denylist.txt`) — skipped, not silently dropped without a log.
Magisk preroot present; KernelSU absent.
- **More complete bacon OK:**
`/srv/rom/artifacts/009-axp-los23-port/20260918T231654Z-axp-kh-full/`
AXP `hardenDefconfig` on the CVE tree (only options this Kconfig has).
Left on: `IO_URING`, `USERFAULTFD`, WireGuard, IPA. Magisk preroot OK.
- **Not done:** userspace Deblob.sh (Widevine/IMS/CNE). Kernel deblob in AXP
is mostly IPA removal — skipped so mobile data keeps working.
- **Flash target (LAB 2026-09-21):** back on
`20260918T143547Z-axp-full` (pre-kernel-hardening). Owner confirmed boot OK.
- **Boot-fail (LAB 2026-09-21):** all three KH artifacts hang at Google logo
(bootloader splash, never recovery/Android):
- `231654Z-axp-kh-full`, `215034Z-axp-kh-cve`, `180359Z-axp-kh-stage1`
- Stage1 **`boot-make.img` (no Magisk)** also hangs → kernel Stage1 (+
Stage1 defconfig extras), not Magisk packaging.
- OTA `payload` boot == Magisk artifact boot; OTA `dtbo` identical across
good vs Stage1. Loose `dtbo.img`/`vbmeta.img` in artifact dirs ≠ payload
(do not flash those sueltos). Embedded boot `dtb` identical all four.
- `/sys/fs/pstore` empty after failed boots (no ramoops).
- **Bisect 2026-09-21:** `20260921T185024Z-bisect-defconfig-only`
(clean LOS + FORTIFY/INIT_ON_*/DMESG/SLAB_FREELIST_HARDENED, no Graphene
patches) → LAB **bootloop** / try-again recovery.
- **LAB 2026-09-28:** Clang FORTIFY nested-`strlen` fix (`strlcpy`/`strcpy` →
`__underlying_strlen`). Family `full` + **combo4+FORTIFY** boot. Magisk bacon:
`apply_combo4_fortify.sh` / `009-bacon-axp-combo4-fortify.sh`.
- **LAB 2026-09-29 (ship kernel pack):** Stage1 (30) + combo4 + Clang FORTIFY
Magisk bacon `20260929T123401Z-axp-stage1-combo4` — boots, Wi‑Fi CRC OK,
Deny USB sysctl present. Attribution: `docs/009-gate-g-kernel-attribution.md`.
Apply: `apply_stage1_combo4.sh`. Bacon: `009-bacon-axp-stage1-combo4.sh`.
- **CVE re-try 2026-09-29:** full `cve-strict` (503) = LAB Google logo hang.
Bisect: `docs/009-cve-bisect-plan.md` (`cve-bisect-h1/h2.list`).
- **Bisect 2026-09-21 mild:** `DMESG_RESTRICT` + `SLAB_FREELIST_HARDENED`
only → same failure.
- **Control 2026-09-26:** `20260918T143547Z` **boot-make.img** (no Magisk,
no extra CONFIG) flashed onto Magisk system → **boots OK**.
- **Audit 2026-09-26 (owner second opinion):** bisect scripts wrongly used
`lunch …-bp2a-userdebug` while good bacon uses `breakfast` → `bp4a`.
Mild/defconfig-only boots carry `security_patch=2025-08-05` / SDK 36.0 vs
GOODMAKE `2026-09-01` / 36.1; ramdisk and other kernel options also differ.
**Invalidates** “DMESG/SLAB alone kill boot”. bp2a one-config builds aborted.
See `docs/009-kernel-boot-audit-20260926.md`.
- **Control ksync 2026-09-26:** new breakfast/bp4a kernel (no Stage1 CONFIG)
injected into GOODMAKE ramdisk → LAB **boots OK** (`20260926T161646Z-bisect-ksync-control`).
Method validated. Next: same inject path, one CONFIG at a time (DMESG then SLAB).
**Future tests:** always `breakfast`/bp4a + inject kernel into GOODMAKE ramdisk;
never bp2a-only bootimage as a “config” experiment. Lessons also in agent
personal store `009-kernel-bisect-lessons.md`.
Deblob still deferred (GPS: no AXP location lockdown).
## CVE / hardening / deblob policy (owner 2026-09-18 evening)
- **Source of truth**: AXP method (`kernel_patches` + `cve_checker`), applied onto
LOS23 `msm-4.14` — not inventing our own CVE hunt.
- **Skip if already fixed** in LOS tree (patcher “already applied” / reverse
hunks). LOS23 is newer than AXP22; many CVEs may already be present via
`4.14.357-openela`. Hardening/deblob are where AXP usually still wins.
- **Loose 4.19**: when enabled, the patcher may also try patches filed under
`4.19/` (and with extreme matching, even `^5.x`/`^6.x`) on a `4.14` tree.
First pass with loose ON applied ~185 non-4.14 patches and **broke the
build** (incomplete binder/bpf/f2fs series). **OFF for CVE apply** (exact
`4.14` + `ANY` only). Re-evaluate later if needed.
- **On failure**: do not silent-drop — attempt fix / adapt / re-apply; only
then document skip with reason (AXP `Fix_CVE_Patchers` style).
- **Deblob**: AXP deblob pass after CVE/hardening inventory (separate from CVE
IDs); Lineage rarely matches AXP here.
- **Flash**: owner on return; agent does not need to flash for this phase.
## 2026 catalog refresh (started 2026-09-19)
CIP `kernel-sec` cloned at `/srv/rom/references/cip-kernel-sec` @ `025be706`
(2026-09-19). Scrape vs AXP catalog `df3cab215` (2025-10-30): **7086** CVEs
not in that list (6317× CVE-2026, 769× CVE-2025). **None** have a `4.14`
backport link; one has `4.19`. The rest are mainline / 5.10+.
kernel.org `linux-stable` `v4.14.*` stops at **4.14.336**. Nothing newer to
incremental-apply; the LOS tree is already `4.14.357-openela`.
Android bulletins (new URL `/docs/security/bulletin/YYYY/YYYY-MM-01`, which
the jar scraper does not accept) have **no** `git.kernel.org` links. Kernel
rows point at `android.googlesource.com/kernel/common`: 2025-12 (44),
2026-03 (36), 2026-06 (2), 2026-09 (10). Links saved in
`/srv/rom/artifacts/009-axp-los23-port/cve-2026/asb-links.txt`.
Download + checker (read-only on the live tree; does not reset the flashed
kernel) is detached as tmux `009-cve-2026`. One mainline patch per new CVE
lands in `/srv/rom/references/axp/kernel_patches_2026/`. Apply is **not**
started: 2026 patches are 6.x and the last loose pass broke 4.14.
+22
View File
@@ -0,0 +1,22 @@
# LAB acceptance note — 20261002T103558Z (AVB infra ship)
Device: `11231JEC207477`
Artifact: `20261002T103558Z-axp-stage1-combo4-cve-hi-releasekeys`
Ship zip twin: `AXP-Resurection-Pro-23.2-20261002-sunfish.zip` (= lineage zip bytes)
Owner smoke 2026-10-02: no errors reported after sideload + boot/vbmeta reinforce.
| Gate | Status | Notes |
|------|--------|-------|
| A Boot/HW | pass (owner) | Boots home; Wi‑Fi/BT/call smoke OK per owner |
| B Google stack | pass (prior+owner) | microG/Phonesky path unchanged this bacon |
| C Toggles | pass (prior) | Spoof / call-rec not regressed this bacon |
| D Magisk | pass | `magiskd` present; preroot; unlocked orange |
| E Banking | deferred | Not MVP |
| F Usability | pass (prior) | Bundled apps / WebView LOS |
| G Hardening | partial | Release-keys + AVB **signing** infra (flags 3); US-S4 stage0 measure done; US-S4-1 code pending next content bacon |
`ro.lineage.version` on this flash is still `23.2-20261002-UNOFFICIAL-sunfish`
(pre-branding.mk). Next content bacon picks up
`…-UNOFFICIAL-AXP-Resurection-Pro-…`.
+27
View File
@@ -0,0 +1,27 @@
# LAB acceptance — 20261002T151944Z (US-S4-1 + Pro branding)
Device: `11231JEC207477`
Artifact: `20261002T151944Z-axp-stage1-combo4-cve-hi-releasekeys`
Ship: `AXP-Resurection-Pro-23.2-20261002-sunfish.zip`
Sideload + boot/vbmeta/dtbo reinforce 2026-10-02 evening. Owner/ChatGPT artifact
gates + LAB post-boot checks below.
| Gate | Status | Notes |
|------|--------|-------|
| A Boot/HW | pass | `sys.boot_completed=1`, slot `_a` |
| B Google stack | pass (prior) | microG/Phonesky unchanged this bacon |
| C Toggles | pass (prior) | Not regressed |
| D Magisk | pass | `magiskd` pid present; TF Magisk boot reinforced |
| E Banking | deferred | |
| F Usability | pass (prior) | |
| G Hardening | pass (this pack) | AVB **signing** infra flags=3; US-S4-1 OpenSSH/rsync/procmem absent; `init.openssh.rc` present (AXP) |
Props:
- `ro.lineage.version=23.2-20261002-UNOFFICIAL-AXP-Resurection-Pro-sunfish`
- `ro.axp.resurection.track=pro`
- `ro.boot.flash.locked=0`
- no `/product/bin/sshd`, no `/product/bin/rsync`
- `/product/etc/init/init.openssh.rc` present
Prior AVB-only ship note: `docs/009-lab-acceptance-20261002-avb.md` (`103558Z`).
+27
View File
@@ -0,0 +1,27 @@
# LAB acceptance — 20261002T162840Z (US-S5-1 Yama + SLAB_FREELIST_RANDOM)
Device: `11231JEC207477`
Artifact: `20261002T162840Z-axp-stage1-combo4-cve-hi-releasekeys`
Ship: `AXP-Resurection-Pro-23.2-20261002-sunfish.zip`
Sideload Pro zip + reinforce Magisk `boot`/`dtbo`/`vbmeta`/`vbmeta_system` on
active slot `_b` (2026-10-02 evening).
| Gate | Status | Notes |
|------|--------|-------|
| A Boot/HW | pass | `sys.boot_completed=1`, slot `_b` |
| B Google stack | pass (prior) | microG/Phonesky unchanged this bacon |
| C Toggles | pass (prior) | Not regressed |
| D Magisk | pass | `magiskd` present; TF Magisk boot reinforced |
| E Banking | deferred | PIF/Vector BL spoof — owner config, not this pack |
| F Usability | pass (prior) | |
| G Hardening | pass (this pack) | US-S5-1: `CONFIG_SECURITY_YAMA=y`, `CONFIG_SLAB_FREELIST_RANDOM=y`, LSM lists `yama`; US-S4-1 OpenSSH still absent; `init.openssh.rc` present |
Props:
- `ro.lineage.version=23.2-20261002-UNOFFICIAL-AXP-Resurection-Pro-sunfish`
- `ro.axp.resurection.track=pro`
- `ro.boot.flash.locked=0`, `verifiedbootstate=orange`
- `/proc/config.gz`: YAMA + SLAB_FREELIST_RANDOM + `LSM=…,yama,…`
Recover baseline: `20261002T085915Z-axp-stage1-combo4-cve-hi-releasekeys` (README).
Prior US-S4 LAB: `docs/009-lab-acceptance-20261002-uss4.md` (`151944Z`).
+22
View File
@@ -0,0 +1,22 @@
# 009 localization / location: AXP vs LOS (deferred — FR-018)
**Status**: Look later — **not** an MVP acceptance gate.
Owner note (2026-09-17): AXP has historically felt aggressive on localization /
location; want a deliberate compare vs stock LineageOS before cloning that
policy blindly.
## What to compare later
| Area | AXP / GOLDEN | Stock LOS 23 | Decision TBD |
|------|--------------|--------------|--------------|
| Network location / NLP backends | microG BeaconDB path (see `004`/`005` docs) | LOS + microG defaults | |
| Location permission defaults | | | |
| Privacy toggles / sensors | | | |
| Extra lockdown patches | | | |
| UX: “how much it capea” | Owner complaint baseline | | |
## Until then
Do **not** invent extra location lockdown beyond what the rest of the AXP clone
already implies. Matrix row = `deferred`.
+111
View File
@@ -0,0 +1,111 @@
# 009 Magisk preinstall runbook (AXP / GOLDEN class)
**Device roles**: GOLDEN `11071JEC203839` = `adb` **read-only** oracle.
LAB `11231JEC207477` = flash/test only. **Never** flash or Magisk-change GOLDEN.
**Evidence**: `/srv/rom/artifacts/009-axp-los23-port/`
**Contract**: `specs/009-axp-los23-port/contracts/magisk-preinstall.md`
## Ship policy (owner 2026-09-17)
- **Clone AXP first (mandatory):** Magisk packaging = EXTENDROM
`EXTENDROM_PREROOT_BOOT` method. Copy that flow; do **not** invent alternate
Magisk install paths. Only diverge if LOS23 / Magisk version breaks the
AXP/EXTENDROM path, and document why.
- **Magisk clean**: ship Magisk preroot only — **do not** bake GOLDEN modules
(`playintegrityfix`, `teesim`, `hosts`, `flag-secure-disabler`,
`zygisk_vector`, …). Owner installs modules later if wanted.
- **Manager UI**: stock **Magisk** upstream (not Magia branding).
- **Dormant until activation (AXP Pro):** after flash, Magisk is pre-rooted in
boot but root must stay inactive until the user opens Magisk and completes
Additional Setup. Skipping setup ⇒ no `su` / no Magisk binaries in the OS
(undetectable). Do **not** bake Magisk as `PRODUCT_PACKAGE`; do **not**
sideload the full Magisk APK before measuring virgin LAB state.
- **Build host**: LAB/GOLDEN USB **not required** to compile. Phones only for
measurement (GOLDEN `adb`) or flash validation (LAB).
## GOLDEN measurement (2026-09-17, live `adb`)
| Item | Value |
|------|--------|
| Build | `22.2-2026.003-sunfish-pro` / `BP1A.250505.005` |
| Magisk binary | **29.0:MAGISK:R** (`29000`) at `/system_ext/bin/magisk` |
| Also present | `/system_ext/bin/magiskpolicy` |
| Runtime | `/data/adb/magisk/` full toolkit + `stub.apk`; `magisk.db` |
| Settings (sqlite via `magisk --sqlite`) | `zygisk=1`, `denylist=1`, `mnt_ns=2`, `bootloop=0` |
| Manager UI | Magisk app **hidden/renamed** (random pkg `oq.aq.cjn`, `versionName=1.0`) — AXP brands this UI as **Magia** in docs/screenshots; package name is not stable after “Hide the Magisk app” |
| Modules seen | `flag-secure-disabler`, `hosts`, `playintegrityfix`, `teesim`, `zygisk_vector` |
| Secure toggles | `allow_signature_fake=1`, `extendrom_call_recording=1` |
**Important:** GOLDEN is already **fully activated**. The virgin
“preinstalled but not fully activated” UX is the **first-boot / additional
setup** Magisk flow after an AXP-style preroot image (see AXP setup guide +
EXTENDROM FAQ: “Requires Additional Setup”). Measure that class from AXP docs +
EXTENDROM `PREINITDEVICE=userdata` behaviour — do not wipe GOLDEN to re-see it.
## How AXP / EXTENDROM builds Magisk into the ROM
Attribute: **sfX-android `android_vendor_extendrom`** + Magisk APK.
1. `EXTENDROM_PREROOT_BOOT=true` — Magisk is **not** a normal PRODUCT_PACKAGE;
it patches **boot / init_boot** at image build time.
2. Magisk APK (v22+) is unzipped under `out/.magisk/`; `boot_patch.sh` runs via
`vendor/extendrom/config/magisk/patch.sh`.
3. Releasetools inject: patch under
`config/magisk/{lineage|axp}/A{version}/0001-build_make_tools_releasetools_common.py`
hooks `common.py` `_BuildBootableImage` / prebuilt boot.
4. Patcher env of note: `KEEPVERITY=true`, `KEEPFORCEENCRYPT=true`,
`PREINITDEVICE=userdata`, `BOOTMODE=false`.
5. Post-flash **virgin**: stub may appear; `/system_ext/bin/magisk` and `su`
appear only **after** user activation (AXP FAQ “Revert Magisk activation”).
Measure virgin before opening Magisk. GOLDEN already activated is not virgin.
Host reference tree: `/srv/rom/references/extendrom/` on `rom-dev01`.
## Enable Magisk on next bacon (009)
```bash
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
export OUT_DIR=/srv/rom/src/lineage/out # or /srv/rom/out-009 for parallel
export SUNFISH_A16_MAGISK_PREROOT=1
# Copy staged runtime into OUT (or re-run prepare):
# rsync -a /srv/rom/prebuilts/magisk/runtime/ "$OUT_DIR/.magisk/"
# or: MAGISK_STAGE_DIR=$OUT_DIR/.magisk vendor/sunfish-a16/magisk/prepare_magisk.sh
```
Hook is already in `build/make/tools/releasetools/common.py` but **no-ops** unless
`SUNFISH_A16_MAGISK_PREROOT=1` (008 in-flight stays safe).
1. Clean-flash LAB with Magisk-preroot `009` images (wipe `/data` if LAB had
Magisk activated before).
2. **Virgin check (before opening Magisk):**
`adb shell 'which su; which magisk; ps -A | grep magiskd'` — expect no `su`,
no Magisk binaries (AXP dormant). Record under `…/magisk/pre-activation.md`.
3. Open Magisk stub → download/install full Magisk if prompted → complete
**Additional setup** → reboot (AXP setup guide).
4. Confirm `magisk -v` matches pin (target: newest workable stable; host aims
at **v30.7** unless regression).
5. Enable Zygisk + DenyList as needed.
6. For DenyList smoke (MVP): hide a **non-bank test package** before first open.
7. Banking DenyList workflow = **deferred** (US3 / Gate E).
## Optional KernelSU recycle (FR-016)
Paths under `/srv/rom/artifacts/007-*/` — debug/reference only. **Not ship.**
Never gate Magisk acceptance on KSU.
**Hard rule for Magisk LAB:** `sunfish_defconfig` must keep `# CONFIG_KSU is not
set` (and SusFS off). Magisk + in-kernel ReSukiSU/KSU on the same boot causes
app `failed to attach` / `start timeout` and hung `su`.
**Full AXP-clone bacon (detached):**
`/srv/rom/artifacts/009-axp-los23-port/scripts/009-bacon-axp.sh`
session `009-bacon-axp` — Magisk preroot + noksu + Phonesky exact-alarm +
WMS FLAG_SECURE (no `ro.debuggable` gate). After zip sideload, flash
artifact `boot.img` (TF IMAGES) to both slots.
## Staged known-good images (FR-013)
Keep prior LAB flashables + SHA-256 under
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/` before each risky Magisk or
hardening stage.
+34
View File
@@ -0,0 +1,34 @@
# AXP Private DNS providers (from GOLDEN Settings)
Source: `Settings.apk` on GOLDEN `11071JEC203839`
(`22.2-2026.003-sunfish-pro`), pulled 2026-09-18.
Artifact: `.local-artifacts/002-golden/20260918-private-dns/Settings.apk`
Stock AOSP/LOS radios: Off / Automatic / Provider hostname.
AXP adds the following preset radios (hostname → DoT provider):
| key (resource) | UI label (en) | hostname |
|----------------|---------------|----------|
| `adguard` | AdGuard (CT) | `dns.adguard.com` |
| `appliedprivacy` | AppliedPrivacy (AT) | `dot1.applied-privacy.net` |
| `cira` | CIRA (CA) | `protected.canadianshield.cira.ca` |
| `cleanbrowsing` | CleanBrowsing (US) | `security-filter-dns.cleanbrowsing.org` |
| `cloudflare` | Cloudflare (US) | `security.cloudflare-dns.com` |
| `controld` | Control D (CA) | `p2.freedns.controld.com` |
| `cznic` | CZ.NIC (CZ) | `odvr.nic.cz` |
| `dnssb` | DNS.SB (DE) | `dot.sb` |
| `dnszero` | DNS0 (FR) | `zero.dns0.eu` |
| `google` | Google (US) | `dns.google` |
| `mullvad` | Mullvad (SE) | `base.dns.mullvad.net` |
| `quadnine` | Quad9 (CH) | `dns.quad9.net` |
| `restena` | Restena (LU) | `kaitain.restena.lu` |
| `switch` | SWITCH (CH) | `dns.switch.ch` |
| `uncensoreddns` | UncensoredDNS (DK) | `anycast.censurfridns.dk` |
Plus stock: Off, Opportunistic (Automatic), custom Provider hostname.
AXP setup docs recommend Quad9 (`dns.quad9.net`) and DNS0 (`zero.dns0.eu`).
Clone target for LOS 23.2: extend `PrivateDnsModeDialogPreference` +
`private_dns_mode_dialog.xml` + strings to match this set (no `axp.os`
branding).
+30
View File
@@ -0,0 +1,30 @@
# 009 — Sideload system package updates (FR-019)
Owner cadence: refresh bundled **system / privileged** packages about every
**2–3 months** without a full ROM reflash. **No** AXP OTA / external repos.
Status: **stub** — fill before final ship (task T046a). Packaging of the
shipping image MUST already preserve update eligibility.
## In scope
| Component | Intended update path | Signing constraint |
|-----------|----------------------|--------------------|
| microG GmsCore / companions | `adb install -r` / recovery sideload of upstream APK | **PRESIGNED** / same signer as shipped |
| Magisk | Magisk app self-update and/or documented boot re-patch | Manager + boot image must stay matched |
| Project Phonesky | Sideload project-signed APK | **Same project key** across releases |
| Other bundled system apps | Sideload matching-signer APK (or recovery zip if priv-app needs it) | Same identity as image |
## Out of scope
- Network OTA / AXP update servers
- Full `bacon` + clean flash for routine APK bumps (still OK for kernel /
framework / Magisk boot changes when required)
## Checklist before claiming FR-019 done
- [ ] Each row above has a concrete command/runbook step
- [ ] `pm install` / PackageManager does not reject for signature mismatch
- [ ] Privileged permissions still grant after update (or re-flash privapp XML noted)
- [ ] Magisk version after update matches kernel/boot expectation
- [ ] Record new versions in `docs/009-version-pins.md`
+52
View File
@@ -0,0 +1,52 @@
# US-S4 — Userspace hardening (first stage)
Status 2026-10-02. No bacon solely for this doc.
## Stage 0 — LAB baseline (measure, closed)
On LAB `11231JEC207477` after AVB ship `20261002T103558Z` (+ boot/vbmeta reinforce):
| Check | Result |
|-------|--------|
| SELinux | Enforcing |
| Encryption | `ro.crypto.state=encrypted`, `ro.crypto.type=file` |
| Verified boot | orange / `flash.locked=0` (unlocked Magisk track) |
| `security.perf_harden` | 1 (already) |
| Yama / `ptrace_scope` | **done** (US-S5-1 `162840Z`) — `CONFIG_SECURITY_YAMA=y` |
## Stage 1 — US-S4-1 drop product OpenSSH / rsync / procmem
**Oracle**: `AXP.OS-22.2-20260324-PRO-sunfish.zip` product (+ system) images —
no `sshd`/`ssh`/`rsync`/`procmem` binaries; orphan `init.openssh.rc` only.
**Shipped in tree**: `vendor/sunfish-a16/hardening.mk` +
`patches/009-axp-los23-port/hardening/README.md`.
Removes Lineage product OpenSSH binaries + `rsync`, `procmem` (DEBUG).
**Keeps** `init.openssh.rc` (AXP PRO still has it). Admin path: adb + Magisk.
LAB verify after flash: no `/product/bin/sshd`, no `/product/bin/rsync`,
`/product/etc/init/init.openssh.rc` present, Magisk still up.
## Stage 2 — US-S4-2 drop Jelly + Gallery2
**Oracle**: AXP PRO product has neither; Fossify on system_ext. See
`docs/009-axp-pro-product-apps-20260324.md`.
Wired: `apply_009_drop_jelly_gallery2.py`, `NoLineageJelly` phony override,
hardening.mk filter, bacon gate. Pending next bacon + LAB.
## Later candidates (one per bacon when risk unclear)
1. Mount / noexec where stock sunfish already allows (verify HW first)
2. Package-visibility / query hardening (high app-compat risk → later)
3. Further debug-tool trim (`adb_root` only if LAB proves unused)
Promote from `docs/hardening-backlog.md` only with upstream attribution.
## Out of scope here
- `WITH_AVB=true` / flags 0
- Bootloader relock
- `lineage_sunfish-user` default lunch (US-S6)
- Kernel Yama / more CVE (US-S5)
+24
View File
@@ -0,0 +1,24 @@
# 009 version pins
Policy: newest stable (or newest tagged pre-release upstream ships) that works
on sunfish / LOS 23.2 — except Phonesky = project artifact, WebView = LOS
default. Refresh with `git fetch --tags` / release APIs before long builds.
Never ship from stale laptop caches.
| component | version_name | version_code | upstream_ref | policy | verified_on | notes |
|-----------|--------------|--------------|--------------|--------|-------------|-------|
| VanillaBaseline | lineage-23.2 sunfish | | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish` | los_default | 2026-09-14 | Known-good vanilla artifact on host |
| microG GmsCore | v0.3.16.252432 | | microG official (matches GitHub latest 2026-09-17) | newest_stable | 2026-09-16 staged | `/srv/rom/prebuilts/microg/` |
| GSF Proxy | v0.1.0 | | microG official | newest_stable | 2026-09-16 staged | |
| Phonesky | 52.9.22-24 | | project patched host artifact | project_phonesky | 2026-09-14 | `/srv/rom/prebuilts/phonesky/`; never Git |
| Magisk | v30.7 | | https://github.com/topjohnwu/Magisk/releases/tag/v30.7 | newest_stable | 2026-09-17 | Staged APK; GOLDEN oracle **29.0** layout only |
| Kernel sunfish | 4.14.357-openela | | `kernel/google/msm-4.14` @ `28f9290ae067` | los_default + FR-017 stage1 | 2026-09-18 | AXP `kernel_patches` @ `df3cab2155b3`; see `docs/009-kernel-hardening-map.md` |
| Aurora Store | present on GOLDEN | | `com.aurora.store` | newest_stable | 2026-09-17 seen | Stage APK later |
| F-Droid | present on GOLDEN | | `org.fdroid.fdroid` | newest_stable | 2026-09-17 seen | |
| Fossify Gallery | present on GOLDEN | | `org.fossify.gallery` | newest_stable | 2026-09-17 seen | |
| Seedvault | present on GOLDEN | | `com.stevesoltys.seedvault` | los_default / newest | 2026-09-17 seen | Often LOS-provides |
| Island | deferred | | `com.oasisfeng.island.fdroid` | | | US3 deferred |
## Host artifact hashes (copy from host VERSIONS when refreshed)
See `/srv/rom/prebuilts/*/VERSIONS.txt` on `rom-dev01`.
+1 -1
View File
@@ -39,7 +39,7 @@ feature. Signing/AVB, userspace hardening, and kernel hardening remain `011`–
| Magisk / preroot | Magisk (**Magia** UI on GOLDEN, 29.0) | **Substitute:** KernelSU-Next (+ SusFS) / evaluate ReSukiSU | substitute | Captured in `002`; do **not** fix GOLDEN; project root = `007` on LAB | | Magisk / preroot | Magisk (**Magia** UI on GOLDEN, 29.0) | **Substitute:** KernelSU-Next (+ SusFS) / evaluate ReSukiSU | substitute | Captured in `002`; do **not** fix GOLDEN; project root = `007` on LAB |
| DenyList / umount for banks | Magia DenyList (pkg list in `docs/002-golden-inventory.md`) | KernelSU app profiles + verified umount | todo | `007` re-runs `006`; GOLDEN DenyList captured `002` | | DenyList / umount for banks | Magia DenyList (pkg list in `docs/002-golden-inventory.md`) | KernelSU app profiles + verified umount | todo | `007` re-runs `006`; GOLDEN DenyList captured `002` |
| Insular work profile | AXP docs say Insular; **GOLDEN measures Island** (`com.oasisfeng.island.fdroid` 6.4.2) | Banking workflow parity (Island or equivalent) | todo | `006`; capture in `002` | | Insular work profile | AXP docs say Insular; **GOLDEN measures Island** (`com.oasisfeng.island.fdroid` 6.4.2) | Banking workflow parity (Island or equivalent) | todo | `006`; capture in `002` |
| Yuh in Insular/Island | Historically worked; **FAIL since ~weeks** (HA / “certificate invalid”; KeyMint/TEE errors in `002` logs) | Hard-gate Compatibility MVP (rootless in `006`); **Revolut not a hard-gate** | partial | `002` evidence; LAB may need SusFS+TrickyStore-class stack (`reference-sources.md` §8); never GOLDEN | | Yuh in Insular/Island | Was FAIL ~weeks (HA); **PASS again 2026-10-02 on GOLDEN** with Vector **bootloader spoofer** (`002`) | Hard-gate Compatibility MVP (rootless in `006`); **Revolut not a hard-gate** | partial | `002`; Gate E on LAB = DenyList + PIF/teesim + Vector BL spoof (sunfish-coherent); never flash GOLDEN |
| Swissquote in Insular/Island | **PASS** on GOLDEN (owner report, DenyList) | Hard-gate Compatibility MVP (rootless in `006`) | partial | `002`; re-test in `006`/`007`/`011` | | Swissquote in Insular/Island | **PASS** on GOLDEN (owner report, DenyList) | Hard-gate Compatibility MVP (rootless in `006`) | partial | `002`; re-test in `006`/`007`/`011` |
| Unrestricted call recording | Yes | Yes — native ROM on Android 16 | todo | AXP/EXTENDROM/iodé as references only; re-validate on LOS 23.2; `008` | | Unrestricted call recording | Yes | Yes — native ROM on Android 16 | todo | AXP/EXTENDROM/iodé as references only; re-validate on LOS 23.2; `008` |
| DNS blocker / privacy DNS | Yes | Wanted; exact UX TBD | decide | `010` | | DNS blocker / privacy DNS | Yes | Wanted; exact UX TBD | decide | `010` |
+21 -16
View File
@@ -55,31 +55,32 @@ Status: `candidate` | `investigate` | `accepted→spec` | `rejected` | `los-prov
The kernel base remains the LineageOS 23.2-compatible sunfish kernel. The kernel base remains the LineageOS 23.2-compatible sunfish kernel.
**Priority (009 / owner 2026-09-18):** AXP clone first → LOS inventory →
IodéOS diff → optional Graphene (modern techniques / selective A13).
**Do not** separately mine DivestOS EOL. See `docs/009-kernel-hardening-map.md`.
```text ```text
┌─ historical GOS sunfish ┌─ AXP sunfish (max priority clone)
│ │
LOS23/A16 sunfish 4.14 ←─┼─ AXP own + DivestOS LOS23/A16 sunfish 4.14 ←─┼─ LOS23 already-present
OUR BASE │ OUR BASE │
├─ modern GOS techniques ├─ IodéOS (LOS fork, hardened/deblobbed)
│ │
└─ original upstream fixes └─ Graphene optional (modern / selective A13)
``` ```
**Not:** GrapheneOS Android 13 (or any old GrapheneOS) kernel tree as our base. **Not:** GrapheneOS Android 13 (or any old GrapheneOS) kernel tree as our base.
**Not:** DivestOS as a hunt target (EOL).
Candidate hardening comes from four classes: Candidate hardening comes from:
1. **Historical GrapheneOS sunfish** — device-specific patches/configuration known 1. **AXP.OS sunfish** — primary; audit actual patch stack on `rom-dev01`.
to have run on sunfish (real commits, not only `.config`). 2. **LineageOS 23.2 msm-4.14** — what is already applied.
2. **AXP.OS / DivestOS sunfish** — audit the actual patch stack; distinguish 3. **IodéOS** — diff vs LOS for extra hardening/deblob worth porting.
AXP-owned changes from inherited changes. 4. **GrapheneOS** — optional: modern Pixel techniques adaptable to 4.14;
3. **Modern GrapheneOS** — use current kernel hardening as a source of historical sunfish A13 only if clearly still valuable.
techniques/security properties; adapt/backport only where feasible on 5. **Original upstream** — when a patch derives from Linux/AOSP common,
non-GKI Linux 4.14; do not assume current Pixel kernel patches cherry-pick prefer upstream form when practical.
cleanly.
4. **Original upstream** — where a GrapheneOS/AXP/Divest patch derives from AOSP,
Android common, Linux stable, or another upstream source, prefer the
appropriate original upstream fix when practical.
For each candidate record: For each candidate record:
@@ -96,6 +97,10 @@ For each candidate record:
- test result; - test result;
- final decision. - final decision.
**Location:** do not extra-cap vs LOS (owner GPS UX); skip AXP location
lockdown in this track unless revisited later.
## Much later / evaluate ## Much later / evaluate
| Idea | Notes | Status | | Idea | Notes | Status |
+152
View File
@@ -0,0 +1,152 @@
#!/bin/bash
# 009 bacon — AXP Magisk preroot + combo4+Clang-FORTIFY kernel (LAB-verified pack).
# Does NOT apply Graphene Stage1 mega-pack (not boot-OK yet on LOS23/Clang).
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-combo4-fortify.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-combo4-fortify $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
APPLY=vendor/sunfish-a16/patches/009-kernel-hardening/apply_combo4_fortify.sh
test -x "$APPLY" || chmod +x "$APPLY"
bash "$APPLY" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: $DEF still enables KernelSU" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
if ! grep -q "^CONFIG_${k}=y" "$DEF"; then
echo "FAIL: missing CONFIG_${k}=y" >&2
exit 1
fi
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
echo "OK: combo4+FORTIFY markers + nested-strlen fix present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
echo "OK: cleared prior Image for kernel rebuild"
# FLAG_SECURE + Settings UX (idempotent)
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
if ! "$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM; then
echo "FAIL: Phonesky missing USE_EXACT_ALARM" >&2
exit 1
fi
echo "OK: Phonesky has USE_EXACT_ALARM"
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-combo4-fortify
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
echo "boot_source=target_files_IMAGES"
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
echo "WARN: no TF IMAGES/boot.img — using Make boot"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
# Magisk present, KernelSU absent
MB=out/.magisk/magiskboot
if [ -x "$MB" ]; then
rm -rf "$DEST/boot-check" && mkdir -p "$DEST/boot-check"
if "$MB" unpack "$DEST/boot.img" "$DEST/boot-check" >/dev/null 2>&1; then
if [ -f "$DEST/boot-check/ramdisk.cpio" ]; then
if "$MB" cpio "$DEST/boot-check/ramdisk.cpio" "exists overlay.d/sbin/magisk64.xz" \
|| "$MB" cpio "$DEST/boot-check/ramdisk.cpio" "exists overlay.d/sbin/magisk.xz"; then
echo "OK: Magisk overlay in boot.img"
else
echo "WARN: Magisk overlay not detected in boot.img ramdisk"
fi
fi
else
echo "WARN: magiskboot unpack failed (host SIGILL flake possible); skip TF check"
fi
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU strings in boot.img" >&2
exit 1
fi
echo "OK: no KernelSU strings in boot.img"
{
echo "artifact=$DEST"
echo "kernel_base=$BASE"
echo "hardening=combo4+clang-fortify (nested __underlying_strlen)"
echo "magisk=preroot EXTENDROM-class v30.7"
echo "stage1_graphene=NOT_INCLUDED (not boot-OK on LOS23 yet)"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
@@ -0,0 +1,142 @@
#!/bin/bash
# 009 bacon — Stage1+combo4+FORTIFY + CVE bisect half (h1|h2).
# Usage on rom-dev01: CVE_BISECT=h1 bash scripts/009-bacon-axp-stage1-combo4-cve-bisect.sh
set -e
BISECT="${CVE_BISECT:?set CVE_BISECT=h1 or h2}"
case "$BISECT" in h1|h2) ;; *) echo "CVE_BISECT must be h1 or h2" >&2; exit 2 ;; esac
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-${BISECT}.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve-${BISECT} $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST="$KH/cve-bisect-${BISECT}.list"
test -f "$LIST"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST"
bash "$KH/apply_cve_strict.sh" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
echo "OK: Stage1+combo4+FORTIFY + CVE-${BISECT} markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-${BISECT}
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
{
echo "artifact=$DEST"
echo "bisect=$BISECT"
echo "list=$LIST"
echo "hardening=Stage1+combo4+FORTIFY + CVE-${BISECT}"
echo "signing=test-keys"
echo "flash=fastboot flash boot boot.img (preferred for bisect); sideload zip if Wi-Fi breaks"
echo "recover=fastboot flash boot from 20260929T123401Z-axp-stage1-combo4"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
@@ -0,0 +1,142 @@
#!/bin/bash
# 009 bacon — Stage1+combo4+FORTIFY + CVE clean-offset allowlist (no huge patch offsets).
# Does not include cve-high-offset-ge200.txt. test-keys. Do not flash until boot-tested.
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-clean.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve-clean $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST="$KH/cve-clean-offset.list"
test -f "$LIST"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST"
# Strict quality gates (defaults in apply_cve_strict.sh)
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean-offset markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-clean
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
{
echo "artifact=$DEST"
echo "list=$LIST (cve-strict minus high-offset>=200 minus denylist)"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean-offset"
echo "signing=test-keys"
echo "flash=fastboot flash boot ONLY after owner OK; recover=Stage1 boot.img"
echo "note=do not flash full 503 CVE zip"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
@@ -0,0 +1,155 @@
#!/bin/bash
# 009 bacon — Stage1+combo4+FORTIFY + CVE clean-offset + high-offset batch1 (≤10).
# Batch1: intentional CVE_MAX_ABS_OFFSET=9999 after spot-check (fuzz=0). test-keys.
# Do not flash until boot-tested. Recover = last clean BOOT_OK boot.img.
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-hi-batch1.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve-hi-batch1 $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST_CLEAN="$KH/cve-clean-offset.list"
LIST_HI="$KH/cve-high-offset-batch1.list"
test -f "$LIST_CLEAN"
test -f "$LIST_HI"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST_CLEAN"
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
echo "=== high-offset batch1 (elevated abs-offset gate; fuzz still ≤1) ==="
export CVE_STRICT_LIST="$LIST_HI"
export CVE_MAX_ABS_OFFSET=9999
bash "$KH/apply_cve_strict.sh" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
# batch1 markers (spot-checked)
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
grep -q "sched_release_group" "$LIVE/kernel/sched/core.c"
grep -q "fwnode_handle_put(tcpci->tcpc.fwnode)" "$LIVE/drivers/usb/typec/tcpm/tcpci.c"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1 markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-hi-batch1
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
{
echo "artifact=$DEST"
echo "list_clean=$LIST_CLEAN (429)"
echo "list_hi=$LIST_HI (7; dropped CVE-2023-3777 missing nft_chain_is_bound)"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1"
echo "cve_max_abs_offset_hi=9999"
echo "signing=test-keys"
echo "flash=fastboot flash boot ONLY after owner OK; recover=cve-clean BOOT_OK"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
+160
View File
@@ -0,0 +1,160 @@
#!/bin/bash
# 009 bacon — Stage1+combo4+FORTIFY + CVE clean + hi-batch1 + hi-batch2.
# Elevated CVE_MAX_ABS_OFFSET=9999 for high-offset lists. test-keys.
# Recover = hi-batch1 BOOT_OK boot.img (or cve-clean).
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-hi-batch2.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve-hi-batch2 $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST_CLEAN="$KH/cve-clean-offset.list"
LIST_HI1="$KH/cve-high-offset-batch1.list"
LIST_HI2="$KH/cve-high-offset-batch2.list"
test -f "$LIST_CLEAN"
test -f "$LIST_HI1"
test -f "$LIST_HI2"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST_CLEAN"
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
echo "=== high-offset batch1 (prior LAB OK) ==="
export CVE_STRICT_LIST="$LIST_HI1"
export CVE_MAX_ABS_OFFSET=9999
bash "$KH/apply_cve_strict.sh" "$LIVE"
echo "=== high-offset batch2 ==="
export CVE_STRICT_LIST="$LIST_HI2"
export CVE_MAX_ABS_OFFSET=9999
bash "$KH/apply_cve_strict.sh" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2 markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-hi-batch2
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
{
echo "artifact=$DEST"
echo "list_clean=$LIST_CLEAN (429)"
echo "list_hi1=$LIST_HI1"
echo "list_hi2=$LIST_HI2"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2"
echo "cve_max_abs_offset_hi=9999"
echo "signing=test-keys"
echo "flash=fastboot flash boot; recover=hi-batch1 BOOT_OK"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
+162
View File
@@ -0,0 +1,162 @@
#!/bin/bash
# 009 bacon — Stage1+combo4+FORTIFY + CVE clean + hi-batch1+2+3.
# Elevated CVE_MAX_ABS_OFFSET=9999 for high-offset lists. test-keys.
# Recover = hi-batch2 BOOT_OK boot.img.
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-hi-batch3.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve-hi-batch3 $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST_CLEAN="$KH/cve-clean-offset.list"
LIST_HI1="$KH/cve-high-offset-batch1.list"
LIST_HI2="$KH/cve-high-offset-batch2.list"
LIST_HI3="$KH/cve-high-offset-batch3.list"
test -f "$LIST_CLEAN"
test -f "$LIST_HI1"
test -f "$LIST_HI2"
test -f "$LIST_HI3"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST_CLEAN"
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
export CVE_MAX_ABS_OFFSET=9999
for L in "$LIST_HI1" "$LIST_HI2" "$LIST_HI3"; do
echo "=== high-offset $(basename "$L") ==="
export CVE_STRICT_LIST="$L"
bash "$KH/apply_cve_strict.sh" "$LIVE"
done
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-hi-batch3
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
{
echo "artifact=$DEST"
echo "list_clean=$LIST_CLEAN (429)"
echo "list_hi1=$LIST_HI1"
echo "list_hi2=$LIST_HI2"
echo "list_hi3=$LIST_HI3"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3"
echo "cve_max_abs_offset_hi=9999"
echo "signing=test-keys"
echo "flash=fastboot flash boot; recover=hi-batch2 BOOT_OK"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
# Force clean exit even if tee gets SIGPIPE on detach wrap
exit 0
+380
View File
@@ -0,0 +1,380 @@
#!/bin/bash
# 009 bacon — ship pack with project release keys (US-S2).
# Stage1+combo4+FORTIFY + CVE clean + hi-batch1+2+3 + PRODUCT_DEFAULT_DEV_CERTIFICATE.
# Requires $HOME/.android-certs/releasekey.{pk8,x509.pem}. userdebug.
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-hi-releasekeys.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-cve-hi-releasekeys $(date -Is) ==="
CERT_DIR="${ANDROID_CERTS_DIR:-$HOME/.android-certs}"
if [[ ! -f "$CERT_DIR/releasekey.pk8" || ! -f "$CERT_DIR/releasekey.x509.pem" ]]; then
echo "FAIL: missing $CERT_DIR/releasekey.{pk8,x509.pem}" >&2
exit 1
fi
export PRODUCT_DEFAULT_DEV_CERTIFICATE="$CERT_DIR/releasekey"
echo "OK: PRODUCT_DEFAULT_DEV_CERTIFICATE=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST_CLEAN="$KH/cve-clean-offset.list"
LIST_HI1="$KH/cve-high-offset-batch1.list"
LIST_HI2="$KH/cve-high-offset-batch2.list"
LIST_HI3="$KH/cve-high-offset-batch3.list"
test -f "$LIST_CLEAN"
test -f "$LIST_HI1"
test -f "$LIST_HI2"
test -f "$LIST_HI3"
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
# US-S5-1 — Yama + SLAB_FREELIST_RANDOM (Graphene/AXP defconfig gaps)
chmod +x "$KH/apply_uss5_1_defconfig.sh" 2>/dev/null || true
bash "$KH/apply_uss5_1_defconfig.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST_CLEAN"
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
export CVE_MAX_ABS_OFFSET=9999
for L in "$LIST_HI1" "$LIST_HI2" "$LIST_HI3"; do
echo "=== high-offset $(basename "$L") ==="
export CVE_STRICT_LIST="$L"
bash "$KH/apply_cve_strict.sh" "$LIVE"
done
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED \
SECURITY_YAMA SLAB_FREELIST_RANDOM; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_unofficial_build_id.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_openssh_packages.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_jelly_gallery2.py
# Host prebuilts → vendor/sunfish-a16/prebuilt (rsync --delete must not wipe these).
mkdir -p vendor/sunfish-a16/prebuilt
link_prebuilt() {
local name="$1" src="$2"
local dst="vendor/sunfish-a16/prebuilt/$name"
ln -sfn "$src" "$dst"
test -e "$dst" || { echo "FAIL: missing prebuilt $dst -> $src" >&2; exit 1; }
}
link_prebuilt Phonesky.apk /srv/rom/prebuilts/phonesky/Phonesky.apk
link_prebuilt GmsCore.apk /srv/rom/prebuilts/microg/GmsCore.apk
link_prebuilt GsfProxy.apk /srv/rom/prebuilts/microg/GsfProxy.apk
link_prebuilt AuroraStore.apk /srv/rom/prebuilts/axp-apps/AuroraStore.apk
link_prebuilt Droidify.apk /srv/rom/prebuilts/axp-apps/Droidify.apk
link_prebuilt FossifyGallery.apk /srv/rom/prebuilts/axp-apps/FossifyGallery.apk
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions vendor/sunfish-a16/prebuilt/Phonesky.apk 2>/dev/null | grep -q USE_EXACT_ALARM
# Early hook so LINEAGE_VERSION includes AXP-Resurection-Pro (before version.mk).
mkdir -p vendor/extra
ln -sfn ../sunfish-a16/extra-product.mk vendor/extra/product.mk
test -f vendor/extra/product.mk
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
# re-export after envsetup (some trees clear it)
export PRODUCT_DEFAULT_DEV_CERTIFICATE="$CERT_DIR/releasekey"
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
BT_SEPOL=$(get_build_var MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES)
echo "MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES=$BT_SEPOL"
case "$BT_SEPOL" in
*vendor/sunfish-a16/certs*) ;;
*) echo "FAIL: BT sepolicy certs still AOSP default ($BT_SEPOL)" >&2; exit 1 ;;
esac
AVB_KEY=$(get_build_var BOARD_AVB_KEY_PATH)
AVB_ALG=$(get_build_var BOARD_AVB_ALGORITHM)
echo "BOARD_AVB_KEY_PATH=$AVB_KEY"
echo "BOARD_AVB_ALGORITHM=$AVB_ALG"
case "$AVB_KEY" in
*vendor/sunfish-a16/certs/avb.pem) ;;
*) echo "FAIL: AVB key not project certs/avb.pem ($AVB_KEY)" >&2; exit 1 ;;
esac
[[ "$AVB_ALG" == "SHA256_RSA4096" ]] || { echo "FAIL: AVB alg=$AVB_ALG" >&2; exit 1; }
echo "=== mka bacon $(date -Is) signing=$PRODUCT_DEFAULT_DEV_CERTIFICATE ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
# Post-ship gates: drop pipefail. Early-exit awk/head + large pipes otherwise
# yield exit=141 (SIGPIPE) and skip Magisk/AVB/US-S4 checks.
set +o pipefail
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-hi-releasekeys
mkdir -p "$DEST"
# Ship pair = target_files IMAGES (Magisk boot + matching vbmeta). OUT/*.img can be
# stock make images that do NOT match the OTA payload — do not prefer those.
TF_IMG=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -type d -path '*/IMAGES' 2>/dev/null | head -1)
[[ -n "${TF_IMG:-}" && -d "$TF_IMG" ]] || { echo "FAIL: missing target_files IMAGES" >&2; exit 1; }
TF_IMG=$(readlink -f "$TF_IMG")
for f in boot.img vbmeta.img vbmeta_system.img dtbo.img; do
[[ -f "$TF_IMG/$f" ]] || { echo "FAIL: missing $TF_IMG/$f" >&2; exit 1; }
cp -v "$TF_IMG/$f" "$DEST/"
done
# Large partition images: hardlink into DEST so verify_image stays repeatable
# after OUT is cleaned (same FS). Fallback: absolute symlink, then full copy.
for f in vendor.img product.img system.img system_ext.img; do
[[ -f "$TF_IMG/$f" ]] || continue
rm -f "$DEST/$f"
if cp -al "$TF_IMG/$f" "$DEST/$f" 2>/dev/null; then
echo "hardlink $f -> $DEST/$f"
elif ln -sfn "$TF_IMG/$f" "$DEST/$f"; then
echo "symlink-abs $f -> $TF_IMG/$f"
else
cp -v "$TF_IMG/$f" "$DEST/$f"
fi
done
# Keep make OUT boot for forensics only (often stock / non-Magisk).
cp -v "$PROD/boot.img" "$DEST/boot-make-out.img" 2>/dev/null || true
cp -v "$PROD"/lineage-*-UNOFFICIAL*.zip "$DEST/" 2>/dev/null || \
cp -v "$PROD"/lineage-*.zip "$DEST/" 2>/dev/null || true
# Publish AXP Resurection Pro ship name (hardlink/copy). Lineage bacon.mk still
# emits lineage-*.zip; do not rebuild solely to rename.
ZIP_LINEAGE=$(ls "$DEST"/lineage-*.zip 2>/dev/null | head -1 || true)
if [[ -n "${ZIP_LINEAGE:-}" ]]; then
# lineage-23.2-YYYYMMDD-UNOFFICIAL[-AXP-Resurection[-Pro]]-sunfish.zip
# → AXP-Resurection-Pro-23.2-YYYYMMDD-sunfish.zip
ZIP_BASE=$(basename "$ZIP_LINEAGE")
ZIP_AXP=$(echo "$ZIP_BASE" | sed -E \
's/^lineage-([0-9]+\.[0-9]+)-([0-9]{8})(-[0-9]{6})?-UNOFFICIAL(-AXP-Resurection(-Pro)?)?-sunfish\.zip$/AXP-Resurection-Pro-\1-\2\3-sunfish.zip/')
if [[ "$ZIP_AXP" == "$ZIP_BASE" ]]; then
ZIP_AXP="AXP-Resurection-Pro-${ZIP_BASE#lineage-}"
fi
ln -f "$ZIP_LINEAGE" "$DEST/$ZIP_AXP" 2>/dev/null || cp -v "$ZIP_LINEAGE" "$DEST/$ZIP_AXP"
echo "ship_zip=$ZIP_AXP (from $ZIP_BASE)"
fi
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
# Avoid pipefail SIGPIPE: never `strings | grep` on large boot.img.
_BOOT_STR=$(mktemp)
strings "$DEST/boot.img" >"$_BOOT_STR"
if grep -iE 'KernelSU|CONFIG_KSU_SUSFS' "$_BOOT_STR" >/dev/null; then
rm -f "$_BOOT_STR"
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
rm -f "$_BOOT_STR"
# Magisk preroot must be present on the ship boot (magiskboot cpio test: 0=stock, 1=Magisk).
MAGISKBOOT="$ANDROID_BUILD_TOP/out/.magisk/magiskboot"
[[ -x "$MAGISKBOOT" ]] || { echo "FAIL: missing $MAGISKBOOT" >&2; exit 1; }
MB_TMP=$(mktemp -d)
cp "$DEST/boot.img" "$MB_TMP/boot.img"
set +e
( cd "$MB_TMP" && "$MAGISKBOOT" unpack boot.img >/dev/null )
( cd "$MB_TMP" && "$MAGISKBOOT" cpio ramdisk.cpio test )
MB_EC=$?
set -e
rm -rf "$MB_TMP"
# magiskboot cpio test: 0=stock, non-zero (usually 1) = Magisk present
[[ "$MB_EC" -ne 0 ]] || { echo "FAIL: ship boot is stock (magiskboot cpio test=$MB_EC); expected Magisk" >&2; exit 1; }
echo "magiskboot_cpio_test=$MB_EC (non-zero => Magisk present)"
ZIP=$(ls "$DEST"/lineage-*-UNOFFICIAL*.zip 2>/dev/null | head -1 || true)
if [[ -n "$ZIP" ]]; then
unzip -p "$ZIP" META-INF/com/android/otacert 2>/dev/null | openssl x509 -noout -subject 2>/dev/null \
| tee "$DEST/otacert-subject.txt" || true
if grep -qi 'Test Keys\|Android\|AOSP' "$DEST/otacert-subject.txt" 2>/dev/null; then
echo "WARN: otacert subject looks like test/AOSP — check keys wire" | tee -a "$DEST/README.txt"
fi
fi
# US-S3 artifact gates — fail closed (signing infrastructure; NOT runtime AVB harden).
# flags=3 => HASHTREE_DISABLED|VERIFICATION_DISABLED (libavb); Magisk-friendly unlocked LAB.
AVBTOOL="$ANDROID_BUILD_TOP/out/host/linux-x86/bin/avbtool"
AVB_PEM="$ANDROID_BUILD_TOP/vendor/sunfish-a16/certs/avb.pem"
AVB_PKMD="$ANDROID_BUILD_TOP/vendor/sunfish-a16/certs/avb_pkmd.bin"
[[ -x "$AVBTOOL" ]] || { echo "FAIL: missing $AVBTOOL" >&2; exit 1; }
[[ -f "$DEST/vbmeta.img" ]] || { echo "FAIL: missing $DEST/vbmeta.img" >&2; exit 1; }
[[ -f "$DEST/boot.img" ]] || { echo "FAIL: missing $DEST/boot.img" >&2; exit 1; }
[[ -f "$AVB_PEM" ]] || { echo "FAIL: missing $AVB_PEM" >&2; exit 1; }
if [[ ! -f "$AVB_PKMD" ]]; then
"$AVBTOOL" extract_public_key --key "$AVB_PEM" --output "$AVB_PKMD"
fi
"$AVBTOOL" info_image --image "$DEST/vbmeta.img" | tee "$DEST/vbmeta-info.txt"
EXP=$(sha1sum "$AVB_PKMD" | awk '{print $1}')
GOT=$(awk '/^Public key \(sha1\):/{print $NF; exit}' "$DEST/vbmeta-info.txt")
FLAGS=$(awk '/^Flags:/{print $2; exit}' "$DEST/vbmeta-info.txt")
echo "avb_pubkey_sha1 artifact=$GOT project=$EXP flags=$FLAGS"
[[ -n "$GOT" ]] || { echo "FAIL: could not parse vbmeta public key sha1" >&2; exit 1; }
[[ "$GOT" == "$EXP" ]] || { echo "FAIL: vbmeta pubkey != project avb_pkmd.bin" >&2; exit 1; }
[[ "$FLAGS" == "3" ]] || { echo "FAIL: expected vbmeta Flags 3 (Magisk track), got '$FLAGS'" >&2; exit 1; }
# Cryptographic check against TF sibling images in $DEST (hardlinked/copied).
# Magisk boot footer is Algorithm NONE — chain trust is top-level vbmeta + project key.
# Does NOT mean the device will enforce AVB at boot (flags 3 disables that).
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image vbmeta.img --key "$AVB_PEM" \
--expected_chain_partition "vbmeta_system:1:$AVB_PKMD"
) >"$DEST/vbmeta-verify.txt" 2>&1
ec_v=$?
set -e
cat "$DEST/vbmeta-verify.txt"
[[ "$ec_v" -eq 0 ]] || { echo "FAIL: avbtool verify_image vbmeta.img (TF Magisk boot chain)" >&2; exit 1; }
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image vbmeta_system.img --key "$AVB_PEM"
) >"$DEST/vbmeta_system-verify.txt" 2>&1
ec_s=$?
set -e
cat "$DEST/vbmeta_system-verify.txt"
[[ "$ec_s" -eq 0 ]] || { echo "FAIL: avbtool verify_image vbmeta_system.img" >&2; exit 1; }
# Boot footer integrity (unsigned Magisk footer is OK); do not require --key match.
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image boot.img
) >"$DEST/boot-verify.txt" 2>&1
ec_b=$?
set -e
cat "$DEST/boot-verify.txt"
[[ "$ec_b" -eq 0 ]] || { echo "FAIL: avbtool verify_image boot.img footer/hash" >&2; exit 1; }
# US-S4-1 — AXP PRO product surface (binaries gone; init.openssh.rc kept).
SIMG="$ANDROID_BUILD_TOP/out/host/linux-x86/bin/simg2img"
[[ -x "$SIMG" ]] || { echo "FAIL: missing $SIMG" >&2; exit 1; }
[[ -e "$DEST/product.img" ]] || { echo "FAIL: missing $DEST/product.img" >&2; exit 1; }
PROD_RAW=$(mktemp /tmp/axp-product-XXXX.img)
"$SIMG" "$DEST/product.img" "$PROD_RAW"
for p in /bin/sshd /bin/ssh /bin/scp /bin/sftp /bin/rsync /bin/start-ssh; do
if debugfs -R "stat $p" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4 expected absent $p on product.img" >&2
exit 1
fi
done
if ! debugfs -R "stat /etc/init/init.openssh.rc" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4 expected init.openssh.rc present (AXP surface)" >&2
exit 1
fi
echo "US-S4-1 product surface OK (no ssh/rsync bins; init.openssh.rc present)"
# US-S4-2 — AXP PRO: no Jelly / Gallery2 *apps* on product (Fossify stays).
# Note: LOCAL_OVERRIDES removes Gallery2.apk but stale JNI symlink dirs under
# product/app/Gallery2/lib can remain from older out/ — gate on the APK.
for p in /app/Jelly/Jelly.apk /app/Gallery2/Gallery2.apk; do
if debugfs -R "stat $p" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4-2 expected absent $p on product.img" >&2
exit 1
fi
done
# Directory-only Gallery2 leftover (jni symlinks) is not a shipped app — warn.
if debugfs -R "stat /app/Gallery2" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
echo "WARN: US-S4-2 /app/Gallery2 dir still present (likely stale jni symlinks; no APK)"
fi
if ! debugfs -R "stat /app/FossifyGallery" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4-2 expected FossifyGallery present" >&2
exit 1
fi
rm -f "$PROD_RAW"
echo "US-S4-2 product surface OK (no Jelly/Gallery2 APK; FossifyGallery present)"
{
echo "artifact=$DEST"
echo "list_clean=$LIST_CLEAN (429)"
echo "list_hi=batch1+2+3"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 + US-S4-1-no-openssh + US-S4-2-no-jelly-gallery2 + US-S5-1-yama-slabrand"
echo "branding=AXP-Resurection-Pro"
echo "signing=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
echo "avb_infra=$AVB_KEY alg=$AVB_ALG flags=3 (HASHTREE_DISABLED|VERIFICATION_DISABLED)"
echo "avb_runtime_enforce=no (unlocked Magisk track; not AVB-hardened boot)"
echo "avb_pubkey_sha1=$GOT"
echo "magiskboot_cpio_test=$MB_EC"
echo "ship_images=target_files/IMAGES (not OUT make images)"
echo "ship_zip_brand=AXP-Resurection-Pro-*-sunfish.zip (sibling of lineage-*.zip; not slim)"
echo "flash=sideload AXP-Resurection-Pro zip (or lineage twin); boot-only insufficient"
echo "recover=20261002T085915Z-axp-stage1-combo4-cve-hi-releasekeys"
echo "bootloader_lock=wont"
echo "oem_unlock=leave_enabled_escape_hatch_not_guaranteed_recovery"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make-out.img vbmeta.img vbmeta_system.img dtbo.img \
lineage-*.zip AXP-Resurection-Pro-*.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "FLASH_READY $DEST"
echo "=== DONE $DEST $(date -Is) ==="
exit 0
+164
View File
@@ -0,0 +1,164 @@
#!/bin/bash
# 009 bacon — Stage1 + combo4+FORTIFY + CVE-strict (AXP cve_checker allowlist).
# Intentionally test-keys (isolate CVE vs signing). Flash only after LAB boot/Wi‑Fi.
# Do NOT flash old 20260918 *-axp-kh-cve/full (pre-FORTIFY hang).
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
# Isolate: no release keys on this CVE iteration
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4-cve $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
KH=vendor/sunfish-a16/patches/009-kernel-hardening
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1_combo4_cve.sh "$KH"/apply_stage1.sh \
"$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4_cve.sh" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: $DEF still enables KernelSU" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
if ! grep -q "^CONFIG_${k}=y" "$DEF"; then
echo "FAIL: missing CONFIG_${k}=y" >&2
exit 1
fi
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
echo "OK: Stage1 + combo4+FORTIFY + CVE markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
echo "OK: cleared prior Image for kernel rebuild"
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
if ! "$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM; then
echo "FAIL: Phonesky missing USE_EXACT_ALARM" >&2
exit 1
fi
echo "OK: Phonesky has USE_EXACT_ALARM"
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
# Force test-keys for this CVE pack (signing is a later stage)
unset PRODUCT_DEFAULT_DEV_CERTIFICATE || true
echo "OK: signing=test-keys (CVE isolation)"
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
echo "boot_source=target_files_IMAGES"
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
echo "WARN: no TF IMAGES/boot.img — using Make boot"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
if [ -n "$KCRC" ] && [ -n "$MCRC" ] && [ "$KCRC" = "$MCRC" ]; then
echo "OK: wlan.ko CRC matches kernel (Wi-Fi pack coherent)"
else
echo "WARN: wlan.ko CRC mismatch or unreadable — check before flash"
fi
fi
MB=out/.magisk/magiskboot
if [ -x "$MB" ]; then
rm -rf "$DEST/boot-check" && mkdir -p "$DEST/boot-check"
if ! "$MB" unpack "$DEST/boot.img" "$DEST/boot-check" >/dev/null 2>&1; then
echo "WARN: magiskboot unpack failed (host SIGILL flake possible)"
fi
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU strings in boot.img" >&2
exit 1
fi
echo "OK: no KernelSU strings in boot.img"
{
echo "artifact=$DEST"
echo "kernel_base=$BASE"
echo "hardening=Stage1 + combo4+clang-fortify + CVE-strict (~503 AXP allowlist, denylist skipped)"
echo "magisk=preroot EXTENDROM-class v30.7"
echo "signing=test-keys (CVE isolation; release-keys later)"
echo "flash=recovery wipe + sideload zip + reboot system"
echo "note=do not flash 20260918 kh-cve/kh-full (pre-FORTIFY hang)"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
+176
View File
@@ -0,0 +1,176 @@
#!/bin/bash
# 009 bacon — Magisk preroot + Stage1 Graphene/AH + combo4 Clang FORTIFY.
# Full zip so vendor modules match kernel (Wi-Fi). LAB flash only after gates.
set -e
cd /srv/rom/src/lineage
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-stage1-combo4 $(date -Is) ==="
LIVE=kernel/google/msm-4.14
BASE=28f9290ae067
test -d "$LIVE"
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
APPLY=vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1_combo4.sh
test -x "$APPLY" || chmod +x "$APPLY"
bash "$APPLY" "$LIVE"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: $DEF still enables KernelSU" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED; do
if ! grep -q "^CONFIG_${k}=y" "$DEF"; then
echo "FAIL: missing CONFIG_${k}=y" >&2
exit 1
fi
done
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
# Stage1 marker (Deny USB / sysctl)
if ! grep -q "deny_new_usb" "$LIVE/kernel/sysctl.c" \
&& ! grep -qE "deny_new_usb|CONFIG_DENY_NEW_USB|usb_device_authorization" \
"$LIVE/drivers/usb/core/hub.c"; then
echo "FAIL: Stage1 Deny_USB markers missing" >&2
exit 1
fi
echo "OK: Stage1 + combo4+FORTIFY markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
echo "OK: cleared prior Image for kernel rebuild"
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
PH=/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk
test -e "$PH"
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
if ! "$AAPT" dump permissions "$PH" 2>/dev/null | grep -q USE_EXACT_ALARM; then
echo "FAIL: Phonesky missing USE_EXACT_ALARM" >&2
exit 1
fi
echo "OK: Phonesky has USE_EXACT_ALARM"
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
# US-S2: project release keys when present (Gate G). Fallback = AOSP test-keys.
CERT_DIR="${ANDROID_CERTS_DIR:-$HOME/.android-certs}"
if [[ -f "$CERT_DIR/releasekey.pk8" && -f "$CERT_DIR/releasekey.x509.pem" ]]; then
export PRODUCT_DEFAULT_DEV_CERTIFICATE="$CERT_DIR/releasekey"
echo "OK: PRODUCT_DEFAULT_DEV_CERTIFICATE=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
else
echo "WARN: no $CERT_DIR/releasekey — building with AOSP test-keys"
fi
breakfast sunfish userdebug
test "${TARGET_RELEASE:-}" = "bp4a"
echo "=== mka bacon $(date -Is) ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
if [[ -n "${PRODUCT_DEFAULT_DEV_CERTIFICATE:-}" ]]; then
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-releasekeys
else
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4
fi
mkdir -p "$DEST"
TF_BOOT=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -path "*/IMAGES/boot.img" 2>/dev/null | head -1)
MAKE_BOOT=$PROD/boot.img
if [ -n "${TF_BOOT:-}" ] && [ -f "$TF_BOOT" ]; then
cp -v "$TF_BOOT" "$DEST/boot.img"
cp -v "$MAKE_BOOT" "$DEST/boot-make.img" 2>/dev/null || true
echo "boot_source=target_files_IMAGES"
else
cp -v "$MAKE_BOOT" "$DEST/boot.img"
echo "WARN: no TF IMAGES/boot.img — using Make boot"
fi
cp -v "$PROD"/lineage-*-UNOFFICIAL-sunfish.zip "$DEST/" 2>/dev/null || true
for f in vbmeta.img dtbo.img; do
[ -f "$PROD/$f" ] && cp -v "$PROD/$f" "$DEST/"
done
# Module CRC sanity vs kernel (Wi-Fi)
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
if [ -n "$KCRC" ] && [ -n "$MCRC" ] && [ "$KCRC" = "$MCRC" ]; then
echo "OK: wlan.ko CRC matches kernel (Wi-Fi pack coherent)"
else
echo "WARN: wlan.ko CRC mismatch or unreadable — check before flash"
fi
fi
MB=out/.magisk/magiskboot
if [ -x "$MB" ]; then
rm -rf "$DEST/boot-check" && mkdir -p "$DEST/boot-check"
if ! "$MB" unpack "$DEST/boot.img" "$DEST/boot-check" >/dev/null 2>&1; then
echo "WARN: magiskboot unpack failed (host SIGILL flake possible)"
fi
fi
if strings "$DEST/boot.img" | grep -qiE 'KernelSU|CONFIG_KSU_SUSFS'; then
echo "FAIL: KernelSU strings in boot.img" >&2
exit 1
fi
echo "OK: no KernelSU strings in boot.img"
{
echo "artifact=$DEST"
echo "kernel_base=$BASE"
echo "hardening=Stage1 Graphene/AH + combo4+clang-fortify (nested __underlying_strlen)"
echo "magisk=preroot EXTENDROM-class v30.7"
echo "signing=${PRODUCT_DEFAULT_DEV_CERTIFICATE:-test-keys}"
echo "flash=recovery wipe + sideload zip + reboot system (do NOT flash boot onto other slot alone)"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
echo "=== DONE $DEST $(date -Is) ==="
+113
View File
@@ -0,0 +1,113 @@
#!/bin/bash
# Usage: 009-bisect-ksync-one.sh <name> [CONFIG_FOO] ...
# Rebuilds kernel with optional CONFIG_=y extras, injects into GOODMAKE.
set -eo pipefail
NAME=${1:?name}; shift
cd /srv/rom/src/lineage
export HOME=/home/android-build CCACHE_DIR=/srv/rom/ccache USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
LIVE=kernel/google/msm-4.14
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
GOOD=/srv/rom/artifacts/009-axp-los23-port/20260918T143547Z-axp-full/boot-make.img
MB=$(find /srv/rom/prebuilts/magisk -name magiskboot | head -1)
echo "=== ksync-$NAME START $(date -Is) configs=[$*] ==="
git -C "$LIVE" reset --hard 28f9290ae067
git -C "$LIVE" clean -fd
sed -i '/CONFIG_KSU/d' "$DEF"
echo '# CONFIG_KSU is not set' >> "$DEF"
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED SLAB_HARDENED; do
sed -i "/CONFIG_${k}/d" "$DEF"
done
for cfg in "$@"; do
echo "${cfg}=y" >> "$DEF"
echo "enable $cfg"
done
# Clang FORTIFY port (no BUG stubs). See docs/009-fortify-clang-port.md
case " $* " in
*" CONFIG_FORTIFY_SOURCE "*)
python3 /srv/rom/artifacts/009-axp-los23-port/scripts/apply_fortify_clang.py "$LIVE"
# Driver bounds for Clang/LTO fortify false-path link (docs/009-fortify-pos0-link-failure-20260927.md)
patch -p1 --forward --batch -d "$LIVE" < /srv/rom/artifacts/009-axp-los23-port/scripts/0001-driver-bounds-rtac-fils.patch
if [ "${FORTIFY_DIAG:-0}" = 1 ]; then
python3 /srv/rom/artifacts/009-axp-los23-port/scripts/apply_fortify_diag.py "$LIVE"
fi
;;
esac
# Plaintext panic mirror into alt ramoops (no FORTIFY). See docs/009-fortify-diag-capture-plan.md
# Also applied via FORTIFY_DIAG above when building fortify-diag.
if [ "${PSTORE_ALT_PLAIN:-0}" = 1 ]; then
python3 /srv/rom/artifacts/009-axp-los23-port/scripts/apply_fortify_diag.py "$LIVE"
fi
# FORTIFY stubs DISABLED by default (see docs/009-fortify-clang-port.md).
# Set ALLOW_FORTIFY_BUG_STUBS=1 only for archaeology — not a valid port.
if [ "${ALLOW_FORTIFY_BUG_STUBS:-0}" = 1 ]; then
case " $* " in *" CONFIG_FORTIFY_SOURCE "*)
echo 'WARN: injecting BUG() fortify stubs (ALLOW_FORTIFY_BUG_STUBS=1)'
python3 - <<'PY2'
from pathlib import Path
import re
p = Path("kernel/google/msm-4.14/lib/string.c")
t = p.read_text()
if "EXPORT_SYMBOL(__read_overflow)" not in t:
m = re.search(r"void fortify_panic\(const char \*name\).*?EXPORT_SYMBOL\(fortify_panic\);", t, re.S)
assert m, 'fortify_panic marker missing'
stub = m.group(0) + """
void __read_overflow(void) { BUG(); }
EXPORT_SYMBOL(__read_overflow);
void __read_overflow2(void) { BUG(); }
EXPORT_SYMBOL(__read_overflow2);
void __read_overflow3(void) { BUG(); }
EXPORT_SYMBOL(__read_overflow3);
void __write_overflow(void) { BUG(); }
EXPORT_SYMBOL(__write_overflow);
"""
p.write_text(t[:m.start()] + stub + t[m.end():])
PY2
;; esac
fi
set +u; source build/envsetup.sh; set -e
breakfast sunfish userdebug
echo "TARGET_RELEASE=${TARGET_RELEASE:-unset}"
test "${TARGET_RELEASE:-}" = "bp4a"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4
rm -f out/target/product/sunfish/boot.img
mka bootimage -j8
PROD=/srv/rom/src/lineage/out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-bisect-ksync-${NAME}
mkdir -p "$DEST"
cp -v "$PROD/boot.img" "$DEST/boot-make-native.img"
WORK=$DEST/work
rm -rf "$WORK"; mkdir -p "$WORK/good" "$WORK/new" "$WORK/check"
(cd "$WORK/good" && "$MB" unpack "$GOOD")
(cd "$WORK/new" && "$MB" unpack "$PROD/boot.img")
cp -f "$WORK/new/kernel" "$WORK/good/kernel"
(cd "$WORK/good" && "$MB" repack "$GOOD" "$DEST/boot-make.img")
(cd "$WORK/check" && "$MB" unpack "$DEST/boot-make.img" >/dev/null && "$MB" cpio ramdisk.cpio extract)
echo "=== VERIFY $NAME ==="
grep -E 'security_patch|sdk_full' "$WORK/check/prop.default" | sort -u
# Prefer header from first successful unpack (WORK/check); do not ||true a SIGILL unpack.
if [[ -f "$WORK/check/kernel" ]]; then
"$MB" unpack "$DEST/boot-make.img" >"$DEST/unpack-hdr.txt" 2>&1 || \
echo "WARN: magiskboot header unpack failed (host SIGILL?); kernel already verified via WORK/check" | tee -a "$DEST/unpack-hdr.txt"
else
"$MB" unpack "$DEST/boot-make.img" >"$DEST/unpack-hdr.txt" 2>&1
fi
grep -E 'OS_PATCH|KERNEL_SZ|KERNEL_FMT' "$DEST/unpack-hdr.txt" || true
# ikconfig spot-check
/srv/rom/src/lineage/kernel/google/msm-4.14/scripts/extract-ikconfig "$WORK/check/kernel" 2>/dev/null \
| grep -E 'CONFIG_(SECURITY_DMESG_RESTRICT|SLAB_FREELIST_HARDENED|FORTIFY_SOURCE|INIT_ON_ALLOC)=' || true
sha256sum "$DEST/boot-make.img" "$DEST/boot-make-native.img" | tee "$DEST/SHA256SUMS"
printf '%s\n' "ksync bisect: $NAME" "configs: $*" "Flash boot-make.img only (GOODMAKE ramdisk)." > "$DEST/README.txt"
echo "=== DONE $DEST $(date -Is) ==="
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# Channel-proof build (no FORTIFY): diag v4 early_ioremap + ioremap after mm_init.
# Validate (docs/009-fortify-diag-capture-plan.md v4 — mid/late only):
# 0) Wipe first 4K of access-ramoops or fresh attempt id
# 1) Flash boot-make.img, boot, adb root
# 2) Milestone: strings | grep AXPDIAG0 (early_map / perm_map / mm_init)
# 3) ONLY SysRq-c (no SysRq-x)
# 4) Recover combo4 — complete AXPDIAG1,C,<attempt>,0x...,sysrq-c
# Does NOT prove pre-early_map FORTIFY coverage. Do not flash v3.
set -euo pipefail
export PSTORE_ALT_PLAIN=1
exec /srv/rom/artifacts/009-axp-los23-port/scripts/009-bisect-ksync-one.sh \
pstore-alt-plain
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Server-only diagnostic build. Uses its own sources/output; never resets LIVE.
# Usage: bash this-script ROOT <family>
# family: see 009-fortify-select-family.py GROUPS
set -euo pipefail
RUN=${1:?isolated root required}
FAMILY=${2:?family required}
case "$FAMILY" in
full|memory|strings|strings-copy|strings-len|\
strings-strcpy-strlen|strings-copy-strlen|strings-copy-strnlen|\
strings-strlen-restcopy|strings-strlen-ncopy|strings-strlen-cat|\
strings-strlen-strncpy|strings-strlen-strlcpy) ;;
*) echo "bad family: $FAMILY" >&2; exit 2;;
esac
test -d "$RUN/inputs"
# Subdir name matches family (strings-copy → strings-copy/)
test ! -e "$RUN/$FAMILY"
TOP=/srv/rom/src/lineage
LIVE=$TOP/kernel/google/msm-4.14
PIN=28f9290ae067d3e7857126eb6312720dbe9c7c80
CLANG=$TOP/prebuilts/clang/host/linux-x86/clang-r563880c
BASE=$RUN/$FAMILY
SRC=$BASE/src
OUT=$BASE/out
mkdir -p "$SRC" "$OUT"
git -C "$LIVE" archive "$PIN" | tar -x -C "$SRC"
python3 "$RUN/inputs/apply_fortify_clang.py" "$SRC"
patch -p1 --batch -d "$SRC" < "$RUN/inputs/0001-driver-bounds-rtac-fils.patch"
python3 "$RUN/inputs/009-fortify-select-family.py" "$SRC" "$FAMILY"
cp "$RUN/inputs/failed-full.config" "$OUT/.config"
cp "$OUT/.config" "$BASE/input.config"
export PATH="$TOP/prebuilts/tools-lineage/linux-x86/bin:$TOP/prebuilts/build-tools/linux-x86/bin:$CLANG/bin:$TOP/out/host/linux-x86/bin:/usr/bin:/bin"
export HIP_PATH=none
export PERL5LIB="$TOP/prebuilts/tools-lineage/common/perl-base"
export BISON_PKGDATADIR="$TOP/prebuilts/build-tools/common/bison"
export KBUILD_BUILD_USER=nobody KBUILD_BUILD_HOST=android-build
# An archived patched tree needs the same release suffix as installed modules.
export LOCALVERSION=-g28f9290ae067-dirty
MAKE=("$TOP/prebuilts/build-tools/linux-x86/bin/make" -j8
"HOSTCFLAGS=--sysroot=$TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/sysroot -I$TOP/prebuilts/kernel-build-tools/linux-x86/include"
"HOSTLDFLAGS=--sysroot=$TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/sysroot -Wl,-rpath,$TOP/prebuilts/kernel-build-tools/linux-x86/lib64 -L $TOP/prebuilts/kernel-build-tools/linux-x86/lib64 -fuse-ld=lld --rtlib=compiler-rt"
LLVM=1 LLVM_IAS=1 "LZ4=$TOP/prebuilts/kernel-build-tools/linux-x86/bin/lz4"
"LEX=$TOP/prebuilts/build-tools/linux-x86/bin/flex"
"YACC=$TOP/prebuilts/build-tools/linux-x86/bin/bison"
"M4=$TOP/prebuilts/build-tools/linux-x86/bin/m4"
"PAHOLE=$TOP/prebuilts/kernel-build-tools/linux-x86/bin/pahole"
"LIBCLANG_PATH=$CLANG/lib" "DTC_EXT=$TOP/out/host/linux-x86/bin/dtc"
-C "$SRC" "O=$OUT" ARCH=arm64 CC=clang LD=ld.lld)
"${MAKE[@]}" olddefconfig
diff -u "$BASE/input.config" "$OUT/.config" > "$BASE/config.diff" || {
cat "$BASE/config.diff"
echo 'STOP: unexpected configuration drift' >&2
exit 3
}
"$CLANG/bin/clang" --version > "$BASE/toolchain.txt"
printf '%s\n' "$PIN" > "$BASE/source-commit.txt"
printf '%q ' "${MAKE[@]}" > "$BASE/make-command.txt"
"${MAKE[@]}" Image.lz4 modules
cp "$OUT/.config" "$BASE/final.config"
test "$(cat "$OUT/include/config/kernel.release")" = '4.14.357-openela-g28f9290ae067-dirty'
if "$CLANG/bin/llvm-nm" "$OUT/vmlinux" | grep -Eq ' [Tt] __((read_overflow[23]?)|(write_overflow))$'; then
echo 'STOP: forbidden overflow stubs' >&2
exit 4
fi
mkdir "$BASE/pack"
cd "$BASE/pack"
MB=$(find /srv/rom/prebuilts/magisk -type f -name magiskboot -print -quit)
test -n "$MB"
GOOD=/srv/rom/artifacts/009-axp-los23-port/20260918T143547Z-axp-full/boot-make.img
"$MB" unpack "$GOOD" > "$BASE/good-header.txt" 2>&1
sha256sum ramdisk.cpio > "$BASE/ramdisk.sha256"
cp "$OUT/arch/arm64/boot/Image" kernel
"$MB" repack "$GOOD" "$BASE/boot-make.img"
sha256sum "$BASE/boot-make.img" "$OUT/arch/arm64/boot/Image" "$OUT/vmlinux" > "$BASE/SHA256SUMS"
printf 'DIAGNOSTIC ONLY: %s family; not full FORTIFY. Requires local unpack/header/kernel verification before LAB use.\n' "$FAMILY" > "$BASE/README.txt"
echo "BUILD COMPLETE: $BASE/boot-make.img"
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# After hi-batch3 BOOT_OK: launch release-keys bacon (US-S2) on rom-dev01.
# Does not flash RK zip (needs recovery sideload); only starts bacon + status poll helper.
set -euo pipefail
SSHCFG="${SSHCFG:-/home/aventura/Workspaces/AXP_Resurection/docs/ssh/config}"
BATCH3_LOG="${BATCH3_LOG:-/tmp/009-wait-flash-cve-hi-batch3.log}"
LOG="${LOG:-/tmp/009-chain-hi3-to-releasekeys.log}"
exec > >(tee -a "$LOG") 2>&1
echo "CHAIN start $(date -Is) wait HI_BATCH3_BOOT_OK"
for i in $(seq 1 200); do
if grep -q 'HI_BATCH3_BOOT_OK' "$BATCH3_LOG" 2>/dev/null; then
echo "seen HI_BATCH3_BOOT_OK"
break
fi
if grep -qE 'BACON_FAILED|NOT_BOOTED|BOOT_TIMEOUT|TIMEOUT' "$BATCH3_LOG" 2>/dev/null; then
# allow exit=141 path that still boots — only abort on clear fail without OK
if ! grep -q 'HI_BATCH3_BOOT_OK' "$BATCH3_LOG" 2>/dev/null; then
if grep -qE 'BACON_FAILED|NOT_BOOTED|BOOT_TIMEOUT' "$BATCH3_LOG" 2>/dev/null; then
echo CHAIN_ABORT_BATCH3_FAIL
exit 1
fi
fi
fi
sleep 30
done
grep -q 'HI_BATCH3_BOOT_OK' "$BATCH3_LOG" || { echo CHAIN_TIMEOUT; exit 1; }
echo "=== launch release-keys bacon $(date -Is) ==="
ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 '
test -f /home/android-build/.android-certs/releasekey.pk8
install -m 0755 /tmp/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh \
/srv/rom/artifacts/009-axp-los23-port/scripts/ 2>/dev/null || true
# script must already be on host
test -x /srv/rom/artifacts/009-axp-los23-port/scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh
/srv/rom/artifacts/bin/rom-detach 009-bacon-cve-hi-releasekeys -- \
bash /srv/rom/artifacts/009-axp-los23-port/scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh
sleep 2
cat /srv/rom/artifacts/detach-009-bacon-cve-hi-releasekeys.status
'
echo CHAIN_RK_STARTED
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Diagnostic-only split of the existing Clang FORTIFY port; never a ship patch.
Apply after apply_fortify_clang.py. Omitted wrappers resolve to the existing
unfortified declarations. No overflow stubs or changes to fortify_panic.
"""
import argparse
import re
from pathlib import Path
STRINGS = {"strncpy", "strcat", "strlen", "strnlen", "strlcpy", "strncat", "strcpy"}
MEMORY = {"memset", "memcpy", "memmove", "memscan", "memcmp", "memchr", "memchr_inv", "kmemdup"}
STRINGS_COPY = {"strcpy", "strncpy", "strlcpy", "strcat", "strncat"}
STRINGS_LEN = {"strlen", "strnlen"}
GROUPS = {
"full": STRINGS | MEMORY,
"strings": STRINGS,
"memory": MEMORY,
# Split of failing strings family (2026-09-27 LAB):
"strings-copy": STRINGS_COPY,
"strings-len": STRINGS_LEN,
# Cross products (copy boots, len boots, both fail → interaction):
"strings-strcpy-strlen": {"strcpy", "strlen"},
"strings-copy-strlen": STRINGS_COPY | {"strlen"},
"strings-copy-strnlen": STRINGS_COPY | {"strnlen"},
# copy-strlen fails, strcpy-strlen boots → rest of copy + strlen:
"strings-strlen-restcopy": {"strlen", "strncpy", "strlcpy", "strcat", "strncat"},
"strings-strlen-ncopy": {"strlen", "strncpy", "strlcpy"},
"strings-strlen-cat": {"strlen", "strcat", "strncat"},
"strings-strlen-strncpy": {"strlen", "strncpy"},
"strings-strlen-strlcpy": {"strlen", "strlcpy"},
}
def select(text, family):
if "AXP_FORTIFY_DIAGNOSTIC_FAMILY" in text:
raise ValueError("Already selected: start from a fresh fully ported header")
if "__pass_object_size" not in text or "__overloadable" not in text:
raise ValueError("Apply the Clang FORTIFY port first")
pattern = re.compile(r"^__FORTIFY_INLINE[^\n]*?\b(\w+)\([^\n]*\)\n\{", re.M)
matches = list(pattern.finditer(text))
expected = STRINGS | MEMORY
names = [m.group(1) for m in matches]
if len(names) != len(expected) or set(names) != expected:
raise ValueError(f"Unexpected wrapper inventory: {names}")
enabled = GROUPS[family]
for match in reversed(matches):
name = match.group(1)
if name in enabled:
continue
start = match.end() - 1
depth = 1
end = start + 1
while depth:
depth += (text[end] == "{") - (text[end] == "}")
end += 1
text = (
text[: match.start()]
+ "#if 0 /* diagnostic: other FORTIFY family */\n"
+ text[match.start() : end]
+ "\n#endif\n"
+ text[end:]
)
return f"/* AXP_FORTIFY_DIAGNOSTIC_FAMILY={family}; NOT FOR SHIP */\n" + text
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("kernel", type=Path)
parser.add_argument("family", choices=sorted(GROUPS))
args = parser.parse_args()
path = args.kernel / "include/linux/string.h"
path.write_text(select(path.read_text(), args.family))
print(f"DIAGNOSTIC ONLY: FORTIFY family={args.family}; panic behavior unchanged")
if __name__ == "__main__":
raise SystemExit(main())
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Generate project Android + AVB signing keys on rom-dev01 only.
# Never run this to a path inside a Git working tree that gets committed.
set -euo pipefail
OUT="${ANDROID_CERTS_DIR:-}"
if [[ -z "$OUT" ]]; then
if [[ -d /srv/rom/secrets && -w /srv/rom/secrets ]]; then
OUT=/srv/rom/secrets/android-certs
else
# Lineage convention; /srv/rom/secrets is often root-owned (750, no group write).
OUT="${HOME}/.android-certs"
fi
fi
LINEAGE_TOP="${LINEAGE_TOP:-/srv/rom/src/lineage}"
MAKE_KEY="${LINEAGE_TOP}/development/tools/make_key"
# Distinct from AOSP testkey DN (android@android.com).
SUBJECT="${ANDROID_CERT_SUBJECT:-/C=DE/ST=NA/L=NA/O=AXP-Resurection/OU=sunfish-a16/CN=release}"
KEYS=(releasekey platform shared media networkstack sdk_sandbox bluetooth nfc)
if [[ ! -x "$MAKE_KEY" && ! -f "$MAKE_KEY" ]]; then
echo "ERROR: make_key not found at $MAKE_KEY" >&2
exit 1
fi
if [[ "$(id -u)" -eq 0 ]]; then
echo "ERROR: refuse to generate keys as root; run as android-build" >&2
exit 1
fi
mkdir -p "$OUT"
chmod 700 "$OUT"
existing=0
for k in "${KEYS[@]}"; do
if [[ -e "$OUT/$k.pk8" || -e "$OUT/$k.x509.pem" ]]; then
echo "EXISTS: $OUT/$k.*"
existing=1
fi
done
if [[ -e "$OUT/avb.pem" ]]; then
echo "EXISTS: $OUT/avb.pem"
existing=1
fi
if [[ "$existing" -eq 1 ]]; then
echo "ERROR: refusing to overwrite. Delete specifically if you truly mean to rotate." >&2
exit 1
fi
echo "=== Generating APK/OTA keys in $OUT ==="
echo "subject=$SUBJECT"
for k in "${KEYS[@]}"; do
# make_key always exits 1 because of its EXIT trap — ignore status; check files.
# Blank password (Lineage/build-server norm); protect via directory mode 700.
printf '\n' | bash "$MAKE_KEY" "$OUT/$k" "$SUBJECT" || true
if [[ ! -f "$OUT/$k.pk8" || ! -f "$OUT/$k.x509.pem" ]]; then
echo "ERROR: failed to create $k" >&2
exit 1
fi
chmod 600 "$OUT/$k.pk8"
chmod 644 "$OUT/$k.x509.pem"
echo "OK $k"
done
echo "=== Generating AVB RSA-4096 key ==="
openssl genrsa -out "$OUT/avb.pem" 4096
chmod 600 "$OUT/avb.pem"
# Public key metadata for avbtool / optional custom key flash (not used for lock ship)
if [[ -x "$LINEAGE_TOP/external/avb/avbtool.py" ]]; then
python3 "$LINEAGE_TOP/external/avb/avbtool.py" extract_public_key \
--key "$OUT/avb.pem" --output "$OUT/avb_pkmd.bin"
chmod 644 "$OUT/avb_pkmd.bin"
elif command -v avbtool >/dev/null 2>&1; then
avbtool extract_public_key --key "$OUT/avb.pem" --output "$OUT/avb_pkmd.bin"
chmod 644 "$OUT/avb_pkmd.bin"
else
echo "WARN: avbtool not found; skipped avb_pkmd.bin (openssl key still OK)"
fi
{
echo "# generated $(date -Is) on $(hostname)"
echo "subject=$SUBJECT"
echo "dir=$OUT"
echo "keys=${KEYS[*]} avb.pem"
} >"$OUT/GENERATED.txt"
chmod 644 "$OUT/GENERATED.txt"
echo
echo "=== DONE ==="
echo "Public releasekey subject:"
openssl x509 -in "$OUT/releasekey.x509.pem" -noout -subject
echo
echo "NEXT (mandatory before first signed ship bacon):"
echo " 1. Encrypted offline backup of $OUT"
echo " 2. If keys are under \$HOME/.android-certs, have root install -d"
echo " /srv/rom/secrets/android-certs and move/copy there (preferred long-term)."
echo " 3. Record backup location/date in docs/009-hardening-signing-checklist.md"
echo " 4. US-S2: export PRODUCT_DEFAULT_DEV_CERTIFICATE=$OUT/releasekey"
echo " 5. Never copy .pk8 / avb.pem into Git"
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# Wait for CVE-clean bacon on rom-dev01, then one-shot fastboot flash boot + one reboot.
# No recovery loops. No auto-recover. Exits if not booted in ~4 min.
set -euo pipefail
SSHCFG="${SSHCFG:-/home/aventura/Workspaces/AXP_Resurection/docs/ssh/config}"
SERIAL="${SERIAL:-11231JEC207477}"
LOCAL="${LOCAL:-/home/aventura/Workspaces/AXP_Resurection/.local-artifacts/009-flash-cve-clean}"
STATUS=detach-009-bacon-cve-clean.status
LOG="${LOG:-/tmp/009-wait-flash-cve-clean.log}"
mkdir -p "$LOCAL"
exec > >(tee -a "$LOG") 2>&1
echo "WAIT start $(date -Is)"
st=""
for i in $(seq 1 180); do
st=$(ssh -F "$SSHCFG" -o BatchMode=yes -o ConnectTimeout=20 rom-dev01 \
"cat /srv/rom/artifacts/$STATUS" 2>/dev/null || echo status_unreadable)
echo "poll $i: $st"
if echo "$st" | grep -q '^exit=0'; then
break
fi
if echo "$st" | grep -qE '^exit=[1-9]|^exit=killed'; then
echo BACON_FAILED
exit 1
fi
sleep 60
done
echo "$st" | grep -q '^exit=0' || { echo TIMEOUT; exit 1; }
ART=$(ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
'ls -dt /srv/rom/artifacts/009-axp-los23-port/*-axp-stage1-combo4-cve-clean 2>/dev/null | head -1')
echo "ART=$ART"
test -n "$ART"
rsync -avP -e "ssh -F $SSHCFG -o BatchMode=yes" \
"rom-dev01:$ART/boot.img" "rom-dev01:$ART/README.txt" "rom-dev01:$ART/SHA256SUMS" "$LOCAL/"
cd "$LOCAL"
grep ' boot.img$' SHA256SUMS | sha256sum -c -
adb start-server >/dev/null 2>&1 || true
cur=$(adb -s "$SERIAL" get-state 2>/dev/null || true)
if [[ "$cur" == "device" ]]; then
echo "adb reboot bootloader once"
adb -s "$SERIAL" reboot bootloader
fi
for j in $(seq 1 40); do
fastboot devices 2>/dev/null | grep -q "$SERIAL" && break
sleep 2
done
fastboot devices | grep -q "$SERIAL" || { echo NO_FASTBOOT; exit 1; }
echo "fastboot flash boot (cve-clean) once"
fastboot -s "$SERIAL" flash boot "$LOCAL/boot.img"
echo "fastboot reboot once"
fastboot -s "$SERIAL" reboot
for j in $(seq 1 60); do
sleep 5
cur=$(adb devices | awk -v s="$SERIAL" '$1==s{print $2}')
boot=$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
echo "t=$((j*5))s adb=$cur boot=$boot"
if [[ "$cur" == "device" && "$boot" == "1" ]]; then
adb -s "$SERIAL" shell 'uname -a; getprop ro.boot.slot_suffix; pidof magiskd || true'
echo CLEAN_BOOT_OK
exit 0
fi
if [[ "$j" -eq 48 ]]; then
echo NOT_BOOTED_4min leave_alone
exit 2
fi
done
echo BOOT_TIMEOUT
exit 2
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Wait for hi-batch1 bacon on rom-dev01, then one-shot fastboot flash boot + reboot.
# Serial-pinned. No recovery loops. Exit if not booted in ~4 min.
set -euo pipefail
SSHCFG="${SSHCFG:-/home/aventura/Workspaces/AXP_Resurection/docs/ssh/config}"
SERIAL="${SERIAL:-11231JEC207477}"
LOCAL="${LOCAL:-/home/aventura/Workspaces/AXP_Resurection/.local-artifacts/009-flash-cve-hi-batch1}"
STATUS=detach-009-bacon-cve-hi-batch1.status
LOG="${LOG:-/tmp/009-wait-flash-cve-hi-batch1.log}"
mkdir -p "$LOCAL"
exec > >(tee -a "$LOG") 2>&1
echo "WAIT hi-batch1 start $(date -Is)"
st=""
for i in $(seq 1 180); do
st=$(ssh -F "$SSHCFG" -o BatchMode=yes -o ConnectTimeout=20 rom-dev01 \
"cat /srv/rom/artifacts/$STATUS" 2>/dev/null || echo status_unreadable)
echo "poll $i: $st"
# exit=141 = SIGPIPE from tee after successful bacon (false fail)
if echo "$st" | grep -qE '^exit=0|^exit=141'; then
break
fi
if echo "$st" | grep -qE '^exit=[1-9]|^exit=killed'; then
if echo "$st" | grep -q '^exit=141'; then
break
fi
echo BACON_FAILED
exit 1
fi
sleep 60
done
echo "$st" | grep -qE '^exit=0|^exit=141' || { echo TIMEOUT; exit 1; }
ART=$(ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
'ls -dt /srv/rom/artifacts/009-axp-los23-port/*-axp-stage1-combo4-cve-hi-batch1 2>/dev/null | head -1')
echo "ART=$ART"
test -n "$ART"
rsync -avP -e "ssh -F $SSHCFG -o BatchMode=yes" \
"rom-dev01:$ART/boot.img" "rom-dev01:$ART/README.txt" "rom-dev01:$ART/SHA256SUMS" "$LOCAL/"
cd "$LOCAL"
grep ' boot.img$' SHA256SUMS | sha256sum -c -
adb start-server >/dev/null 2>&1 || true
cur=$(adb -s "$SERIAL" get-state 2>/dev/null || true)
if [[ "$cur" == "device" ]]; then
echo "adb reboot bootloader once"
adb -s "$SERIAL" reboot bootloader
fi
for j in $(seq 1 40); do
fastboot devices 2>/dev/null | grep -q "$SERIAL" && break
sleep 2
done
fastboot devices | grep -q "$SERIAL" || { echo NO_FASTBOOT; exit 1; }
echo "fastboot flash boot (hi-batch1) once"
fastboot -s "$SERIAL" flash boot "$LOCAL/boot.img"
echo "fastboot reboot once"
fastboot -s "$SERIAL" reboot
for j in $(seq 1 60); do
sleep 5
cur=$(adb devices | awk -v s="$SERIAL" '$1==s{print $2}')
boot=$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
echo "t=$((j*5))s adb=$cur boot=$boot"
if [[ "$cur" == "device" && "$boot" == "1" ]]; then
adb -s "$SERIAL" shell 'uname -a; getprop ro.boot.slot_suffix; pidof magiskd || true'
echo HI_BATCH1_BOOT_OK
exit 0
fi
if [[ "$j" -eq 48 ]]; then
echo NOT_BOOTED_4min leave_alone
exit 2
fi
done
echo BOOT_TIMEOUT
exit 2
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Wait for hi-batch2 bacon on rom-dev01, then one-shot fastboot flash boot + reboot.
# Serial-pinned. No recovery loops. Exit if not booted in ~4 min.
set -euo pipefail
SSHCFG="${SSHCFG:-/home/aventura/Workspaces/AXP_Resurection/docs/ssh/config}"
SERIAL="${SERIAL:-11231JEC207477}"
LOCAL="${LOCAL:-/home/aventura/Workspaces/AXP_Resurection/.local-artifacts/009-flash-cve-hi-batch2}"
STATUS=detach-009-bacon-cve-hi-batch2.status
LOG="${LOG:-/tmp/009-wait-flash-cve-hi-batch2.log}"
mkdir -p "$LOCAL"
exec > >(tee -a "$LOG") 2>&1
echo "WAIT hi-batch2 start $(date -Is)"
st=""
for i in $(seq 1 180); do
st=$(ssh -F "$SSHCFG" -o BatchMode=yes -o ConnectTimeout=20 rom-dev01 \
"cat /srv/rom/artifacts/$STATUS" 2>/dev/null || echo status_unreadable)
echo "poll $i: $st"
# exit=141 = SIGPIPE from tee after successful bacon (false fail)
if echo "$st" | grep -qE '^exit=0|^exit=141'; then
break
fi
if echo "$st" | grep -qE '^exit=[1-9]|^exit=killed'; then
if echo "$st" | grep -q '^exit=141'; then
break
fi
echo BACON_FAILED
exit 1
fi
sleep 60
done
echo "$st" | grep -qE '^exit=0|^exit=141' || { echo TIMEOUT; exit 1; }
# Confirm artifact exists (guards real failures that somehow report 141)
ART_PROBE=$(ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
'ls -dt /srv/rom/artifacts/009-axp-los23-port/*-axp-stage1-combo4-cve-hi-batch2 2>/dev/null | head -1')
test -n "$ART_PROBE" || { echo NO_ARTIFACT; exit 1; }
ART=$(ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
'ls -dt /srv/rom/artifacts/009-axp-los23-port/*-axp-stage1-combo4-cve-hi-batch2 2>/dev/null | head -1')
echo "ART=$ART"
test -n "$ART"
rsync -avP -e "ssh -F $SSHCFG -o BatchMode=yes" \
"rom-dev01:$ART/boot.img" "rom-dev01:$ART/README.txt" "rom-dev01:$ART/SHA256SUMS" "$LOCAL/"
cd "$LOCAL"
grep ' boot.img$' SHA256SUMS | sha256sum -c -
adb start-server >/dev/null 2>&1 || true
cur=$(adb -s "$SERIAL" get-state 2>/dev/null || true)
if [[ "$cur" == "device" ]]; then
echo "adb reboot bootloader once"
adb -s "$SERIAL" reboot bootloader
fi
for j in $(seq 1 40); do
fastboot devices 2>/dev/null | grep -q "$SERIAL" && break
sleep 2
done
fastboot devices | grep -q "$SERIAL" || { echo NO_FASTBOOT; exit 1; }
echo "fastboot flash boot (hi-batch2) once"
fastboot -s "$SERIAL" flash boot "$LOCAL/boot.img"
echo "fastboot reboot once"
fastboot -s "$SERIAL" reboot
for j in $(seq 1 60); do
sleep 5
cur=$(adb devices | awk -v s="$SERIAL" '$1==s{print $2}')
boot=$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
echo "t=$((j*5))s adb=$cur boot=$boot"
if [[ "$cur" == "device" && "$boot" == "1" ]]; then
adb -s "$SERIAL" shell 'uname -a; getprop ro.boot.slot_suffix; pidof magiskd || true'
echo HI_BATCH2_BOOT_OK
exit 0
fi
if [[ "$j" -eq 48 ]]; then
echo NOT_BOOTED_4min leave_alone
exit 2
fi
done
echo BOOT_TIMEOUT
exit 2
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Wait for hi-batch3 bacon on rom-dev01, then one-shot fastboot flash boot + reboot.
# Treats exit=141 (tee SIGPIPE) as OK when artifact exists.
set -euo pipefail
SSHCFG="${SSHCFG:-/home/aventura/Workspaces/AXP_Resurection/docs/ssh/config}"
SERIAL="${SERIAL:-11231JEC207477}"
LOCAL="${LOCAL:-/home/aventura/Workspaces/AXP_Resurection/.local-artifacts/009-flash-cve-hi-batch3}"
STATUS=detach-009-bacon-cve-hi-batch3.status
LOG="${LOG:-/tmp/009-wait-flash-cve-hi-batch3.log}"
mkdir -p "$LOCAL"
exec > >(tee -a "$LOG") 2>&1
echo "WAIT hi-batch3 start $(date -Is)"
st=""
for i in $(seq 1 180); do
st=$(ssh -F "$SSHCFG" -o BatchMode=yes -o ConnectTimeout=20 rom-dev01 \
"cat /srv/rom/artifacts/$STATUS" 2>/dev/null || echo status_unreadable)
echo "poll $i: $st"
if echo "$st" | grep -qE '^exit=0|^exit=141'; then
break
fi
if echo "$st" | grep -qE '^exit=[1-9]|^exit=killed'; then
echo BACON_FAILED
exit 1
fi
sleep 60
done
echo "$st" | grep -qE '^exit=0|^exit=141' || { echo TIMEOUT; exit 1; }
ART=$(ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
'ls -dt /srv/rom/artifacts/009-axp-los23-port/*-axp-stage1-combo4-cve-hi-batch3 2>/dev/null | head -1')
echo "ART=$ART"
test -n "$ART"
# ensure SHA256SUMS exists (tee may have truncated post-bacon)
ssh -F "$SSHCFG" -o BatchMode=yes rom-dev01 \
"test -f $ART/SHA256SUMS || (cd $ART && sha256sum boot.img boot-make.img lineage-*-UNOFFICIAL-sunfish.zip 2>/dev/null | tee SHA256SUMS)"
rsync -avP -e "ssh -F $SSHCFG -o BatchMode=yes" \
"rom-dev01:$ART/boot.img" "rom-dev01:$ART/README.txt" "rom-dev01:$ART/SHA256SUMS" "$LOCAL/"
cd "$LOCAL"
grep ' boot.img$' SHA256SUMS | sha256sum -c -
adb start-server >/dev/null 2>&1 || true
cur=$(adb -s "$SERIAL" get-state 2>/dev/null || true)
if [[ "$cur" == "device" ]]; then
echo "adb reboot bootloader once"
adb -s "$SERIAL" reboot bootloader
fi
for j in $(seq 1 40); do
fastboot devices 2>/dev/null | grep -q "$SERIAL" && break
sleep 2
done
fastboot devices | grep -q "$SERIAL" || { echo NO_FASTBOOT; exit 1; }
echo "fastboot flash boot (hi-batch3) once"
fastboot -s "$SERIAL" flash boot "$LOCAL/boot.img"
echo "fastboot reboot once"
fastboot -s "$SERIAL" reboot
for j in $(seq 1 60); do
sleep 5
cur=$(adb devices | awk -v s="$SERIAL" '$1==s{print $2}')
boot=$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
echo "t=$((j*5))s adb=$cur boot=$boot"
if [[ "$cur" == "device" && "$boot" == "1" ]]; then
adb -s "$SERIAL" shell 'uname -a; getprop ro.boot.slot_suffix; pidof magiskd || true'
echo HI_BATCH3_BOOT_OK
exit 0
fi
if [[ "$j" -eq 48 ]]; then
echo NOT_BOOTED_4min leave_alone
exit 2
fi
done
echo BOOT_TIMEOUT
exit 2
+54
View File
@@ -0,0 +1,54 @@
#!/bin/bash
# Wait for release-keys bacon to finish, then start Stage1+combo4+CVE bacon.
set -euo pipefail
RK_STATUS=/srv/rom/artifacts/detach-009-bacon-axp-stage1-combo4-rk.status
CVE_NAME=009-bacon-axp-stage1-combo4-cve
CVE_SCRIPT=/srv/rom/artifacts/bin/009-bacon-axp-stage1-combo4-cve.sh
DETACH=/srv/rom/artifacts/bin/rom-detach
echo "watcher: waiting for RK bacon to leave 'running' ($(date -Is))"
while true; do
if [[ -f "$RK_STATUS" ]]; then
if grep -q '^exit=' "$RK_STATUS" 2>/dev/null; then
break
fi
if ! grep -q '^running ' "$RK_STATUS" 2>/dev/null; then
break
fi
else
# no status → assume free
break
fi
# still building?
if ! pgrep -f 'm bacon -j' >/dev/null 2>&1 \
&& ! pgrep -f 'combined-lineage_sunfish.ninja' >/dev/null 2>&1; then
# status stale?
sleep 30
if ! pgrep -f 'm bacon -j' >/dev/null 2>&1; then
echo "watcher: no mka/ninja; treating RK as done ($(date -Is))"
break
fi
fi
sleep 60
done
echo "watcher: RK status now:"
cat "$RK_STATUS" 2>/dev/null || true
if grep -q '^exit=0' "$RK_STATUS" 2>/dev/null; then
echo "watcher: RK bacon OK"
elif grep -q '^exit=' "$RK_STATUS" 2>/dev/null; then
echo "watcher: RK bacon FAILED — still launching CVE (kernel will reset)"
else
echo "watcher: RK status unclear — launching CVE after short settle"
sleep 15
fi
# Ensure scripts synced into lineage vendor
KH=/srv/rom/src/lineage/vendor/sunfish-a16/patches/009-kernel-hardening
test -x "$CVE_SCRIPT"
test -f "$KH/cve-strict.list"
test -x "$KH/apply_stage1_combo4_cve.sh"
"$DETACH" "$CVE_NAME" -- bash "$CVE_SCRIPT"
echo "watcher: CVE bacon detached ($(date -Is))"
@@ -12,7 +12,7 @@
| Readback | `adb shell settings get secure disable_secure_windows` | | Readback | `adb shell settings get secure disable_secure_windows` |
| UI preference key | `allow_flag_secure_override` (writes the Secure key above) | | UI preference key | `allow_flag_secure_override` (writes the Secure key above) |
| Scope | **Global** (all apps); per-app lists not planned | | Scope | **Global** (all apps); per-app lists not planned |
| Note | WMS only applies this on **debuggable** builds (`ro.debuggable`) | | Note | Stock AOSP gated WMS on `ro.debuggable`; project removes that gate (`SUNFISH_A16_008_WMS_DISABLE_SECURE_NO_DEBUGGABLE_GATE`) so `ro.debuggable` stays `0` |
## Capture behaviour ## Capture behaviour
+6 -2
View File
@@ -39,8 +39,12 @@ has `Settings.Secure.DISABLE_SECURE_WINDOWS` wired through
Options switch (preference key `allow_flag_secure_override`) that writes Options switch (preference key `allow_flag_secure_override`) that writes
`disable_secure_windows`. `disable_secure_windows`.
**Constraint**: WMS applies the setting only on debuggable builds **Constraint (stock AOSP)**: WMS applied the setting only on debuggable builds
(`ro.debuggable`). LAB `userdebug` is in scope; `user` retest before release. (`ro.debuggable`). **Project decision (2026-09-18):** remove that gate in
`updateDisableSecureWindows()` (`apply_wms_disable_secure_debuggable_gate.py`)
so ship images keep `ro.debuggable=0` while the DO toggle works. No other
secondary gate found in `WindowState.isSecureLocked` / `Task.isSecure`.
LAB `userdebug` with `ro.debuggable=0` was the motivating case.
**Alternatives rejected**: Magisk `services.jar` dex patchers as shipping **Alternatives rejected**: Magisk `services.jar` dex patchers as shipping
mechanism. mechanism.
@@ -27,27 +27,39 @@
- [ ] Activation documented and completed on LAB - [ ] Activation documented and completed on LAB
- [ ] DenyList-class hides root from a test/banking package - [ ] DenyList-class hides root from a test/banking package
## Gate E — Banking ## Gate E — Banking *(deferred — not MVP)*
- [ ] Island present; Swissquote PASS in managed profile workflow - [ ] *(Later)* Island present; Swissquote PASS in managed profile workflow
- [ ] Yuh recorded; not worse than GOLDEN for packaging/spoofing reasons - [ ] *(Later)* Yuh recorded; not worse than GOLDEN for packaging/spoofing
- [ ] Banks do not receive root - [ ] *(Later)* Banks do not receive root
## Gate F — Usability / WebView ## Gate F — Usability / WebView
- [ ] Bundled AXP categories present or documented impossible - [ ] Bundled AXP categories present or documented impossible/deferred
- [ ] Versions newest workable (except Phonesky rule) - [ ] Versions newest workable (except Phonesky rule)
- [ ] WebView default LOS; chooser unlocked - [ ] WebView default LOS; chooser unlocked
## Gate G — Hardening / signing class ## Gate G — Hardening / signing + kernel
- [ ] GOLDEN-class hardening/signing/AVB posture present **or** each gap - [ ] Project **release / definitive signing keys** on ship images/OTAs (not
documented as impossible on this stack (no silent omit) AOSP test-keys); Phonesky / update identity on that key
- [ ] Staged known-good images retained for bisect - [ ] GOLDEN-class **userspace** hardening + AVB *build* posture as applicable
on unlocked sunfish **or** each in-scope gap documented (no silent omit)
- [x] Updated LOS23-compatible sunfish kernel + hardening attribution list
(FR-017 / SC-010) — see `docs/009-gate-g-kernel-attribution.md`
(Stage1 + combo4 + Clang FORTIFY; LAB 2026-09-29)
- [x] Staged known-good images retained for bisect
(`20260929T123401Z-axp-stage1-combo4`, prior combo4-fortify, GOODMAKE)
- [x] Localization/location AXP-vs-LOS compare **not** required yet (FR-018)
- [x] Bootloader relock **not** a Gate G requirement (AXP/LOS unlocked norm;
IOdes-class lock is non-goal) — see research 2026-09-29
## Non-gates ## Non-gates
- KernelSU presence or SusFS - KernelSU presence or SusFS
- FLAG_SECURE override (optional) - FLAG_SECURE override (optional)
- Custom AXP backup ROM feature - Custom AXP backup ROM feature
- Banking / Swissquote / Yuh (deferred owner future)
- Localization hardening policy freeze (FR-018 later)
- Yuh PASS if GOLDEN attestation path still FAILs equivalently - Yuh PASS if GOLDEN attestation path still FAILs equivalently
- Bootloader lock / relock (IOdes-class); unlocked sunfish is the ship model
+11 -7
View File
@@ -6,11 +6,13 @@
## Summary ## Summary
Resurrect **AXP.OS Pro on LOS22 / GOLDEN** as a single shippable LineageOS Resurrect **AXP.OS Pro on LOS22 / GOLDEN** as an **updated AXP-class clone**
23.2 / Android 16 `sunfish` ROM on **LAB**: vanilla `003` base + full AXP on LineageOS 23.2 / Android 16 `sunfish` (**LAB**): vanilla `003` base + AXP
capability set (Google stack, selectable spoofing, Island banking workflow, capability set (Google stack, selectable spoofing, selectable call-recording
selectable call-recording unlock, Magisk like GOLDEN, bundled usability apps, unlock, Magisk like GOLDEN, bundled usability apps / options surface,
GOLDEN-class hardening/signing/AVB posture). GOLDEN-class userspace hardening/signing/AVB, **updated hardened sunfish
kernel**). **Banking/Island/Swissquote validation deferred** (owner future).
**Localization hardening AXP-vs-LOS compare deferred** (FR-018).
**Explicit exceptions:** LOS23 WebView default (Aosmium EOL) with unlocked **Explicit exceptions:** LOS23 WebView default (Aosmium EOL) with unlocked
provider chooser; Phonesky = project's recent patched artifact (reconsider if provider chooser; Phonesky = project's recent patched artifact (reconsider if
@@ -22,7 +24,9 @@ packaging where they already match; replace the KernelSU ship path with
AXP-style Magisk preinstall (newest Magisk); pin newest stable microG and AXP-style Magisk preinstall (newest Magisk); pin newest stable microG and
bundled apps; keep Phonesky on the existing host pipeline; measure Magisk bundled apps; keep Phonesky on the existing host pipeline; measure Magisk
“preinstalled but not fully activated” behaviour on GOLDEN before freezing LAB “preinstalled but not fully activated” behaviour on GOLDEN before freezing LAB
setup docs. setup docs; port attributable kernel hardening onto current LOS23 sunfish
kernel; stage userspace hardening; leave location-policy compare for later.
## Technical Context ## Technical Context
@@ -88,7 +92,7 @@ specs/009-axp-los23-port/
│ ├── magisk-preinstall.md │ ├── magisk-preinstall.md
│ ├── webview-surface.md │ ├── webview-surface.md
│ └── acceptance-gates.md │ └── acceptance-gates.md
└── tasks.md # /speckit-tasks (not this command) └── tasks.md # /speckit-tasks (generated)
``` ```
### Host + tree ### Host + tree
+13 -10
View File
@@ -49,29 +49,31 @@ adb -s 11231JEC207477 shell settings get secure extendrom_call_recording
3. Configure DenyList-class hide for a banking package **before** first open. 3. Configure DenyList-class hide for a banking package **before** first open.
4. Save Magisk version + screenshots under `…/magisk/`. 4. Save Magisk version + screenshots under `…/magisk/`.
## 4. Banking (Gate E) ## 4. Banking (Gate E) — deferred
1. Aurora → install Swissquote → **do not open**. Skip for MVP. Owner future. Magisk DenyList can still be smoke-tested with a
2. DenyList → Island clone → first launch in Island → login. non-bank test package under Gate D.
3. Repeat attempt for Yuh; classify PASS / attestation-like-GOLDEN /
packaging regression.
4. Confirm banks lack root.
## 5. Usability + WebView (Gate F) ## 5. Usability + WebView (Gate F)
1. Confirm bundled categories vs pin record. 1. Confirm bundled categories vs pin record.
2. `adb shell cmd webviewupdate current` → LOS default; chooser still listed. 2. `adb shell cmd webviewupdate current` → LOS default; chooser still listed.
## 6. Hardening / signing class (Gate G) ## 6. Hardening / signing + kernel (Gate G)
1. Run the hardening/signing checklist defined for this build stage. 1. Userspace: follow `docs/009-hardening-signing-checklist.md` (release keys;
2. Any missing GOLDEN-class item → written impossible/gap note (no silent unlocked AVB; no bootloader lock).
2. Kernel: `docs/009-gate-g-kernel-attribution.md` (Stage1+combo4+FORTIFY).
2. Record kernel rev + hardening patches applied/skipped (FR-017).
3. Any missing GOLDEN-class item → written impossible/gap note (no silent
skip). skip).
4. Do **not** block on localization AXP-vs-LOS compare (FR-018 later).
## Pass criteria ## Pass criteria
All boxes in [acceptance-gates.md](./contracts/acceptance-gates.md) for the All boxes in [acceptance-gates.md](./contracts/acceptance-gates.md) for the
claimed stage. Full resurrection acceptance = Gates A–G. claimed stage **except Gate E** (deferred). Full clone acceptance for current
owner priority = Gates A–D + F–G.
## Notes ## Notes
@@ -79,3 +81,4 @@ claimed stage. Full resurrection acceptance = Gates A–G.
- Call-recording legality is the user’s responsibility. - Call-recording legality is the user’s responsibility.
- If project Phonesky misbehaves vs GOLDEN needs → stop and reconsider; do - If project Phonesky misbehaves vs GOLDEN needs → stop and reconsider; do
not accept Gate B. not accept Gate B.
- Localization hardening compare is explicitly postponed.
+132 -7
View File
@@ -111,15 +111,140 @@ Document impossible-on-sunfish gaps rather than silent omit.
**Alternatives considered**: Defer all of `011`–`013` — rejected by owner. **Alternatives considered**: Defer all of `011`–`013` — rejected by owner.
One mega-commit everything — rejected (attribution / bisect). One mega-commit everything — rejected (attribution / bisect).
## Decision: Banking workflow = GOLDEN Magisk DenyList + Island ## Decision: Release keys yes; bootloader lock no (2026-09-29)
**Decision**: Restore constitution banking steps with **Magisk DenyList** **Decision**:
(not KSU profiles) + Island: Aurora install → do not open → DenyList → clone
to Island → first launch in Island. Swissquote PASS hard gate; Yuh honest vs 1. **Sign with definitive / project release keys** — required for Gate G
GOLDEN attestation FAIL. userspace. Move off AOSP test-keys for ship images/OTAs; keep Phonesky and
update identity on that key. Keys live only on `rom-dev01`
(`/srv/rom/secrets/`), never in the laptop Git tree.
2. **Do not pursue bootloader relock** as a ship goal. AXP and LineageOS do
not lock the bootloader as their normal product path; Magisk preroot and
recovery sideload assume unlocked. IOdes-style locked bootloader is an
explicit non-goal for this track.
**Rationale**: Clone AXP/LOS norms; avoid a locked-device model that fights
Magisk EXTENDROM preroot. Signing still matters for update trust and
PRESIGNED / same-identity sideload.
**Alternatives considered**: IOdes-class locked AVB ship — rejected by owner
(2026-09-29). Skip release keys entirely — rejected (Gate G / FR signing).
## Decision: Banking workflow deferred (owner future)
**Decision (2026-09-17)**: Banking / Island / Swissquote / Yuh validation is
**deferred**. Not an MVP acceptance gate. Magisk DenyList-class capability
still ships (general). Owner may resume US3 later.
**Rationale**: Owner wants updated AXP clone (features, options, hardening,
security, kernel) first; banks are a personal future problem.
**Alternatives considered**: Keep Swissquote as hard gate — rejected by owner.
## Decision: Updated hardened LOS23 sunfish kernel (FR-017)
**Decision (refined 2026-09-18):** Ship current LineageOS 23.2–compatible
`sunfish` kernel (`msm-4.14`) with attributable hardening. **Not** an
unrelated Graphene A13 / stock prebuilt as base. Record applied/skipped list
in `docs/009-kernel-hardening-map.md`.
**Source priority (owner):**
1. **AXP** — maximum priority; clone their sunfish kernel hardening first
(build sources / OTA oracle / GOLDEN). Inherited Divest-era commits that
already live *inside* the AXP stack are fine as part of that clone; do
**not** separately mine DivestOS EOL trees.
2. **LOS 23.2** — inventory what the lineage kernel already has; avoid
duplicate work.
3. **IodéOS** — LOS fork, more hardened/deblobbed; clone sources on
`rom-dev01` and diff for 4.14-safe ideas worth porting.
4. **GrapheneOS** — optional extras only when they make sense on non-GKI
4.14 sunfish: prefer portable techniques from modern Pixel kernels /
docs; historical sunfish A13 patches only if still clearly valuable.
Do **not** treat GOS A13 sunfish tree as the base.
**Out of hunt:** DivestOS as a separate research target (EOL).
**Rationale**: AXP-first resurrection; GOS/Iodé as additive; honest skips on
4.14.
**Inventory (2026-09-18):** LOS `msm-4.14` @ `28f9290ae067` (`4.14.357-openela`).
AXP `kernel_patches` @ `df3cab2155b3` on host. Graphene/AH sequential probe:
OK=30 / SKIP_ALREADY=4 / FAIL=12. Iodé `v5-staging` manifest has no sunfish
4.14 kernel fork. Stage 1 allowlist + apply script under
`vendor/sunfish-a16/patches/009-kernel-hardening/`. CVE mega deferred.
Ledger: `docs/009-kernel-hardening-map.md`.
## Decision: Location / localization hardening — do not extra-cap (FR-018)
**Decision (2026-09-18):** For this Magisk track, **do not** port AXP-class
aggressive location lockdown (kernel or userspace) beyond stock LOS 23.2
defaults. No “non-hardening location” Settings toggle for MVP — that is
extra product surface; simpler and matches owner GPS frustration to **leave
LOS location behaviour**. FR-018 compare doc may still note what AXP did,
for later curiosity, without shipping those caps.
**Alternatives considered**: (1) Clone AXP location caps — rejected (slow /
frustrating fixes). (2) Owner toggle to disable caps — deferred; only if
LOS defaults prove insufficient and owner asks later.
## Decision: Bundled store client = Droidify; no NeoLauncher
**Decision (2026-09-17):** Ship **Droidify** as the F-Droid-class client
instead of stock F-Droid (lighter; many repos by default). Do **not**
integrate NeoLauncher — keep LOS Launcher3. Aurora remains for Play-sideload
workflow.
## Decision: No AXP external repos / OTA servers
**Decision:** Do not wire AXP market/F-Droid remote repos or AXP OTA update
endpoints. AXP infra is EOL / servers down — no product sense. Periodic
system-package refresh is **owner sideload** (FR-019), not network OTA.
## Decision: Sideloadable system package refreshes (FR-019)
**Decision (2026-09-17):** Final ROM MUST keep **microG**, **Magisk**,
**project Phonesky**, and other bundled system/privileged apps
**updateable without full reflash**, on a ~**2–3 month** cadence the owner
drives. Prefer PackageManager-compatible sideload (same signing identity;
PRESIGNED microG; continuous project key for Phonesky). Magisk: document
manager self-update and/or recovery/boot re-patch when APK sideload alone is
not enough. Deliver a short recovery/sideload runbook before ship; do **not**
reintroduce AXP OTA.
## Decision: Private DNS + Internet connectivity (sans axp.os)
**Decision:** Port AXP-class **Private DNS** provider presets. Keep AXP-class
**Internet connectivity** / special INTERNET permission behaviour where it
still fits LOS 23.2, but strip any hard dependency on `axp.os` branding or
endpoints.
## Decision: Restore Disable app on Android 16
**Decision:** Investigate and re-expose classic **Disable application** UX on
A16/LOS23 where Archive replaced or hid it. Owner prefers Disable over
Archive. Exact Settings/PackageManager patch path TBD from AXP/LOS delta
research — must remain attributable and reversible.
## Decision: AXP build sources as compile oracle
**Decision:** On `rom-dev01`, prefer cloning/checking out AXP **build** +
EXTENDROM (and axpos.org developer building docs already mirrored under
`/srv/rom/references/axp/archive/`) to see how Magisk preroot / product packages
are wired — before treating GOLDEN `adb` as the compile guide. GOLDEN stays the
runtime compatibility oracle.
**Decision**: Do **not** freeze localization/location lockdown policy in this
feature’s acceptance. Later compare how much AXP “capea” location vs stock
LOS (owner historically complains AXP is aggressive). Until then, avoid
inventing extra location lockdown beyond what the clone already implies;
matrix row = deferred.
**Alternatives considered**: Blind-copy all AXP location patches now —
rejected (owner wants a deliberate compare first).
**Alternatives considered**: Rootless-only banking gate — incomplete vs AXP.
Insular rename — GOLDEN runs Island.
## Decision: Version freshness policy ## Decision: Version freshness policy
+165 -67
View File
@@ -4,7 +4,8 @@
**Created**: 2026-09-16 **Created**: 2026-09-16
**Status**: Clarified — ready for `/speckit-plan` **Status**: Spec clarified 2026-09-17 (banking deferred; kernel hardening in;
localization compare later) — tasks updated; ready for `/speckit-implement`
**Input**: User description: "009-axp based LOS22 feature port to LOS23 — **Input**: User description: "009-axp based LOS22 feature port to LOS23 —
take vanilla LineageOS 23 for sunfish and integrate the full AXP.OS (LOS22 / take vanilla LineageOS 23 for sunfish and integrate the full AXP.OS (LOS22 /
@@ -24,6 +25,29 @@ with that Phonesky; signature spoofing and call recording **selectable**
existing functional kernels if useful; otherwise everything equal to existing functional kernels if useful; otherwise everything equal to
AXP/GOLDEN. AXP/GOLDEN.
**Owner clarifications (2026-09-17):** Primary ship interest = an **updated
AXP clone on LOS23** (features, options, userspace hardening/security, updated
hardened sunfish kernel). Banking / Island / Swissquote / Yuh **deferred**
(owner future). Localization AXP-vs-LOS compare **later** (FR-018). Prefer AXP
**build sources + docs** on `rom-dev01` as the compile oracle (how Magisk /
product packaging is wired); GOLDEN `adb` for *runtime* behaviour, not the
first place to reverse “how to build”. **Do not** ship NeoLauncher — keep LOS
Launcher3. Prefer **Droidify** over stock F-Droid client (lighter; many repos
by default) unless packaging forces otherwise. **Do not** integrate AXP
**external** market repos or AXP **OTA** servers (EOL / servers down — no
sense). **Do** clone AXP-class **Private DNS** presets (many provider entries).
**Do** keep AXP-class **Internet connectivity** / special INTERNET permission
UX where practical, but **do not** hard-wire `axp.os` branding/endpoints.
**Wanted:** restore classic **Disable app** behaviour on Android 16 (LOS/AOSP
replaced much of this with **Archive**, which the owner dislikes) — research
and re-expose Disable where still feasible on A16. **Later (final ROM):**
system components (microG, Magisk, project Phonesky, other bundled system
apps) MUST remain **sideload-updatable** on a shipping build — owner plans
refreshes every **2–3 months** without a full ROM reflash. Packaging MUST
preserve update paths (same signing identity / PRESIGNED where applicable;
document Magisk update path separately from APK sideload if boot-preroot).
## Context / non-goals ## Context / non-goals
**Product intent (this feature is normative for the AXP resurrection ship):** **Product intent (this feature is normative for the AXP resurrection ship):**
@@ -83,30 +107,39 @@ AXP/GOLDEN.
- Building a custom AXP-style backup/restore ROM feature (personal backup - Building a custom AXP-style backup/restore ROM feature (personal backup
remains an external user tool if needed). remains an external user tool if needed).
- Claiming Yuh hardware-attestation PASS if GOLDEN itself currently FAILs that - Claiming Yuh hardware-attestation PASS if GOLDEN itself currently FAILs that
path — reproduce GOLDEN's *workflow* and document attestation parity honestly. path — banking attestation is deferred with the banking workflow (owner
future); do not treat Swissquote/Yuh as ship gates here.
- Deleting or rewriting the standby KernelSU / split-roadmap specs - Deleting or rewriting the standby KernelSU / split-roadmap specs
(`004`–`008`/`007`) — they remain valid on the standby branch/track. (`004`–`008`/`007`) — they remain valid on the standby branch/track.
- Shipping KernelSU-Next as this track’s root (out of ship here; Magisk is - Shipping KernelSU-Next as this track’s root (out of ship here; Magisk is
the resurrection root). Optional later reuse of `007` kernels is allowed the resurrection root). Optional later reuse of `007` kernels is allowed
under FR-016 but MUST NOT delay Magisk acceptance. under FR-016 but MUST NOT delay Magisk acceptance.
- Resolving localization/location-hardening policy vs LOS defaults in this
feature’s acceptance (deferred research FR-018 — compare AXP vs LOS later).
- Integrating AXP **external** F-Droid/market repos or AXP **OTA** update
servers (EOL; servers down — no sense).
- Shipping NeoLauncher.
- Hard-wiring `axp.os` branding/endpoints into Internet-connectivity /
network UX (keep the permission/connectivity *behaviour* where practical).
## User Scenarios & Testing *(mandatory)* ## User Scenarios & Testing *(mandatory)*
### User Story 1 - AXP resurrection daily-driver on LAB (Priority: P1) ### User Story 1 - AXP resurrection daily-driver on LAB (Priority: P1)
As the owner, after flashing one LAB build produced under this feature, the As the owner, after flashing one LAB build produced under this feature, the
phone boots and presents the same class of daily-driver capabilities I rely on phone boots and presents an **updated AXP-class clone on LOS23**: Google
with AXP on GOLDEN (Google compatibility stack, Play, managed banking profile compatibility stack, Play, selectable call recording unlock, Magisk root/hide
workflow, selectable call recording unlock, Magisk root/hide workflow like workflow like GOLDEN, AXP options/settings surface, userspace hardening /
GOLDEN, AXP hardening/signing posture, and AXP bundled usability apps — signing posture, bundled usability apps, and an updated hardened sunfish
except WebView as defined here), without a checklist of deferred AXP features. kernel — except WebView as defined here. Banking/Island validation is
**out of this story’s acceptance** (deferred to owner future / US3).
**Why this priority**: This is the whole reason for the resurrection. **Why this priority**: This is the whole reason for the resurrection.
**Independent Test**: Flash one `009` build to LAB; walk the GOLDEN daily **Independent Test**: Flash one `009` build to LAB; walk the GOLDEN daily
checklist (microG Self-Check, Play open/install, Island profile, call-record checklist minus banking (microG Self-Check, Play open/install, call-record
toggle off/on, spoofing toggle off/on, Magisk present in AXP-like install toggle off/on, spoofing toggle off/on, Magisk present in AXP-like install
state, bundled apps present, hardening/signing checks that GOLDEN implies); state, bundled apps present, hardening/signing + kernel hardening checks);
compare against `002` inventory categories. compare against `002` inventory categories.
**Acceptance Scenarios**: **Acceptance Scenarios**:
@@ -115,13 +148,13 @@ compare against `002` inventory categories.
**When** the `009` image is flashed, **Then** the device boots to home and **When** the `009` image is flashed, **Then** the device boots to home and
cellular/Wi‑Fi/basic hardware still work as in the vanilla smoke. cellular/Wi‑Fi/basic hardware still work as in the vanilla smoke.
2. **Given** that build, **When** I compare shipped capability categories to 2. **Given** that build, **When** I compare shipped capability categories to
GOLDEN/AXP Pro (Google stack, Play, selectable spoofing, managed profile GOLDEN/AXP Pro (Google stack, Play, selectable spoofing, selectable call
tool, selectable call recording unlock, Magisk/DenyList-class path, recording unlock, Magisk/DenyList-class path, DNS/hosts privacy helpers if
DNS/hosts privacy helpers if present on GOLDEN, F‑Droid/Aurora/launcher/ present on GOLDEN, F‑Droid/Aurora/launcher/gallery-class bundled apps,
gallery-class bundled apps, hardening/signing/AVB class), **Then** no userspace hardening/signing/AVB class, kernel update + hardening class),
category is left as “decide / later” without an explicit obsolescence note **Then** no category is left as “decide / later” without an explicit
tied to Android 16 / sunfish impossibility (WebView exception allowed per obsolescence or **deferred** note (WebView exception FR-015; banking
FR-015). deferred FR-006/FR-007; localization hardening compare deferred FR-018).
3. **Given** Git and host artifact policy, **When** inspected, **Then** no 3. **Given** Git and host artifact policy, **When** inspected, **Then** no
proprietary Phonesky APK bytes or secrets were committed. proprietary Phonesky APK bytes or secrets were committed.
@@ -165,22 +198,22 @@ behaviour; push notifications smoke for a known test app.
--- ---
### User Story 3 - Banking profile workflow parity (Priority: P1) ### User Story 3 - Banking profile workflow parity (Priority: P3 — deferred)
As the owner, I can recreate the GOLDEN banking layout on LAB: install via the As the owner, **later** I can recreate the GOLDEN banking layout on LAB:
same store path I use on AXP, place banks in Island, and keep banking apps Island + Magisk DenyList-class hiding + Swissquote/Yuh. This is explicitly
free of root via Magisk DenyList-class hiding like GOLDEN. **not** required to accept the AXP-on-LOS23 clone for functionalities,
options, hardening, security, or kernel work.
**Why this priority**: Compatibility hard gates; daily-driver without banks **Why this priority**: Owner (2026-09-17) — banking is a future personal
fails the resurrection. problem; do not block the updated AXP clone on bank gates.
**Independent Test**: Install Swissquote (hard gate) and attempt Yuh; run **Independent Test** *(when owner resumes)*: Install Swissquote and attempt
inside Island; Magisk DenyList (or AXP-equivalent) applied; record PASS/FAIL Yuh; Island; Magisk DenyList; record PASS/FAIL honestly vs GOLDEN.
honestly vs GOLDEN.
**Acceptance Scenarios**: **Acceptance Scenarios** *(deferred — not ship gates)*:
1. **Given** Island on LAB, **When** I follow the documented GOLDEN banking 1. **Given** Island on LAB, **When** the owner follows the GOLDEN banking
install order, **Then** Swissquote reaches a usable logged-in state in the install order, **Then** Swissquote reaches a usable logged-in state in the
managed profile. managed profile.
2. **Given** Yuh on the same workflow, **When** tested, **Then** either it 2. **Given** Yuh on the same workflow, **When** tested, **Then** either it
@@ -211,32 +244,38 @@ recording unlock off (restricted) and on (record allowed).
with GOLDEN measurement, using the newest Magisk stable that works on this with GOLDEN measurement, using the newest Magisk stable that works on this
stack. stack.
2. **Given** Magisk setup completed on LAB, **When** DenyList-class hiding is 2. **Given** Magisk setup completed on LAB, **When** DenyList-class hiding is
configured for a banking package, **Then** that package does not receive configured for a test package, **Then** that package does not receive root
root. (banking packages deferred with US3).
3. **Given** selectable call recording unlock, **When** OFF, **Then** stock 3. **Given** selectable call recording unlock, **When** OFF, **Then** stock
LOS country/carrier style restrictions apply; **When** ON, **Then** Dialer LOS country/carrier style restrictions apply; **When** ON, **Then** Dialer
can record on LAB without that block. can record on LAB without that block.
--- ---
### User Story 5 - AXP usability, privacy, hardening (Priority: P2) ### User Story 5 - AXP usability, options, hardening + kernel (Priority: P1)
As the owner, the ROM ships the rest of the AXP usability/privacy surface and As the owner, the ROM ships the rest of the AXP usability/privacy surface,
GOLDEN-class hardening/signing/AVB posture at newest workable stables, with AXP-class options/settings behaviour, GOLDEN-class userspace hardening /
LOS23 WebView default and an unlocked provider chooser. signing / AVB posture, and an **updated LOS 23.2 sunfish kernel with
attributable hardening** (AXP / Graphene / Divest class where still valid on
non-GKI 4.14), at newest workable stables, with LOS23 WebView default and an
unlocked provider chooser. Localization/location-hardening delta vs LOS is
**out of scope for now** (FR-018 later).
**Why this priority**: Completes resurrection beyond Google/banking/root core. **Why this priority**: Owner (2026-09-17) — this is the core of “clon
actualizado de AXP”, ahead of banking.
**Independent Test**: Package list vs `002`; open bundled apps; confirm **Independent Test**: Package list vs `002`; open bundled apps; confirm
default WebView is LOS and another WebView can be selected if installed; default WebView is LOS; userspace hardening/signing checks; kernel version /
hardening/signing checks agreed for GOLDEN-class parity. hardening patch attribution recorded for the accepted build.
**Acceptance Scenarios**: **Acceptance Scenarios**:
1. **Given** the `009` build, **When** I list bundled system/user apps in the 1. **Given** the `009` build, **When** I list bundled system/user apps in the
AXP usability set, **Then** each GOLDEN/AXP category is present or AXP usability set, **Then** each GOLDEN/AXP category is present or
explicitly marked impossible on Android 16/sunfish with rationale explicitly marked impossible on Android 16/sunfish with rationale
(Aosmium absence is the allowed WebView exception). (Aosmium absence is the allowed WebView exception; banking tools may ship
as packages without Gate E validation).
2. **Given** Developer options, **When** I open WebView implementation, 2. **Given** Developer options, **When** I open WebView implementation,
**Then** LOS23 WebView is available/default and the chooser is not locked **Then** LOS23 WebView is available/default and the chooser is not locked
against future alternate providers. against future alternate providers.
@@ -244,9 +283,13 @@ hardening/signing checks agreed for GOLDEN-class parity.
FR-004) are recorded, **Then** each is newest stable that still runs on FR-004) are recorded, **Then** each is newest stable that still runs on
the ROM. the ROM.
4. **Given** GOLDEN/AXP hardening and signing expectations, **When** the 4. **Given** GOLDEN/AXP hardening and signing expectations, **When** the
accepted build is evaluated, **Then** equivalent class of protections / accepted build is evaluated, **Then** equivalent class of userspace
signing posture is present or each gap is documented as impossible on protections / signing posture is present or each gap is documented as
this stack — not silently dropped. impossible on this stack — not silently dropped.
5. **Given** the LOS 23.2 sunfish kernel tree, **When** the accepted boot
image is built, **Then** it is an updated lineage/sunfish-compatible
kernel (not an unrelated prebuilt) with attributable hardening patches
applied or each skipped hardening item documented.
--- ---
@@ -291,6 +334,9 @@ work when published.
silently fall back to FakeStore. silently fall back to FakeStore.
- Alternate WebView installed by user later: selection must remain possible; - Alternate WebView installed by user later: selection must remain possible;
default remains LOS23 WebView until the user changes it. default remains LOS23 WebView until the user changes it.
- Localization/location hardening: AXP historically more aggressive than LOS
in places the owner dislikes; **do not decide** AXP-vs-LOS location policy
in this feature’s acceptance — compare later (FR-018).
## Requirements *(mandatory)* ## Requirements *(mandatory)*
@@ -298,9 +344,11 @@ work when published.
- **FR-001**: The project MUST produce a flashable LineageOS 23.2 / Android 16 - **FR-001**: The project MUST produce a flashable LineageOS 23.2 / Android 16
`sunfish` build that resurrects the AXP.OS on LOS22 / GOLDEN capability set `sunfish` build that resurrects the AXP.OS on LOS22 / GOLDEN capability set
(including Magisk-class root and GOLDEN-class hardening / signing / AVB (including Magisk-class root, GOLDEN-class userspace hardening / signing /
posture), without leaving AXP capabilities as open “decide” omissions AVB posture, and an updated hardened sunfish kernel per FR-017), without
except the WebView exception in FR-015. leaving AXP capabilities as open “decide” omissions except documented
exceptions/deferrals (WebView FR-015; banking FR-006/FR-007; localization
compare FR-018).
- **FR-002**: Version selection for system and bundled components (except - **FR-002**: Version selection for system and bundled components (except
Phonesky per FR-004) MUST prefer the newest stable (or newest tagged Phonesky per FR-004) MUST prefer the newest stable (or newest tagged
pre-release upstream currently ships) that still works on sunfish / pre-release upstream currently ships) that still works on sunfish /
@@ -316,10 +364,13 @@ work when published.
microG + Phonesky path (owner-controlled toggle; default safe/off or microG + Phonesky path (owner-controlled toggle; default safe/off or
project-documented default). Spoofing MUST remain restricted/controlled — project-documented default). Spoofing MUST remain restricted/controlled —
not unrestricted platform spoofing for all apps. not unrestricted platform spoofing for all apps.
- **FR-006**: The ROM MUST support the GOLDEN managed-profile banking workflow - **FR-006**: *(Deferred — owner future)* The ROM SHOULD support the GOLDEN
(Island as measured on GOLDEN) including Swissquote PASS as a hard gate. managed-profile banking workflow (Island as measured on GOLDEN) including
- **FR-007**: Banking applications MUST run without being granted root Swissquote PASS. This is **not** an acceptance gate for the AXP clone MVP.
(Magisk DenyList-class hiding). - **FR-007**: *(Deferred — owner future)* Banking applications SHOULD run
without being granted root (Magisk DenyList-class hiding). Magisk MUST still
provide DenyList-class capability for general use (FR-010); bank-specific
validation is deferred with FR-006.
- **FR-008**: The ROM MUST include **selectable** AXP-equivalent call - **FR-008**: The ROM MUST include **selectable** AXP-equivalent call
recording unlock (toggle; OFF = stock LOS restrictions, ON = unlock). recording unlock (toggle; OFF = stock LOS restrictions, ON = unlock).
- **FR-009**: The ROM MUST include the AXP usability/privacy bundled surface - **FR-009**: The ROM MUST include the AXP usability/privacy bundled surface
@@ -329,12 +380,12 @@ work when published.
- **FR-010**: The ROM MUST ship **Magisk** like GOLDEN: newest Magisk stable - **FR-010**: The ROM MUST ship **Magisk** like GOLDEN: newest Magisk stable
that works on this stack, in an AXP-like preinstalled but not fully that works on this stack, in an AXP-like preinstalled but not fully
activated layout (exact first-boot/setup behaviour measured from GOLDEN), activated layout (exact first-boot/setup behaviour measured from GOLDEN),
with DenyList-class hiding sufficient for FR-007. KernelSU is **out of with DenyList-class hiding available. KernelSU is **out of ship for now**
ship for now** (not the resurrection root path). (not the resurrection root path).
- **FR-011**: The feature acceptance gate MUST include GOLDEN/AXP-class - **FR-011**: The feature acceptance gate MUST include GOLDEN/AXP-class
hardening and signing / AVB posture (as applicable on LOS 23.2 / sunfish), userspace hardening and signing / AVB posture (as applicable on LOS 23.2 /
not only apps and Google compatibility. Gaps MUST be documented as sunfish), not only apps and Google compatibility. Gaps MUST be documented
impossible on this stack rather than silently omitted. as impossible on this stack rather than silently omitted.
- **FR-012**: Acceptance flashing MUST use LAB only; GOLDEN MUST remain - **FR-012**: Acceptance flashing MUST use LAB only; GOLDEN MUST remain
unmodified by this feature's work. unmodified by this feature's work.
- **FR-013**: Each shipped capability MUST be attributable (what upstream or - **FR-013**: Each shipped capability MUST be attributable (what upstream or
@@ -354,6 +405,32 @@ work when published.
Such reuse is **optional** (“si se puede, ok”), MUST NOT become an Such reuse is **optional** (“si se puede, ok”), MUST NOT become an
acceptance requirement for Magisk resurrection, and MUST NOT imply KernelSU acceptance requirement for Magisk resurrection, and MUST NOT imply KernelSU
is back in the ship path unless a future spec explicitly says so. is back in the ship path unless a future spec explicitly says so.
- **FR-017**: The ship MUST use an **updated** LineageOS 23.2–compatible
`sunfish` kernel (current lineage/kernel_google_sunfish or project-tracked
equivalent — not an unrelated Graphene A13 / stock A13 prebuilt), with
**attributable kernel hardening** ported from AXP / Graphene / Divest where
still valid on non-GKI 4.14. Skipped items MUST be documented with rationale.
- **FR-018**: *(Deferred research — look later)* Compare how AXP/GOLDEN
hardens or constrains **localization / location** (network location,
backends, permissions, privacy toggles) versus stock LineageOS 23 behaviour.
Owner historically finds AXP aggressive here; do **not** freeze a
clone-or-LOS policy in this feature’s acceptance until that comparison
exists. Default until then: do not silently invent extra location lockdown
beyond what the rest of the AXP clone already implies; record the open
question in the capability matrix.
- **FR-019**: On a **final** shipping `009` image, owner MUST be able to
refresh bundled **system / privileged** packages by **sideload** (or the
documented Magisk-equivalent path) roughly every **2–3 months**, without
requiring a full ROM reflash for routine bumps of at least: **microG
(GmsCore / companions)**, **Magisk**, **project Phonesky**, and other
project-bundled system apps that the capability inventory treats as
updatable. Packaging MUST preserve PackageManager update eligibility
(matching signing identity; **PRESIGNED** where upstream expects
self-updates; project-key continuity for Phonesky). Magisk MAY use its
normal manager / boot-image update flow when APK-only sideload is
insufficient — that path MUST be documented in the recovery/sideload
runbook. This does **not** restore AXP external repos or OTA (still
forbidden); updates are owner-driven sideload / recovery packages.
### Key Entities ### Key Entities
@@ -364,13 +441,16 @@ work when published.
(resurrection target). (resurrection target).
- **Version pin record**: Documented package/component versions chosen for the - **Version pin record**: Documented package/component versions chosen for the
accepted `009` build (newest workable stable each; Phonesky = project accepted `009` build (newest workable stable each; Phonesky = project
artifact id/version). artifact id/version; kernel rev + hardening patch list).
- **Phonesky host artifact**: Project patched/signed Play APK and permission - **Phonesky host artifact**: Project patched/signed Play APK and permission
metadata kept outside Git on the build host. metadata kept outside Git on the build host.
- **Magisk preinstall state**: Measured GOLDEN/AXP layout for Magisk present - **Magisk preinstall state**: Measured GOLDEN/AXP layout for Magisk present
but not fully activated, plus LAB setup steps. but not fully activated, plus LAB setup steps.
- **LAB acceptance build**: Flashable image set + hashes + smoke/banking / - **Hardened sunfish kernel**: Updated LOS23-compatible kernel build +
Magisk / hardening results for this feature. attributed hardening patch set for FR-017.
- **LAB acceptance build**: Flashable image set + hashes + smoke / Magisk /
userspace+kernel hardening results for this feature (banking evidence
optional/deferred).
## Success Criteria *(mandatory)* ## Success Criteria *(mandatory)*
@@ -380,15 +460,16 @@ work when published.
hardware smoke within the same session as flash (owner can place a call, hardware smoke within the same session as flash (owner can place a call,
use Wi‑Fi, and unlock the device). use Wi‑Fi, and unlock the device).
- **SC-002**: 100% of AXP resurrection capability categories from the agreed - **SC-002**: 100% of AXP resurrection capability categories from the agreed
inventory are either demonstrated on LAB or explicitly documented as inventory are either demonstrated on LAB, marked deferred (banking,
impossible on Android 16/sunfish — zero silent omissions (WebView exception localization compare), or explicitly documented as impossible on Android
allowed only as FR-015). 16/sunfish — zero silent omissions (WebView exception allowed only as
FR-015).
- **SC-003**: With spoofing enabled as required, microG Self-Check required - **SC-003**: With spoofing enabled as required, microG Self-Check required
items and project Phonesky open/install succeed on LAB for ordinary apps on items and project Phonesky open/install succeed on LAB for ordinary apps on
the first accepted build candidate (or within one documented fix iteration). the first accepted build candidate (or within one documented fix iteration).
- **SC-004**: Swissquote managed-profile + Magisk DenyList-class workflow - **SC-004**: *(Deferred with US3)* Swissquote managed-profile + Magisk
PASSes on LAB; Yuh result is recorded and is not worse than GOLDEN for DenyList-class workflow PASSes on LAB when owner resumes banking; not
packaging/spoofing reasons. required for AXP-clone MVP acceptance.
- **SC-005**: Every bundled/system app version in the acceptance record - **SC-005**: Every bundled/system app version in the acceptance record
(except Phonesky) is newer than or equal to GOLDEN's measured version, (except Phonesky) is newer than or equal to GOLDEN's measured version,
unless a newer release was tried and failed with a written regression note; unless a newer release was tried and failed with a written regression note;
@@ -399,7 +480,16 @@ work when published.
can be fully activated on LAB using documented steps measured from GOLDEN. can be fully activated on LAB using documented steps measured from GOLDEN.
- **SC-008**: Default WebView is LOS23's; provider selection remains available. - **SC-008**: Default WebView is LOS23's; provider selection remains available.
- **SC-009**: Owner can perform the GOLDEN-equivalent daily checklist on LAB - **SC-009**: Owner can perform the GOLDEN-equivalent daily checklist on LAB
without consulting a scatter of conflicting feature specs for what “ships”. **excluding deferred banking** without consulting a scatter of conflicting
feature specs for what “ships”.
- **SC-010**: Accepted build records kernel revision + hardening attribution
list (applied / skipped+why) satisfying FR-017.
- **SC-011**: Acceptance docs list, for each FR-019 component, the intended
update channel (sideload APK / recovery zip / Magisk self-update) and confirm
signing identity is not broken by packaging (PRESIGNED or project key as
applicable). Full 2–3 month refresh drill MAY wait until a final ROM
candidate; packaging regressions that block updates MUST be fixed before
ship.
## Assumptions ## Assumptions
@@ -408,11 +498,12 @@ work when published.
- GOLDEN inventory (`002`) plus AXP Pro docs are sufficient to enumerate the - GOLDEN inventory (`002`) plus AXP Pro docs are sufficient to enumerate the
capability set; live `adb` re-query of GOLDEN is allowed (especially Magisk capability set; live `adb` re-query of GOLDEN is allowed (especially Magisk
preinstall/activation details). preinstall/activation details).
- “AXP resurrection” means Magisk + GOLDEN-class hardening/signing/AVB + full - “AXP resurrection” / updated clone means Magisk + GOLDEN-class userspace
AXP user surface on LOS23, with only the stated WebView and Phonesky-artifact hardening/signing/AVB + AXP user/options surface + updated hardened kernel
exceptions. on LOS23, with WebView, Phonesky-artifact, banking-deferral, and
- Island remains the managed-profile tool because that is what GOLDEN localization-compare exceptions as stated.
actually runs (AXP docs saying “Insular” are historical naming). - Island / banking validation is owner-future; shipping Island as a bundled
app is optional and does not imply Gate E.
- FakeStore is never the primary Play path. - FakeStore is never the primary Play path.
- Selectable spoofing and selectable call recording follow the same class of - Selectable spoofing and selectable call recording follow the same class of
Developer Options UX already explored in `005`/`008` runbooks where useful. Developer Options UX already explored in `005`/`008` runbooks where useful.
@@ -424,3 +515,10 @@ work when published.
- Prior KernelSU LAB experiments (including the two functional kernels already - Prior KernelSU LAB experiments (including the two functional kernels already
built) are **out of ship for now** but kept as recyclable assets: reuse if built) are **out of ship for now** but kept as recyclable assets: reuse if
helpful, never required for Magisk acceptance. helpful, never required for Magisk acceptance.
- Localization hardening AXP-vs-LOS comparison is intentionally postponed
(FR-018); do not block MVP on it.
- No AXP-style OTA or external repos; periodic system-package refresh is
**owner sideload** (FR-019), not a network updater in the ROM.
- microG stays PRESIGNED / replace-prebuilt so same-signer updates remain
possible; Phonesky stays on the project signing key across releases;
Magisk update docs cover manager + any boot re-patch when needed.
+339
View File
@@ -0,0 +1,339 @@
# Tasks: AXP LOS22 feature port to LOS23 (resurrection)
**Input**: Design documents from `/specs/009-axp-los23-port/`
**Prerequisites**: `plan.md`, `spec.md`, `research.md`, `data-model.md`,
`contracts/`, `quickstart.md`; constitution Magisk-resurrection track;
standby KernelSU track (`004`–`008`/`007`) remains untouched
**Tests**: LAB acceptance only (Gates A–G in `contracts/acceptance-gates.md`).
No unit/contract TDD suite requested — validation tasks are adb/flash checks.
**Organization**: Setup → Foundational → US1 / US2 / US4 / US5 (MVP) → US6 →
Polish. **US3 banking is deferred (P3)** — skip for MVP. US5 includes
**kernel update + hardening** (FR-017). Localization AXP-vs-LOS (FR-018) =
look later, not a ship gate. Full US1 Independent Test needs US2+US4+US5
(not banking).
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no incomplete deps)
- **[Story]**: US1…US6 from `spec.md`
- Include exact file paths in descriptions
## Path conventions
- Meta repo: `vendor/sunfish-a16/`, `docs/`, `specs/009-axp-los23-port/`
- Build host: `/srv/rom/src/lineage/`, `/srv/rom/prebuilts/`,
`/srv/rom/artifacts/009-axp-los23-port/`, `/srv/rom/secrets/`
- LAB: `11231JEC207477` only — never GOLDEN flash/modify
- GOLDEN: `adb` read-only oracle (esp. Magisk preinstall layout)
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Host dirs, runbook stubs, capability inventory scaffold
- [x] T001 Create host dirs `/srv/rom/prebuilts/{magisk,axp-apps}/`,
`/srv/rom/artifacts/009-axp-los23-port/`, `/srv/rom/logs/009-build/`
on `rom-dev01` (android-build writable)
- [x] T002 [P] Add stub `docs/009-magisk-preinstall-runbook.md` (GOLDEN
measure checklist, activation steps, DenyList banking order, LAB serial)
- [x] T003 [P] Add stub `docs/009-version-pins.md` (VersionPinRecord table:
component / version_name / version_code / upstream_ref / policy /
verified_on)
- [x] T004 [P] Add stub `docs/009-capability-matrix.md` mapping
`docs/002-golden-inventory.md` + `docs/axp-pro-parity.md` rows →
`required` / `exception` / `impossible` per `data-model.md`
AxpCapabilitySet
- [x] T005 [P] Create `vendor/sunfish-a16/patches/009-axp-los23-port/README.md`
(Magisk-resurrection apply order; reuse `005`/`008`; no KernelSU ship;
attribute upstreams)
**Checkpoint**: Dirs + stubs exist; no bacon yet
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Freshness policy, GOLDEN Magisk measurement, product wiring
baseline — MUST complete before story packages land on LAB acceptance builds
**⚠️ CRITICAL**: Blocks user-story packaging/flash acceptance
- [x] T006 Refresh microG + GSF to newest stable upstream (fetch tags /
releases; no stale host cache) into `/srv/rom/prebuilts/microg/` +
`VERSIONS.txt`; record pins in `docs/009-version-pins.md`
- [x] T007 [P] Confirm project patched Phonesky still present under
`/srv/rom/prebuilts/phonesky/` + XMLs; record artifact id in
`docs/009-version-pins.md` (`policy=project_phonesky`); halt note if
GOLDEN-class gap suspected
- [x] T008 [P] Fetch newest Magisk stable that targets this stack into
`/srv/rom/prebuilts/magisk/` + `VERSIONS.txt`; record pin in
`docs/009-version-pins.md`
- [x] T009 Measure GOLDEN Magisk/Magia preinstall layout via `adb` only
(paths, packages, first-boot/setup UX) and write results into
`docs/009-magisk-preinstall-runbook.md` (FR-010 / contracts/
magisk-preinstall.md)
- [x] T010 Fill `docs/009-capability-matrix.md` for all GOLDEN/AXP categories
(google-stack, spoofing, call-rec, Magisk, bundled apps, DNS/hosts,
userspace hardening/signing/AVB, **kernel hardening**, WebView=exception,
banking=deferred, localization=deferred FR-018); zero silent omissions
- [x] T011 Wire Magisk-resurrection product includes in
`vendor/sunfish-a16/` (extend `gms.mk` / add `magisk.mk` or equivalent;
keep Phonesky priv-app path; **no FakeStore**; **no KernelSU** product
packages) and document include hook in
`vendor/sunfish-a16/patches/009-axp-los23-port/README.md`
- [x] T012 Confirm `003` vanilla (or documented equivalent) known-good
baseline is available for LAB clean-flash acceptance; note rev in
`docs/009-version-pins.md` VanillaBaseline row
- [x] T013 [P] Confirm optional `007` kernel artifacts remain on host under
`/srv/rom/artifacts/007-*/` for FR-016 recycle only; list paths in
`docs/009-magisk-preinstall-runbook.md` as non-ship
**Checkpoint**: Prebuilts fresh, Magisk UX measured, product wire documented —
story work can begin
---
## Phase 3: User Story 1 — AXP resurrection daily-driver on LAB (Priority: P1) 🎯 MVP framing
**Goal**: One flashable `009` composition path and Gate A smoke; full
daily-driver Independent Test completes after US2+US4+US5 (banking deferred)
**Independent Test**: Flash one `009` build to LAB; walk GOLDEN checklist
**minus banking** from `docs/009-capability-matrix.md`
### Implementation for User Story 1
- [x] T014 [US1] Define staged known-good image policy in
`docs/009-magisk-preinstall-runbook.md` (retain prior LAB image hashes
under `/srv/rom/artifacts/009-axp-los23-port/<build-id>/` per FR-013)
- [ ] T015 [US1] Add LAB flash/acceptance checklist linking Gates A–G to
`specs/009-axp-los23-port/quickstart.md` and
`contracts/acceptance-gates.md` (artifact subdirs `gate-a/`…`gate-g/`)
- [ ] T016 [US1] Produce first Magisk-track bacon on `rom-dev01` (detached
`/srv/rom/artifacts/bin/rom-detach` / tmux) with current wired packages;
logs under `/srv/rom/logs/009-build/`; images + SHA-256 under
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/`
- [ ] T017 [US1] Clean-flash LAB `11231JEC207477` only; complete Gate A
(boot/home/call/Wi‑Fi/unlock); save `getprop` + notes under
`…/<build-id>/gate-a/`
- [ ] T018 [US1] After US2+US4+US5 land, re-flash accepted candidate and mark
SC-001…SC-003 + SC-005…SC-010 / Gates A–D + F–G in
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/acceptance.md`
against `docs/009-capability-matrix.md` (skip Gate E / SC-004)
- [ ] T019 [US1] Verify Git/meta-repo contains no proprietary Phonesky APK
bytes or secrets; document check in
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/acceptance.md`
**Checkpoint**: Gate A proven; full US1 Independent Test after US2+US4+US5
---
## Phase 4: User Story 2 — Google stack: newest microG + project Phonesky (Priority: P1)
**Goal**: Newest microG/GSF + project Phonesky + selectable restricted spoofing
**Independent Test**: microG Self-Check; Phonesky open/install; spoofing
OFF/ON per `contracts/toggles-surface.md`
### Implementation for User Story 2
- [ ] T020 [P] [US2] Ensure `vendor/sunfish-a16/gms.mk` +
`vendor/sunfish-a16/Android.mk` package refreshed microG/GSF + Phonesky
without FakeStore; privilege XMLs remain under
`vendor/sunfish-a16/permissions/` and
`vendor/sunfish-a16/default-permissions/`
- [ ] T021 [P] [US2] Re-apply / verify selectable signature spoofing via
`vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py` on LOS
23.2 tree; document A16 deltas in `docs/005-spoofing-runbook.md` (or
`docs/009-magisk-preinstall-runbook.md` cross-link)
- [ ] T022 [US2] Build+flash LAB image including Google stack; run Gate B
(Self-Check, Phonesky open, ordinary app install/update); evidence under
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/gate-b/`
- [ ] T023 [US2] Verify Secure key `allow_signature_fake` OFF/ON behaviour per
`contracts/toggles-surface.md`; record in
`…/<build-id>/gate-c/spoofing.md`
- [x] T024 [US2] Finalize microG/GSF/Phonesky rows in `docs/009-version-pins.md`;
if Phonesky lacks GOLDEN-class daily fix → FAIL closed note in
`…/<build-id>/gate-b/phonesky-halt.md` (do not accept)
**Checkpoint**: Gate B + spoofing half of Gate C pass (or explicit Phonesky halt)
---
## Phase 5: User Story 3 — Banking (Priority: P3 — deferred)
**Goal**: Island + Magisk DenyList banking — **owner future; not MVP**
**Independent Test**: *(when resumed)* Swissquote in Island; Yuh honest; no root
### Deferred tasks (do not block acceptance)
- [ ] T025 [P] [US3] *(Deferred)* Stage Island under
`/srv/rom/prebuilts/axp-apps/` and wire into `vendor/sunfish-a16/` if
desired as optional package; pin in `docs/009-version-pins.md`
- [ ] T026 [US3] *(Deferred)* Document GOLDEN banking order in
`docs/009-magisk-preinstall-runbook.md`
- [ ] T027 [US3] *(Deferred)* Swissquote Gate E under
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/gate-e/swissquote/`
- [ ] T028 [US3] *(Deferred)* Yuh classify in `…/gate-e/yuh.md`
- [ ] T029 [US3] *(Deferred)* Banks lack root note in `…/gate-e/root-check.md`
**Checkpoint**: Skipped for MVP — resume only when owner asks
---
## Phase 6: User Story 4 — Magisk like GOLDEN + selectable call recording (Priority: P1)
**Goal**: Magisk AXP-like preinstall + DenyList; selectable call-rec unlock
**Independent Test**: Preinstall state vs GOLDEN notes; activation; DenyList
test app; call-rec OFF/ON
### Implementation for User Story 4
- [ ] T030 [US4] Implement Magisk AXP-like preinstall packaging from
`/srv/rom/prebuilts/magisk/` into `vendor/sunfish-a16/magisk.mk` (or
equivalent) matching `docs/009-magisk-preinstall-runbook.md` measured
layout (`contracts/magisk-preinstall.md`)
- [ ] T031 [P] [US4] Re-apply / verify selectable call-rec unlock via
`vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py`
(`extendrom_call_recording`); keep default OFF; optional FLAG_SECURE
not required for AXP acceptance — document in
`docs/008-call-rec-flag-secure-runbook.md`
- [ ] T032 [US4] Build+flash LAB; document pre-activation Magisk state vs
GOLDEN under `…/<build-id>/magisk/pre-activation.md` (Gate D)
- [ ] T033 [US4] Complete Magisk activation on LAB per runbook; configure
DenyList-class hide for a **test** package (not bank gate); save version +
screenshots under `…/<build-id>/magisk/`
- [ ] T034 [US4] Verify `extendrom_call_recording` OFF = stock restrictions,
ON = Dialer can record; evidence under
`…/<build-id>/gate-c/call-rec.md`
- [ ] T035 [US4] Record Magisk version pin (newest workable stable) in
`docs/009-version-pins.md`
**Checkpoint**: Gates C (call-rec) + D pass
---
## Phase 7: User Story 5 — Usability, options, hardening + kernel (Priority: P1)
**Goal**: Bundled AXP surface; LOS23 WebView; userspace hardening/signing;
**updated hardened sunfish kernel** (FR-017). Localization compare = later.
**Independent Test**: Packages vs matrix; WebView LOS; Gate G userspace +
kernel attribution (SC-010)
### Implementation for User Story 5
- [ ] T036 [P] [US5] Enumerate bundled usability packages from
`docs/009-capability-matrix.md` (Aurora, F-Droid/repos as on GOLDEN,
launcher/gallery-class, DNS/hosts helpers if required); stage newest
workable APKs under `/srv/rom/prebuilts/axp-apps/`
- [ ] T037 [US5] Wire bundled apps into `vendor/sunfish-a16/` product
makefiles; pin all versions in `docs/009-version-pins.md`
- [ ] T038 [P] [US5] Verify WebView policy: no Aosmium; default LOS23
provider; chooser unlocked — document in
`…/<build-id>/gate-f/webview.md` per `contracts/webview-surface.md`
- [ ] T039 [US5] Build+flash LAB; confirm bundled categories present or
marked impossible/deferred in `docs/009-capability-matrix.md`; Gate F
evidence under `…/<build-id>/gate-f/`
- [x] T040 [US5] Define staged userspace hardening/signing/AVB checklist in
`docs/009-hardening-signing-checklist.md` (GOLDEN vs LAB measured
2026-09-29; release keys yes / bootloader lock wont; stages US-S0…S6)
- [ ] T041 [US5] Implement first attributable userspace hardening/signing
stage under `vendor/sunfish-a16/patches/009-axp-los23-port/` (US-S1
keygen + US-S2 wire); keep known-good prior image
(`20260929T123401Z-axp-stage1-combo4`)
- [ ] T042 [US5] Inventory LOS23 sunfish kernel tree on `rom-dev01`
(`/srv/rom/src/lineage/kernel/google/sunfish` or manifest path); record
baseline rev in `docs/009-version-pins.md`
- [ ] T043 [US5] Build attributable kernel hardening patch list (AXP /
Graphene / Divest → 4.14-safe) in
`docs/009-kernel-hardening-map.md` (applied vs skip+why)
- [ ] T044 [US5] Apply first kernel hardening stage on LOS23 sunfish kernel;
rebuild boot; flash LAB; evidence under `…/<build-id>/gate-g/kernel/`
- [ ] T045 [US5] Run Gate G userspace+kernel on accepted candidate; gaps in
`…/<build-id>/gate-g/gaps.md`; **do not** block on FR-018 localization
- [x] T046 [P] [US5] *(Look later — FR-018)* Stub only:
`docs/009-localization-axp-vs-los.md` outlining what to compare later
(AXP location lockdown vs stock LOS); no policy decision in MVP
- [ ] T046a [P] [US5] *(FR-019 — before final ship)* Write
`docs/009-sideload-system-updates.md`: how to refresh microG, Magisk,
Phonesky, and other system apps every ~2–3 months without full reflash
(signing identity / PRESIGNED / Magisk manager+boot path); verify
packaging does not break update eligibility
**Checkpoint**: Gates F + G (userspace + kernel) for claimed stage; FR-018 stub
only; FR-019 runbook before final ship
---
## Phase 8: User Story 6 — Parallel tracks stay honest (Priority: P2)
**Goal**: Docs/branches make Magisk `009` active vs KernelSU standby clear
**Independent Test**: `specs/004`–`008`/`007` still present; `009` does not
retire standby track
### Implementation for User Story 6
- [ ] T047 [P] [US6] Update `docs/README.md` (and `AGENTS.md` if needed) to
state active Magisk-resurrection track = `009` / this repo; standby
KernelSU = sibling `sunfish-a16-rom` + specs `004`–`008`/`007`
- [ ] T048 [P] [US6] Add short track note to
`specs/009-axp-los23-port/spec.md` only if needed (do **not** rewrite
standby feature specs)
- [ ] T049 [US6] Remap relevant `docs/axp-pro-parity.md` rows to `009`
- [ ] T050 [US6] Confirm `specs/001`–`008`/`007` directories still exist; record in
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/acceptance.md`
- [ ] T051 [P] [US6] Document optional FR-016 KSU recycle (non-ship) in
`docs/009-magisk-preinstall-runbook.md` + `docs/007-kernelsu-runbook.md`
cross-link
**Checkpoint**: Parallel-track honesty readable without rewriting history
---
## Phase 9: Polish & Cross-Cutting Concerns
**Purpose**: Freshness audit, quickstart validation, handoff
- [ ] T052 [P] Audit all `docs/009-version-pins.md` rows against freshness
policy; fix stale host copies if found
- [ ] T053 [P] Run `specs/009-axp-los23-port/quickstart.md` on LAB
(**skip Gate E**); fix doc gaps
- [ ] T054 Collect final artifacts under
`/srv/rom/artifacts/009-axp-los23-port/<build-id>/`
- [ ] T055 [P] Add/update `docs/009-gate-handoff.md` (next: banking later;
localization compare FR-018 later; sideload refresh cadence FR-019)
- [ ] T056 Mark `specs/009-axp-los23-port/spec.md` Status after Gates A–D +
F–G
---
## Dependencies & Execution Order
### MVP order (owner 2026-09-17)
1. Setup + Foundational
2. US2 Google stack
3. US4 Magisk + call-rec
4. US5 usability + **hardening + kernel**
5. US1 full acceptance (T018) **without banking**
6. US6 docs
7. *(Later)* US3 banking + FR-018 localization compare
### Suggested MVP scope
**T001–T024 + T030–T046** (skip T025–T029)
## Notes
- Banking Gate E / T025–T029 = deferred (owner future)
- Kernel hardening = in-scope (FR-017); localization policy = later (FR-018)
- LAB only; never flash GOLDEN
- Long builds detached on `rom-dev01`
- Owner handles git commit / push / MR
+1
View File
@@ -0,0 +1 @@
certs/
+48
View File
@@ -13,6 +13,8 @@ LOCAL_OVERRIDES_PACKAGES := com.google.android.gms
LOCAL_SRC_FILES := prebuilt/GmsCore.apk LOCAL_SRC_FILES := prebuilt/GmsCore.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/GmsCore.apk LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/GmsCore.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
# microG v0.3.16+ declares required uses-library com.android.location.provider
LOCAL_USES_LIBRARIES := com.android.location.provider
LOCAL_OPTIONAL_USES_LIBRARIES := \ LOCAL_OPTIONAL_USES_LIBRARIES := \
org.apache.http.legacy \ org.apache.http.legacy \
androidx.window.extensions \ androidx.window.extensions \
@@ -47,3 +49,49 @@ LOCAL_OPTIONAL_USES_LIBRARIES := \
androidx.window.extensions \ androidx.window.extensions \
androidx.window.sidecar androidx.window.sidecar
include $(BUILD_PREBUILT) include $(BUILD_PREBUILT)
# 009 bundled apps (PRESIGNED; host prebuilts via symlink) → product
# Optional window libs: same manifest tags as Phonesky/GmsCore (A16 enforce check).
include $(CLEAR_VARS)
LOCAL_MODULE := AuroraStore
LOCAL_MODULE_CLASS := APPS
LOCAL_PRODUCT_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := prebuilt/AuroraStore.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/AuroraStore.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_OPTIONAL_USES_LIBRARIES := \
androidx.window.extensions \
androidx.window.sidecar
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := Droidify
LOCAL_MODULE_CLASS := APPS
LOCAL_PRODUCT_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := prebuilt/Droidify.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/Droidify.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_OPTIONAL_USES_LIBRARIES := \
androidx.window.extensions \
androidx.window.sidecar
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := FossifyGallery
LOCAL_MODULE_CLASS := APPS
LOCAL_PRODUCT_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := Gallery Gallery2 Glimpse
LOCAL_SRC_FILES := prebuilt/FossifyGallery.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/FossifyGallery.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
# US-S4-2 — AXP PRO has no Jelly (system_ext noDefaultBrowserWebView).
include $(CLEAR_VARS)
LOCAL_MODULE := NoLineageJelly
LOCAL_MODULE_TAGS := optional
LOCAL_OVERRIDES_PACKAGES := Jelly
include $(BUILD_PHONY_PACKAGE)
+19
View File
@@ -7,3 +7,22 @@
# official microG (which breaks F-Droid same-signer updates). # official microG (which breaks F-Droid same-signer updates).
BOARD_SUPER_PARTITION_ERROR_LIMIT := 9441378304 BOARD_SUPER_PARTITION_ERROR_LIMIT := 9441378304
# Product LINEAGE_VERSION id: set in vendor/extra/product.mk (via bacon
# symlink to extra-product.mk) — BoardConfig is too late for version.mk.
# Keep a duplicate here only as documentation / get_build_var fallback.
TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro
# US-S3 — project AVB *signing* key (Gate G infrastructure).
# Included after device/google/sunfish BoardConfig-common.mk → override here.
# Keep WITH_AVB unset → BoardConfigLineage adds vbmeta --flags 3
# (HASHTREE_DISABLED|VERIFICATION_DISABLED). Magisk-friendly unlocked LAB;
# NOT runtime AVB enforcement / "hardened verified boot".
# Bootloader relock = wont. OEM unlocking: leave enabled (escape hatch only).
_AXP_AVB_KEY := vendor/sunfish-a16/certs/avb.pem
ifneq ($(wildcard $(_AXP_AVB_KEY)),)
BOARD_AVB_ALGORITHM := SHA256_RSA4096
BOARD_AVB_KEY_PATH := $(_AXP_AVB_KEY)
BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA4096
BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := $(_AXP_AVB_KEY)
endif
+8
View File
@@ -0,0 +1,8 @@
# Bundled user apps (009) — Aurora Store, Droidify, Fossify Gallery.
# APKs live on host at /srv/rom/prebuilts/axp-apps/ (never Git).
# Symlink into vendor/sunfish-a16/prebuilt/ before bacon.
PRODUCT_PACKAGES += \
AuroraStore \
Droidify \
FossifyGallery
+13
View File
@@ -0,0 +1,13 @@
# AXP Resurection Pro product / OTA identity (009).
#
# "Pro" marks the full Magisk-resurrection track (not a future slim/minimal
# variant). GOLDEN used releasetype PRO + ro.lineage.version=…-sunfish-pro.
#
# TARGET_UNOFFICIAL_BUILD_ID is set in vendor/extra/product.mk (early Lineage
# hook, before version.mk). BoardConfig + this file are not early enough alone.
# Ship scripts also publish AXP-Resurection-Pro-*.zip from lineage-*.zip.
# Identity props (not axp.os endpoints). Empty on GOLDEN for ro.axp.version —
# local track marker only.
PRODUCT_PRODUCT_PROPERTIES += \
ro.axp.resurection.track=pro
+5
View File
@@ -0,0 +1,5 @@
# Early Lineage hook (vendor/lineage/config/common.mk inherits this *before*
# version.mk). Required so LINEAGE_VERSION picks up the unofficial id.
# BoardConfig.mk is too late for TARGET_UNOFFICIAL_BUILD_ID.
TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro
+29
View File
@@ -0,0 +1,29 @@
# US-S4 — userspace hardening (AXP Resurection Pro).
# Match AXP.OS PRO sunfish product surface (see hardening/README.md +
# docs/009-axp-pro-product-apps-20260324.md).
#
# Real strips:
# US-S4-1 apply_009_drop_openssh_packages.py (common.mk)
# US-S4-2 apply_009_drop_jelly_gallery2.py (Jelly + Gallery2)
# PRODUCT_PACKAGES filter-out via inherit alone has failed before — keep as
# belt-and-suspenders only.
PRODUCT_PACKAGES := $(filter-out \
scp \
sftp \
ssh \
sshd \
sshd_config \
ssh-keygen \
start-ssh \
rsync \
Jelly \
Gallery2 \
Glimpse \
,$(PRODUCT_PACKAGES))
PRODUCT_PACKAGES += NoLineageJelly
PRODUCT_PACKAGES_DEBUG := $(filter-out procmem,$(PRODUCT_PACKAGES_DEBUG))
# Keep init.openssh.rc — AXP PRO still ships it (mkdir /data/ssh* only).
+15
View File
@@ -0,0 +1,15 @@
# Magisk preroot (009) — clean Magisk, stock UI, no modules.
# Wiring: vendor/sunfish-a16/magisk/{prepare_magisk.sh,patch.sh,apply_releasetools_hook.py}
# Runtime: SUNFISH_A16_MAGISK_PREROOT=1 + prepare_magisk.sh before packaging.
# Magisk is NOT a PRODUCT_PACKAGE (EXTENDROM-style preroot).
# No packages — boot is patched at releasetools time.
# US-S2 release keys (PRODUCT_DEFAULT_DEV_CERTIFICATE) — see signing.mk
$(call inherit-product-if-exists, vendor/sunfish-a16/signing.mk)
# OTA / version branding (TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro)
$(call inherit-product-if-exists, vendor/sunfish-a16/branding.mk)
# US-S4-1 userspace hardening (drop product OpenSSH, etc.)
$(call inherit-product-if-exists, vendor/sunfish-a16/hardening.mk)
+26
View File
@@ -0,0 +1,26 @@
# Magisk preroot (sunfish-a16 / 009)
**Policy:** clone AXP / EXTENDROM `EXTENDROM_PREROOT_BOOT` — do not invent.
Diverge only if LOS23 or Magisk version breaks that path.
**Clean Magisk**, stock UI. No GOLDEN modules. No KernelSU in ship kernel.
| File | Role |
|------|------|
| `prepare_magisk.sh` | Unzip Magisk APK → `$OUT_DIR/.magisk` (same as extendrom `er.sh`) |
| `patch.sh` | From sfX extendrom — patches boot.img via Magisk `boot_patch.sh` |
| `apply_releasetools_hook.py` | Inject into releasetools `common.py` (A16) |
AXP virgin UX: Magisk not a `PRODUCT_PACKAGE`; root inactive until user
Additional Setup. See `docs/009-magisk-preinstall-runbook.md`.
```bash
export SUNFISH_A16_MAGISK_PREROOT=1 # optional; READY file is the real gate
MAGISK_STAGE_DIR=$ANDROID_BUILD_TOP/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
```
Hook **v2** runs when `out/.magisk/READY` exists (preferred) and/or
`SUNFISH_A16_MAGISK_PREROOT=1`. Env alone is unreliable under soong/ninja.
Without READY / env the hook no-ops.
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""Idempotent Magisk preroot hook into releasetools common.py (LOS 23.2 / A16).
Gate (either is enough):
- ``out/.magisk/READY`` present (preferred — survives ninja/soong env scrubbing)
- ``SUNFISH_A16_MAGISK_PREROOT=1``
Matches sfX extendrom inject (always-on once staged), adapted for A16 + our
vendor path. ``RunAndCheckOutput`` env is merged with ``os.environ`` so PATH /
ANDROID_BUILD_TOP survive (extendrom only passed ``system_root_image``; that
works via shebang but is fragile under sandboxes).
"""
from __future__ import annotations
import argparse
import os
import pathlib
import re
import sys
MARKER = "SUNFISH_A16: Magisk preroot hook"
MARKER_VER = "SUNFISH_A16: Magisk preroot hook v2"
BUILD_HOOK = '''
# SUNFISH_A16: Magisk preroot hook v2 (before AVB)
# Gate: staged out/.magisk/READY and/or SUNFISH_A16_MAGISK_PREROOT=1
_sf_top = os.getenv("ANDROID_BUILD_TOP") or os.environ.get("PWD") or os.getcwd()
_sf_ready = os.path.isfile(os.path.join(_sf_top, "out", ".magisk", "READY"))
_sf_env = os.environ.get("SUNFISH_A16_MAGISK_PREROOT") == "1"
if _sf_ready or _sf_env:
if ((partition_name == "boot" and info_dict.get("init_boot") != "true")
or partition_name == "init_boot"):
mpatcher = [
os.path.join(_sf_top, "vendor/sunfish-a16/magisk/patch.sh"),
img.name,
_sf_top,
]
logger.info("SUNFISH_A16 Magisk patching (build): %s", img.name)
_sf_penv = os.environ.copy()
_sf_penv["system_root_image"] = info_dict.get("system_root_image", "false")
_sf_penv.setdefault("ANDROID_BUILD_TOP", _sf_top)
RunAndCheckOutput(mpatcher, verbose=True, env=_sf_penv)
'''
PREBUILT_HOOK = '''
# SUNFISH_A16: Magisk preroot hook v2 (prebuilt)
_sf_top = os.getenv("ANDROID_BUILD_TOP") or os.environ.get("PWD") or os.getcwd()
_sf_ready = os.path.isfile(os.path.join(_sf_top, "out", ".magisk", "READY"))
_sf_env = os.environ.get("SUNFISH_A16_MAGISK_PREROOT") == "1"
if _sf_ready or _sf_env:
if ((prebuilt_name == "boot.img" or prebuilt_name == "init_boot.img")
and os.path.exists(prebuilt_path)):
if info_dict is None:
info_dict = OPTIONS.info_dict
bimg = os.path.join(_sf_top, prebuilt_path)
logger.info("SUNFISH_A16 Magisk patching (prebuilt): %s", bimg)
mpatcher = [
os.path.join(_sf_top, "vendor/sunfish-a16/magisk/patch.sh"),
bimg,
_sf_top,
]
_sf_penv = os.environ.copy()
_sf_penv["system_root_image"] = info_dict.get("system_root_image", "false")
_sf_penv.setdefault("ANDROID_BUILD_TOP", _sf_top)
RunAndCheckOutput(mpatcher, verbose=True, env=_sf_penv)
'''
def _strip_old_hooks(text: str) -> str:
"""Remove prior SUNFISH Magisk hook blocks (v1 gated or v2)."""
# Build hook: from our comment through just before AVB block
text = re.sub(
r"\n # SUNFISH_A16: Magisk preroot hook(?: v2)? \(before AVB\).*?"
r"(?=\n # AVB: if enabled)",
"\n",
text,
count=1,
flags=re.S,
)
# Prebuilt hook: after BOOTABLE_IMAGES assign, before "if os.path.exists(prebuilt_path)"
text = re.sub(
r"( prebuilt_path = os\.path\.join\(unpack_dir, \"BOOTABLE_IMAGES\", prebuilt_name\)\n)"
r"\n # SUNFISH_A16: Magisk preroot hook(?: v2)? \(prebuilt\).*?"
r"(?=\n if os\.path\.exists\(prebuilt_path\):)",
r"\1",
text,
count=1,
flags=re.S,
)
return text
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--top", default=os.environ.get("ANDROID_BUILD_TOP", ""))
ap.add_argument("--dry-run", action="store_true")
ap.add_argument(
"--force",
action="store_true",
help="Replace an existing SUNFISH Magisk hook with v2",
)
args = ap.parse_args()
top = pathlib.Path(args.top or ".")
target = top / "build/make/tools/releasetools/common.py"
if not target.is_file():
print(f"ERROR: missing {target}", file=sys.stderr)
return 2
text = target.read_text()
if MARKER_VER in text and not args.force:
print(f"already applied (v2): {target}")
return 0
if MARKER in text or MARKER_VER in text:
if not args.force and MARKER_VER not in text:
# old v1 present — upgrade
pass
text = _strip_old_hooks(text)
needle_avb = (
' # AVB: if enabled, calculate and add hash to boot.img or recovery.img.\n'
' if info_dict.get("avb_enable") == "true":\n'
' avbtool = info_dict["avb_avbtool"]\n'
' if partition_name == "recovery":'
)
idx = text.find(needle_avb)
if idx < 0:
print("ERROR: AVB anchor not found", file=sys.stderr)
return 3
text = text[:idx] + BUILD_HOOK + "\n" + text[idx:]
assign = ' prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)\n'
idx2 = text.find(assign)
if idx2 < 0:
print("ERROR: prebuilt assign not found", file=sys.stderr)
return 4
insert_at = idx2 + len(assign)
text = text[:insert_at] + PREBUILT_HOOK + "\n" + text[insert_at:]
if args.dry_run:
print("dry-run OK (v2)")
return 0
target.write_text(text)
print(f"applied v2 -> {target}")
return 0
if __name__ == "__main__":
sys.exit(main())
+122
View File
@@ -0,0 +1,122 @@
#!/bin/bash
#########################################################################################################
#
# simple patch wrapper for patching a boot.img during the build process
#
#########################################################################################################
#
# Copyright (C) 2023-2024 steadfasterX <steadfasterX -AT- binbash #DOT# rocks>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#########################################################################################################
#
# usage:
# ./patch.sh <full-path-of-boot.img>
#
#########################################################################################################
# DO NOT set -e !
# parse calling params
export BOOTIMG="$1"
export TOP="$2"
[ -z "$TOP" -o ! -d "$TOP" ] && TOP=$PWD
# colored (optional) output
PRINT(){
MSG="$2"
case $1 in
[1-9]*|E|ERROR)
echo -e "\e[0;31m$0 - ERROR: ${MSG}\e[0m" ;;
I|INFO)
echo -e "\e[1;34m$0 - INFO: ${MSG}\e[0m" ;;
0|OK|SUCCESS)
echo -e "\e[0;32m$0 - OK: ${MSG}\e[0m" ;;
*)
echo -e "$0 - ${MSG}" ;;
esac
}
PRINT I "Starting: $0 with: >$BOOTIMG< (TOP=$TOP)"
# LEGACYSAR can't be detected by the magisk patcher so must be set by us where needed (e.g. fajita).
# LEGACYSAR ensures kernel gets patched if required.
# https://github.com/topjohnwu/Magisk/blob/9aa466c7730ef4ef73195b476f9804ebf86932d2/scripts/boot_patch.sh#L220-L243
export LEGACYSAR=${system_root_image}
PRINT I "system_root_image=$LEGACYSAR"
# PREINITDEVICE can't be detected during build so must be set manually
# otherwise "Requires Additional Setup" on first start will not be able to patch w/o direct flash
# which is a problem on locked devices (obviously)
# https://github.com/AXP-OS/build/wiki/Frequently-Asked-Questions#magisk-requires-additional-setup-on-start
export PREINITDEVICE=userdata
# tell magisk that we are not in booted android
export BOOTMODE=false
# set output file descriptor to pts0 which *should* be the current shell
export OUTFD=0
# do not remove verity nor encryption
export KEEPVERITY=true
export KEEPFORCEENCRYPT=true
# set magisk environment
export OUT_DIR=$TOP/out
export MAG_DIR="$OUT_DIR/.magisk"
# disable due to not allowed binary or useless within the build process
alias dos2unix=
alias mount=echo
alias umount=echo
# enable a fallback to get properties, according to:
# https://github.com/topjohnwu/Magisk/blob/4eaf701cb79cd9255333abad8482e69987f86280/scripts/util_functions.sh#L59
getprop(){
find $OUT_DIR/target/product -type f -name build.prop -exec grep $1 {} \; | tail -n1
}; export -f getprop
test -e $BOOTIMG
PRINT $? "testing existence of >$BOOTIMG< returned: $?"
# do the magisk magic
/bin/bash $MAG_DIR/boot_patch.sh "$BOOTIMG" \
&& cp -v $MAG_DIR/new-boot.img "$BOOTIMG"
RET=$?
# simple check if magisk can be found in the resulting boot.img
grep -q --text 'magisk' $BOOTIMG
MCHK=$?
# sometimes the above is not valid even though patching was successful
if [ $MCHK -ne 0 ];then
PRINT I "Simple magisk check failed, unpacking for a detailed test..\n"
[ -d "$MAG_DIR/test" ] && rm -rf $MAG_DIR/test
mkdir $MAG_DIR/test
cd $MAG_DIR/test
$MAG_DIR/magiskboot unpack $BOOTIMG && $MAG_DIR/magiskboot cpio ramdisk.cpio test
case $? in
1|4) MCHK=0;;
*) ;;
esac
cd $TOP
rm -rf $MAG_DIR/test
fi
# check & print result
if [ "$MCHK" -eq 0 ];then PRINT OK "MAGISK found in $BOOTIMG!";else PRINT E "Magisk not found in $BOOTIMG!";fi
RET=$((RET + $MCHK))
PRINT $RET "ended with: $RET\n\n"
exit $RET
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# Stage Magisk v22+ APK into $OUT_DIR/.magisk (or MAGISK_STAGE_DIR) for preroot.
# Attribute: sfX-android extendrom er.sh MAGISK block + Magisk upstream.
set -euo pipefail
TOP="${ANDROID_BUILD_TOP:-${1:-}}"
OUT_DIR="${OUT_DIR:-$TOP/out}"
STAGE="${MAGISK_STAGE_DIR:-$OUT_DIR/.magisk}"
APK="${MAGISK_APK:-/srv/rom/prebuilts/magisk/Magisk-v30.7.apk}"
ARCH="${MAGISK_TARGET_ARCH:-arm64}"
if [ -z "$TOP" ] || [ ! -d "$TOP" ]; then
echo "Usage: ANDROID_BUILD_TOP=/path prepare_magisk.sh" >&2
exit 2
fi
test -f "$APK" || { echo "Missing Magisk APK: $APK" >&2; exit 4; }
rm -rf "$STAGE"
mkdir -p "$STAGE"
unzip -q "$APK" -d "$STAGE/src"
# Host tools (x86_64) for patching during build
cp "$STAGE/src/lib/x86_64/libmagiskboot.so" "$STAGE/magiskboot" 2>/dev/null \
|| cp "$STAGE/src/lib/x86/libmagiskboot.so" "$STAGE/magiskboot"
chmod 755 "$STAGE/magiskboot"
if [ "$ARCH" = "arm64" ]; then
if [ -f "$STAGE/src/lib/arm64-v8a/libmagisk64.so" ]; then
cp "$STAGE/src/lib/arm64-v8a/libmagisk64.so" "$STAGE/magisk64"
else
cp "$STAGE/src/lib/arm64-v8a/libmagisk.so" "$STAGE/magisk"
fi
cp "$STAGE/src/lib/arm64-v8a/libmagiskinit.so" "$STAGE/magiskinit"
[ -f "$STAGE/src/lib/arm64-v8a/libinit-ld.so" ] && cp "$STAGE/src/lib/arm64-v8a/libinit-ld.so" "$STAGE/init-ld"
[ -f "$STAGE/src/lib/arm64-v8a/libmagiskpolicy.so" ] && cp "$STAGE/src/lib/arm64-v8a/libmagiskpolicy.so" "$STAGE/magiskpolicy"
else
echo "Unsupported MAGISK_TARGET_ARCH=$ARCH" >&2
exit 4
fi
chmod 755 "$STAGE/src/assets/boot_patch.sh"
rm -rf "$STAGE/src/assets/dexopt" "$STAGE/src/assets/chromeos" 2>/dev/null || true
cp "$STAGE/src/assets/"* "$STAGE/"
cp "$STAGE/src/assets/boot_patch.sh" "$STAGE/root_boot.sh"
chmod 755 "$STAGE/boot_patch.sh" "$STAGE/magiskinit" "$STAGE/magiskboot"
[ -f "$STAGE/magisk64" ] && chmod 755 "$STAGE/magisk64"
[ -f "$STAGE/magisk" ] && chmod 755 "$STAGE/magisk"
[ -f "$STAGE/init-ld" ] && chmod 755 "$STAGE/init-ld"
# Point patch.sh at our vendor copy
MARKER="$STAGE/READY"
echo "apk=$(basename "$APK")" > "$MARKER"
echo "arch=$ARCH" >> "$MARKER"
echo "staged=$(date -u -Iseconds)" >> "$MARKER"
echo "OK Magisk staged at $STAGE"
cat "$MARKER"
@@ -10,9 +10,11 @@ python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_f
- **Call recording**: EXTENDROM `er.sh` F_CALLREC / AXP GOLDEN - **Call recording**: EXTENDROM `er.sh` F_CALLREC / AXP GOLDEN
(`extendrom_call_recording`). steadfasterX EXTENDROM. (`extendrom_call_recording`). steadfasterX EXTENDROM.
- **FLAG_SECURE**: AOSP `Settings.Secure.DISABLE_SECURE_WINDOWS` already - **FLAG_SECURE**: AOSP `Settings.Secure.DISABLE_SECURE_WINDOWS` + DO
wired in `WindowManagerService` on A16; DO preference preference `allow_flag_secure_override`. Stock WMS only applies the
`allow_flag_secure_override` writes that key. setting when `ro.debuggable=1`; we remove that gate via
`apply_wms_disable_secure_debuggable_gate.py` so ship images keep
`ro.debuggable=0` while the toggle still works.
## Touched paths ## Touched paths
@@ -24,9 +26,15 @@ python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_f
| Settings controllers | `…/ER_CallRec*.java`, `…/FlagSecureOverride*.java` | | Settings controllers | `…/ER_CallRec*.java`, `…/FlagSecureOverride*.java` |
| Settings DO XML + strings | `res/xml/development_settings.xml`, `res/values/strings.xml` | | Settings DO XML + strings | `res/xml/development_settings.xml`, `res/values/strings.xml` |
| Dashboard | `DevelopmentSettingsDashboardFragment.java` | | Dashboard | `DevelopmentSettingsDashboardFragment.java` |
| Capture path | stock WMS `disable_secure_windows` (no extra WM patch) | | Capture path | WMS `updateDisableSecureWindows()` — **no** `ro.debuggable` gate |
| WMS apply | `apply_wms_disable_secure_debuggable_gate.py` |
```bash
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
```
Backups: `/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots/` Backups: `/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots/`
Marker: `SUNFISH_A16_008_CALL_REC_FLAG_SECURE` Markers: `SUNFISH_A16_008_CALL_REC_FLAG_SECURE`,
`SUNFISH_A16_008_WMS_DISABLE_SECURE_NO_DEBUGGABLE_GATE`
See `docs/008-call-rec-flag-secure-runbook.md`. See `docs/008-call-rec-flag-secure-runbook.md`.
@@ -5,16 +5,18 @@ Call recording (AXP GOLDEN / EXTENDROM F_CALLREC):
- Settings.Secure.ER_ALLOW_ANY_CALL_REC = \"extendrom_call_recording\" - Settings.Secure.ER_ALLOW_ANY_CALL_REC = \"extendrom_call_recording\"
- DO switch + Dialer CallRecorder.canRecordInCurrentCountry early-return - DO switch + Dialer CallRecorder.canRecordInCurrentCountry early-return
FLAG_SECURE (AOSP A16 already implements WM path): FLAG_SECURE (AOSP A16 WM path + project WMS gate removal):
- DO switch writes Settings.Secure.DISABLE_SECURE_WINDOWS - DO switch writes Settings.Secure.DISABLE_SECURE_WINDOWS
(\"disable_secure_windows\"). WindowManagerService.updateDisableSecureWindows() (\"disable_secure_windows\").
already honours it on debuggable builds (LAB userdebug). - Also run apply_wms_disable_secure_debuggable_gate.py so WMS honours the
setting with ro.debuggable=0 (stock AOSP required debuggable).
Attribution: EXTENDROM (steadfasterX) for call-rec; AOSP DISABLE_SECURE_WINDOWS Attribution: EXTENDROM (steadfasterX) for call-rec; AOSP DISABLE_SECURE_WINDOWS
for capture; sunfish-a16 DO wiring. for capture; sunfish-a16 DO + WMS gate wiring.
Run on rom-dev01: Run on rom-dev01:
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_wms_disable_secure_debuggable_gate.py
""" """
from __future__ import annotations from __future__ import annotations
@@ -125,8 +127,9 @@ public class ER_CallRecInfo extends DeveloperOptionsPreferenceController
FLAG_SECURE_CTRL = """/* FLAG_SECURE_CTRL = """/*
* sunfish-a16-rom ({marker}): Developer Options gate for AOSP * sunfish-a16-rom ({marker}): Developer Options gate for AOSP
* Settings.Secure.DISABLE_SECURE_WINDOWS (WindowManagerService already * Settings.Secure.DISABLE_SECURE_WINDOWS. Pair with
* honours this on debuggable builds). * apply_wms_disable_secure_debuggable_gate.py so WMS honours the setting
* when ro.debuggable=0.
*/ */
package com.android.settings.development; package com.android.settings.development;
@@ -406,8 +409,8 @@ def patch_strings() -> None:
<!-- {MARKER}: FLAG_SECURE override (writes AOSP disable_secure_windows) --> <!-- {MARKER}: FLAG_SECURE override (writes AOSP disable_secure_windows) -->
<string name="flag_secure_override_category">Secure window screenshots</string> <string name="flag_secure_override_category">Secure window screenshots</string>
<string name="allow_flag_secure_override">Allow screenshots of secure windows</string> <string name="allow_flag_secure_override">Allow screenshots of secure windows</string>
<string name="allow_flag_secure_override_summary">When enabled, apps that set FLAG_SECURE can still be captured by the system screenshot / screen record paths (debuggable builds). Weakens app privacy.</string> <string name="allow_flag_secure_override_summary">When enabled, apps that set FLAG_SECURE can still be captured by the system screenshot / screen record paths. Weakens app privacy. Does not change ro.debuggable.</string>
<string name="allow_flag_secure_override_details">Writes Settings.Secure.disable_secure_windows. On userdebug/eng, WindowManager treats FLAG_SECURE windows as capturable. Default OFF restores stock secure-window behaviour.</string> <string name="allow_flag_secure_override_details">Writes Settings.Secure.disable_secure_windows. WindowManager treats FLAG_SECURE windows as capturable while this is ON (project build; ro.debuggable stays 0). Default OFF restores stock secure-window behaviour.</string>
""" """
text = text.replace("</resources>", add + "</resources>", 1) text = text.replace("</resources>", add + "</resources>", 1)
path.write_text(text) path.write_text(text)
@@ -458,6 +461,17 @@ def verify_dialer_config() -> None:
print("Dialer config.xml: call_recording_audio_source=4 (OK)") print("Dialer config.xml: call_recording_audio_source=4 (OK)")
def apply_wms_gate() -> None:
"""Invoke sibling script that drops the ro.debuggable gate in WMS."""
import subprocess
import sys
sibling = Path(__file__).resolve().parent / "apply_wms_disable_secure_debuggable_gate.py"
r = subprocess.run([sys.executable, str(sibling)], check=False)
if r.returncode != 0:
raise SystemExit(f"WMS disable-secure gate apply failed: {r.returncode}")
def main() -> None: def main() -> None:
if not TREE.is_dir(): if not TREE.is_dir():
raise SystemExit(f"LOS tree missing: {TREE}") raise SystemExit(f"LOS tree missing: {TREE}")
@@ -475,6 +489,7 @@ def main() -> None:
patch_development_xml() patch_development_xml()
patch_dashboard() patch_dashboard()
verify_dialer_config() verify_dialer_config()
apply_wms_gate()
print("done:", MARKER) print("done:", MARKER)
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Remove AOSP ro.debuggable gate on Settings.Secure.DISABLE_SECURE_WINDOWS.
LOS 23.2 / A16 WindowManagerService.updateDisableSecureWindows() returns early
unless SystemProperties.getBoolean(\"ro.debuggable\") is true. That blocks the
Developer Options toggle on builds that keep ro.debuggable=0 (including this
project's LAB image labelled userdebug).
This patch removes ONLY that gate. Behaviour:
disable_secure_windows=0 → stock FLAG_SECURE (mDisableSecureWindows=false)
disable_secure_windows=1 → mDisableSecureWindows=true; isSecureLocked()/Task.isSecure()
honour the override (no second debuggable check elsewhere)
Does NOT change ro.debuggable, Build.IS_DEBUGGABLE, ViewServer, or screen-share
protections (DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS).
Audit (LOS23 frameworks/base, 2026-09-18): sole gate is updateDisableSecureWindows();
WindowState.isSecureLocked / Task.isSecure only call getDisableSecureWindows().
Run on rom-dev01:
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/\\
apply_wms_disable_secure_debuggable_gate.py
"""
from __future__ import annotations
import shutil
from pathlib import Path
TREE = Path("/srv/rom/src/lineage")
MARKER = "SUNFISH_A16_008_WMS_DISABLE_SECURE_NO_DEBUGGABLE_GATE"
BAK_ROOT = Path("/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots")
WMS = (
TREE
/ "frameworks/base/services/core/java/com/android/server/wm/"
"WindowManagerService.java"
)
OLD = """ void updateDisableSecureWindows() {
if (!SystemProperties.getBoolean(SYSTEM_DEBUGGABLE, false)) {
return;
}
boolean disableSecureWindows;
"""
NEW = f""" void updateDisableSecureWindows() {{
// {MARKER}: honour Settings.Secure.DISABLE_SECURE_WINDOWS even when
// ro.debuggable=0. Stock AOSP gated this on SYSTEM_DEBUGGABLE; ship
// builds keep debuggable off and use the Secure setting alone.
boolean disableSecureWindows;
"""
def backup(path: Path) -> None:
BAK_ROOT.mkdir(parents=True, exist_ok=True)
dest = BAK_ROOT / path.name
if not dest.exists():
shutil.copy2(path, dest)
print(f"backup -> {dest}")
def main() -> int:
if not WMS.is_file():
print(f"ERROR: missing {WMS}")
return 2
text = WMS.read_text()
if MARKER in text:
print(f"already applied: {WMS}")
return 0
if OLD not in text:
print("ERROR: expected updateDisableSecureWindows debuggable gate not found")
print("(tree already diverged — inspect manually)")
return 3
backup(WMS)
WMS.write_text(text.replace(OLD, NEW, 1))
print(f"applied -> {WMS}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,222 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Attribute: AXP GOLDEN ConnectivityCheckPreferenceController (Settings.apk),
* cloned for LOS 23.2 without axp.os / captiveportal.axpos.org endpoints.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
package com.android.settings.network;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.core.lifecycle.events.OnResume;
import android.os.UserHandle;
import android.os.UserManager;
/** Internet connectivity check provider list (captive portal URLs). */
public class ConnectivityCheckPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, OnResume {
private static final String KEY = "connectivity_check_settings";
private static final int DISABLED = 0;
private static final int AMAZON = 1;
private static final int CLOUDFLARE = 2;
private static final int GOOGLE = 3;
private static final int HUAWEI = 4;
private static final int KUKETZ = 5;
private static final int MICROSOFT = 6;
private static final int OPENSUSE = 7;
private static final int UBUNTU = 8;
private ListPreference mConnectivityPreference;
public ConnectivityCheckPreferenceController(Context context) {
super(context, KEY);
}
@Override
public int getAvailabilityStatus() {
return isDisabledByAdmin() ? DISABLED_FOR_USER : AVAILABLE;
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
ListPreference pref = new ListPreference(screen.getContext());
pref.setKey(KEY);
pref.setOrder(30);
pref.setIcon(R.drawable.ic_settings_language);
pref.setTitle(R.string.connectivity_check_title);
pref.setSummary(R.string.connectivity_check_summary);
pref.setEntries(R.array.connectivity_check_entries);
pref.setEntryValues(R.array.connectivity_check_values);
pref.setOnPreferenceChangeListener(this);
if (mConnectivityPreference == null) {
screen.addPreference(pref);
mConnectivityPreference = pref;
}
super.displayPreference(screen);
updatePreferenceState();
}
private void updatePreferenceState() {
if (mConnectivityPreference == null) {
return;
}
ContentResolver cr = mContext.getContentResolver();
int mode = Settings.Global.getInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, 1);
if (mode == 0) {
mConnectivityPreference.setValueIndex(DISABLED);
return;
}
String http = Settings.Global.getString(cr, Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
int idx = GOOGLE;
if ("http://fireoscaptiveportal.com/generate_204".equals(http)) {
idx = AMAZON;
} else if ("http://cp.cloudflare.com".equals(http)) {
idx = CLOUDFLARE;
} else if ("http://connectivitycheck.gstatic.com/generate_204".equals(http)) {
idx = GOOGLE;
} else if ("http://connectivitycheck.platform.hicloud.com/generate_204".equals(http)) {
idx = HUAWEI;
} else if ("http://captiveportal.kuketz.de".equals(http)) {
idx = KUKETZ;
} else if ("http://edge-http.microsoft.com/captiveportal/generate_204".equals(http)) {
idx = MICROSOFT;
} else if ("http://conncheck.opensuse.org".equals(http)) {
idx = OPENSUSE;
} else if ("http://connectivity-check.ubuntu.com".equals(http)) {
idx = UBUNTU;
}
mConnectivityPreference.setValueIndex(idx);
}
private void setCaptivePortalURLs(ContentResolver cr, int value) {
final String httpKey = Settings.Global.CAPTIVE_PORTAL_HTTP_URL;
final String httpsKey = Settings.Global.CAPTIVE_PORTAL_HTTPS_URL;
final String fallbackKey = Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL;
final String otherKey = Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
final String modeKey = Settings.Global.CAPTIVE_PORTAL_MODE;
String http;
String https;
String fallback;
String other;
int mode = 1;
switch (value) {
case DISABLED:
// Match GOLDEN: write Google URLs then mode=0
http = "http://connectivitycheck.gstatic.com/generate_204";
https = "https://www.google.com/generate_204";
fallback = "http://www.google.com/gen_204";
other = "http://play.googleapis.com/generate_204";
mode = 0;
break;
case AMAZON:
http = "http://fireoscaptiveportal.com/generate_204";
https = "https://fireoscaptiveportal.com/generate_204";
fallback = http;
other = http;
break;
case CLOUDFLARE:
http = "http://cp.cloudflare.com";
https = "https://cp.cloudflare.com";
fallback = http;
other = http;
break;
case HUAWEI:
http = "http://connectivitycheck.platform.hicloud.com/generate_204";
https = "https://connectivitycheck.platform.hicloud.com/generate_204";
fallback = http;
other = http;
break;
case KUKETZ:
http = "http://captiveportal.kuketz.de";
https = "https://captiveportal.kuketz.de";
fallback = http;
other = http;
break;
case MICROSOFT:
http = "http://edge-http.microsoft.com/captiveportal/generate_204";
https = http;
fallback = http;
other = http;
break;
case OPENSUSE:
http = "http://conncheck.opensuse.org";
https = "https://conncheck.opensuse.org";
fallback = http;
other = http;
break;
case UBUNTU:
http = "http://connectivity-check.ubuntu.com";
https = "https://connectivity-check.ubuntu.com";
fallback = http;
other = http;
break;
case GOOGLE:
default:
http = "http://connectivitycheck.gstatic.com/generate_204";
https = "https://www.google.com/generate_204";
fallback = "http://www.google.com/gen_204";
other = "http://play.googleapis.com/generate_204";
break;
}
Settings.Global.putString(cr, httpKey, http);
Settings.Global.putString(cr, httpsKey, https);
Settings.Global.putString(cr, fallbackKey, fallback);
Settings.Global.putString(cr, otherKey, other);
Settings.Global.putInt(cr, modeKey, mode);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (KEY.equals(preference.getKey())) {
setCaptivePortalURLs(
mContext.getContentResolver(), Integer.parseInt((String) newValue));
return true;
}
return false;
}
@Override
public void onResume() {
updatePreferenceState();
if (mConnectivityPreference != null && mConnectivityPreference.getValue() != null) {
setCaptivePortalURLs(
mContext.getContentResolver(),
Integer.parseInt(mConnectivityPreference.getValue()));
}
}
private EnforcedAdmin getEnforcedAdmin() {
return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
mContext, UserManager.DISALLOW_CONFIG_PRIVATE_DNS, UserHandle.myUserId());
}
private boolean isDisabledByAdmin() {
return getEnforcedAdmin() != null;
}
}
+58
View File
@@ -0,0 +1,58 @@
# 009 AXP LOS23 port — patch / packaging README
**Track**: Magisk AXP resurrection on LOS 23.2 sunfish (`AXP_Resurection`).
**Not ship**: KernelSU / SusFS (`007` standby / optional recycle only).
## Apply order (host tree under `/srv/rom/src/lineage/`)
1. **Google stack** — `vendor/sunfish-a16/gms.mk` + `Android.mk` (microG, GSF,
Phonesky). Host APKs: `/srv/rom/prebuilts/{microg,phonesky}/` (never Git).
2. **Signature spoofing** — `vendor/sunfish-a16/patches/005-sigspoof/`
(`apply_005_sigspoof.py`). Attribute: LOS restricted spoofing + AXP/005.
3. **Call recording unlock** — `vendor/sunfish-a16/patches/008-call-rec-flag-secure/`
(`extendrom_call_recording`). Attribute: EXTENDROM `F_CALLREC`.
4. **Magisk preroot** — EXTENDROM-class boot/`init_boot` patch using Magisk APK
from `/srv/rom/prebuilts/magisk/` (see `docs/009-magisk-preinstall-runbook.md`).
Stub: `vendor/sunfish-a16/magisk.mk`. Attribute: Magisk upstream + sfX
extendrom Magisk patcher. **Host tree currently has no `vendor/extendrom`**
— must vendor minimal patcher or checkout extendrom before first Magisk bacon.
5. **Bundled apps** — `/srv/rom/prebuilts/axp-apps/` + `apps.mk` (Aurora,
Droidify, Fossify Gallery). Symlink APKs into `vendor/sunfish-a16/prebuilt/`.
6. **Private DNS presets** — `apply_009_private_dns_providers.py` (GOLDEN list).
7. **Internet connectivity check** — `apply_009_connectivity_check.py` (GOLDEN
list **minus** AXP.OS / `axpos.org`).
8. **Disable for user apps** — `apply_009_disable_user_apps.py` (SPA gate).
9. **Userspace hardening / signing** — staged: see `signing/README.md`,
`hardening/README.md` (US-S4-1 OpenSSH trim), and
`docs/009-hardening-signing-checklist.md` (US-S1 keys → US-S2 wire →
US-S3 AVB → US-S4+ harden). Bootloader lock = wont.
10. **Kernel hardening** — LOS23 `msm-4.14` + map in
`docs/009-kernel-hardening-map.md` (FR-017). Stage 1:
`vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1.sh`
(AXP Graphene/AH allowlist). Priority: AXP → LOS → Iodé → optional
Graphene; no Divest hunt; no extra location lockdown.
## Product includes
Wire Magisk-resurrection product from `vendor/sunfish-a16/` into
`lineage_sunfish` (same pattern as `004`/`005`). Keep Phonesky priv-app path.
**No FakeStore. No KernelSU product packages.**
## Deferred
- Banking Gate E / Island validation (owner future).
- Localization AXP-vs-LOS compare (`docs/009-localization-axp-vs-los.md` stub).
## Host paths (rom-dev01)
| What | Path |
|------|------|
| Magisk APK | `/srv/rom/prebuilts/magisk/Magisk-v30.7.apk` |
| EXTENDROM Magisk patcher | `/srv/rom/references/extendrom/config/magisk/` |
| Kernel source | `/srv/rom/src/lineage/kernel/google/msm-4.14` |
| Product include (gms) | `device/google/sunfish/lineage_sunfish.mk` → `vendor/sunfish-a16/gms.mk` |
| Vanilla baseline | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish/` |
| Optional KSU boots | `/srv/rom/artifacts/007-kernelsu/` (non-ship) |
**Gap:** `vendor/extendrom` is **not** checked out in the lineage tree yet.
Magisk bacon blocked on T011 (symlink/checkout + PREROOT or slim wire).
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""009: port GOLDEN Internet connectivity check UI (sans axp.os).
Copies ConnectivityCheckPreferenceController, adds string/array resources,
wires into NetworkDashboardFragment. Idempotent.
"""
from __future__ import annotations
import pathlib
import shutil
import sys
MARKER = "sunfish-a16-009-connectivity-check"
ENTRIES = """ <!-- {m}: GOLDEN connectivity providers minus AXP.OS -->
<string-array name="connectivity_check_entries" translatable="false">
<item>Disabled</item>
<item>Amazon Fire OS (US)</item>
<item>Cloudflare (US)</item>
<item>Google (US) [default]</item>
<item>Huawei (CN)</item>
<item>Kuketz (DE)</item>
<item>Microsoft Edge (US)</item>
<item>openSUSE (DE)</item>
<item>Ubuntu (UK)</item>
</string-array>
<string-array name="connectivity_check_values" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</string-array>
""".format(
m=MARKER
)
STRINGS = f""" <!-- {MARKER} -->
<string name="connectivity_check_title" translatable="false">Internet connectivity check</string>
<string name="connectivity_check_summary" translatable="false">HTTP endpoints to use for performing internet connectivity checks.</string>
"""
def ensure_arrays(path: pathlib.Path) -> None:
text = path.read_text(encoding="utf-8")
if "connectivity_check_entries" in text:
return
if "</resources>" not in text:
raise SystemExit(f"FAIL: no </resources> in {path}")
path.write_text(text.replace("</resources>", ENTRIES + "</resources>", 1), encoding="utf-8")
def ensure_strings(path: pathlib.Path) -> None:
text = path.read_text(encoding="utf-8")
if "connectivity_check_title" in text:
return
path.write_text(text.replace("</resources>", STRINGS + "</resources>", 1), encoding="utf-8")
def wire_fragment(path: pathlib.Path) -> None:
text = path.read_text(encoding="utf-8")
if "ConnectivityCheckPreferenceController" in text:
return
if "controllers.add(privateDnsPreferenceController);" not in text:
raise SystemExit("FAIL: expected PrivateDns add site missing")
text = text.replace(
"controllers.add(privateDnsPreferenceController);",
"controllers.add(privateDnsPreferenceController);\n"
f" // {MARKER}\n"
" controllers.add(new ConnectivityCheckPreferenceController(context));",
1,
)
path.write_text(text, encoding="utf-8")
def main() -> int:
root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
here = pathlib.Path(__file__).resolve().parent
src = here / "ConnectivityCheckPreferenceController.java"
dst = root / (
"packages/apps/Settings/src/com/android/settings/network/"
"ConnectivityCheckPreferenceController.java"
)
arrays = root / "packages/apps/Settings/res/values/arrays.xml"
# Prefer cm_strings for non-translated titles; fall back to strings.xml end
cm = root / "packages/apps/Settings/res/values/cm_strings.xml"
frag = root / (
"packages/apps/Settings/src/com/android/settings/network/NetworkDashboardFragment.java"
)
for p in (src, arrays, cm, frag):
if not p.is_file():
print(f"FAIL: missing {p}", file=sys.stderr)
return 1
if dst.is_file() and MARKER.split("-")[0] and "sans axp" in dst.read_text(encoding="utf-8"):
# already copied
pass
else:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
ensure_arrays(arrays)
ensure_strings(cm)
wire_fragment(frag)
print(f"OK: connectivity check wired -> {dst}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""009: allow classic Disable for non-system apps (A16 Archive era).
LOS AppDisableButton returns null for !isSystemApp. Graphene/AXP-class UX
keeps Disable usable for user apps. Attribute: AXP/owner preference over Archive.
Idempotent.
"""
from __future__ import annotations
import pathlib
import sys
MARKER = "sunfish-a16-009-disable-user-apps"
CANDIDATES = [
pathlib.Path("packages/apps/Settings/src/com/android/settings/spa/app/appinfo/AppDisableButton.kt"),
]
def main() -> int:
root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
path = None
for rel in CANDIDATES:
p = root / rel
if p.is_file():
path = p
break
if path is None:
print("FAIL: AppDisableButton.kt not found", file=sys.stderr)
return 1
text = path.read_text(encoding="utf-8")
if MARKER in text:
print(f"OK: already applied ({path})")
return 0
old = " if (!app.isSystemApp) return null\n"
if old not in text:
print("FAIL: expected isSystemApp gate not found", file=sys.stderr)
return 2
new = (
f" // {MARKER}: allow Disable for user apps (not only system)\n"
f" // if (!app.isSystemApp) return null\n"
)
path.write_text(text.replace(old, new, 1), encoding="utf-8")
print(f"OK: patched {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""US-S4-2 — drop Lineage Jelly + Gallery2 to match AXP.OS PRO product surface.
Oracle: AXP.OS-22.2-20260324-PRO-sunfish.zip product/app has neither Jelly nor
Gallery2 (browser stub + FossifyGallery_FD live on system_ext). See
docs/009-axp-pro-product-apps-20260324.md.
FossifyGallery already declares LOCAL_OVERRIDES_PACKAGES := Gallery Gallery2
Glimpse, but Gallery2 still landed in product/app on the 162840Z ship — same
class of failure as OpenSSH filter-out. Patch the Lineage PRODUCT_PACKAGES
add sites and add belt-and-suspenders stubs.
Usage (from ANDROID_BUILD_TOP):
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_jelly_gallery2.py
"""
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
MARKER = "sunfish-a16-009-uss4-2-jelly-gallery2"
# Common Lineage shapes (tolerant).
JELLY_BLOCK = re.compile(
r"# Browser\n"
r"PRODUCT_PACKAGES \+= \\\n"
r" Jelly\n",
re.MULTILINE,
)
# ExactCalculator + Jelly list (common Lineage shape) → keep ExactCalculator only.
JELLY_WITH_CALC = re.compile(
r"(PRODUCT_PACKAGES \+= \\\n"
r" ExactCalculator) \\\n"
r" Jelly\n",
re.MULTILINE,
)
GALLERY2_IN_LIST = re.compile(
r"( DeskClock \\\n)"
r" Gallery2 \\\n",
re.MULTILINE,
)
def patch_text(text: str, path: Path) -> tuple[str, list[str]]:
notes: list[str] = []
bad = f"# Jelly stripped ({MARKER})" in text or f"# Gallery2 stripped ({MARKER})" in text
has_pkg = bool(re.search(r"(?m)^[ \t]*Jelly( \\)?$", text)) or bool(
re.search(r"(?m)^[ \t]*Gallery2( \\)?$", text)
)
if MARKER in text and not bad and not has_pkg:
notes.append("already")
return text, notes
orig = text
text, n = JELLY_BLOCK.subn(
f"# Browser (Jelly) omitted ({MARKER}) — AXP PRO uses noDefaultBrowserWebView\n",
text,
count=1,
)
if n:
notes.append(f"jelly_block={n}")
text, n = JELLY_WITH_CALC.subn(
rf"\1\n",
text,
count=1,
)
if n:
notes.append(f"jelly_calc={n}")
text, n = GALLERY2_IN_LIST.subn(
rf"\1",
text,
count=1,
)
if n:
notes.append(f"gallery2_list={n}")
# Remove leftover broken comment-only continuations from older applicator.
text2 = re.sub(
r"^[ \t]*# (?:Jelly|Gallery2) stripped \(" + re.escape(MARKER) + r"\)\n",
"",
text,
flags=re.MULTILINE,
)
if text2 != text:
notes.append("scrub_bad_comments")
text = text2
if text == orig and not notes:
return orig, notes
if MARKER not in text and text != orig:
text = f"# {MARKER}\n" + text
return text, notes
def main() -> int:
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
candidates = [
top / "vendor/lineage/config/common.mk",
top / "vendor/lineage/config/common_mobile.mk",
top / "vendor/lineage/config/common_full.mk",
top / "vendor/lineage/config/telephony.mk",
top / "build/make/target/product/handheld_product.mk",
top / "build/make/target/product/handheld_system.mk",
top / "build/make/target/product/generic_system.mk",
]
# Also scan device trees that often force Gallery2
for p in (top / "device").rglob("*.mk") if (top / "device").is_dir() else []:
if p.name in ("device.mk", "lineage.mk", "lineage_sunfish.mk", "aosp_sunfish.mk"):
candidates.append(p)
touched = 0
still_present = []
for mk in candidates:
if not mk.is_file():
continue
text = mk.read_text(encoding="utf-8", errors="replace")
if "Jelly" not in text and "Gallery2" not in text:
continue
# Ignore mention-only in comments/docs after a clean strip.
has_pkg = bool(re.search(r"(?m)^[ \t]*Jelly( \\)?$", text)) or bool(
re.search(r"(?m)^[ \t]*Gallery2( \\)?$", text)
)
bad = f"# Jelly stripped ({MARKER})" in text or f"# Gallery2 stripped ({MARKER})" in text
if not has_pkg and not bad:
continue
new, notes = patch_text(text, mk)
if notes == ["already"]:
print(f"OK: already applied ({mk})")
touched += 1
continue
if new == text:
print(f"WARN: {mk} mentions Jelly/Gallery2 but no pattern matched: {notes}")
still_present.append(str(mk))
continue
mk.write_text(new, encoding="utf-8")
print(f"OK: patched {mk} ({', '.join(notes)})")
touched += 1
if still_present:
print(
"FAIL: Jelly/Gallery2 still present, patterns missed: "
+ ", ".join(still_present),
file=sys.stderr,
)
return 1
if touched == 0:
print("OK: Jelly/Gallery2 already absent from PRODUCT_PACKAGES sites")
return 0
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""US-S4-1 — strip OpenSSH/rsync/procmem packages from Lineage common.mk.
AXP.OS PRO sunfish (A15) product/system lack sshd/ssh/rsync/procmem binaries but
still ship init.openssh.rc. PRODUCT_PACKAGES filter-out via inherit-product did
*not* remove them from the 20261002T125259Z ship image — patch the source block
instead (same place Lineage adds them).
Keeps PRODUCT_COPY_FILES for init.openssh.rc (AXP surface).
Usage:
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_openssh_packages.py
"""
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
MARKER = "sunfish-a16-009-uss4-openssh"
OPENSSH_PACKAGES = re.compile(
r"# Openssh\n"
r"PRODUCT_PACKAGES \+= \\\n"
r"(?: \S+ \\\n)*"
r" start-ssh\n",
re.MULTILINE,
)
RSYNC_PACKAGES = re.compile(
r"# rsync\n"
r"PRODUCT_PACKAGES \+= \\\n"
r" rsync\n",
re.MULTILINE,
)
PROCMEM_DEBUG = re.compile(
r"# These packages are excluded from user builds\n"
r"PRODUCT_PACKAGES_DEBUG \+= \\\n"
r" procmem\n"
r"\n"
r"ifneq \(\$\(TARGET_BUILD_VARIANT\),user\)\n"
r"PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST \+= \\\n"
r" system/bin/procmem\n"
r"endif\n",
re.MULTILINE,
)
def main() -> int:
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
mk = top / "vendor/lineage/config/common.mk"
if not mk.is_file():
print(f"FAIL: missing {mk}", file=sys.stderr)
return 1
text = mk.read_text()
if MARKER in text:
print(f"OK: already applied ({mk})")
return 0
orig = text
text, n1 = OPENSSH_PACKAGES.subn(
f"# Openssh packages stripped ({MARKER}) — keep init.openssh.rc below\n",
text,
count=1,
)
text, n2 = RSYNC_PACKAGES.subn(
f"# rsync stripped ({MARKER})\n",
text,
count=1,
)
text, n3 = PROCMEM_DEBUG.subn(
f"# procmem debug package stripped ({MARKER})\n",
text,
count=1,
)
if n1 != 1 or n2 != 1:
print(
f"FAIL: Openssh/rsync block match n1={n1} n2={n2} n3={n3}",
file=sys.stderr,
)
return 1
# procmem block optional if layout drifted
if n3 == 0:
print("WARN: procmem debug block not matched (layout drift)")
if "init.openssh.rc" not in text:
print("FAIL: init.openssh.rc COPY_FILES missing after patch", file=sys.stderr)
return 1
mk.write_text(text)
print(f"OK: patched {mk} (openssh_pkgs={n1} rsync={n2} procmem={n3})")
return 0 if text != orig else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,498 @@
#!/usr/bin/env python3
"""009: expand Private DNS presets to AXP GOLDEN list (no axp.os).
Clones GOLDEN Settings radios/hostnames into LOS PrivateDnsModeDialogPreference
(which already has a Cloudflare stub). Idempotent via marker comments.
"""
from __future__ import annotations
import pathlib
import re
import sys
MARKER = "sunfish-a16-009-private-dns"
# key, UI label, DoT hostname — GOLDEN order (cloudflare hostname = security.*)
PROVIDERS = [
("adguard", "AdGuard (CT)", "dns.adguard.com"),
("appliedprivacy", "AppliedPrivacy (AT)", "dot1.applied-privacy.net"),
("cira", "CIRA (CA)", "protected.canadianshield.cira.ca"),
("controld", "Control D (CA)", "p2.freedns.controld.com"),
("cleanbrowsing", "CleanBrowsing (US)", "security-filter-dns.cleanbrowsing.org"),
("cznic", "CZ.NIC (CZ)", "odvr.nic.cz"),
("cloudflare", "Cloudflare (US)", "security.cloudflare-dns.com"),
("dnszero", "DNS0 (FR)", "zero.dns0.eu"),
("dnssb", "DNS.SB (DE)", "dot.sb"),
("google", "Google (US)", "dns.google"),
("mullvad", "Mullvad (SE)", "base.dns.mullvad.net"),
("quadnine", "Quad9 (CH)", "dns.quad9.net"),
("restena", "Restena (LU)", "kaitain.restena.lu"),
("switch", "SWITCH (CH)", "dns.switch.ch"),
("uncensoreddns", "UncensoredDNS (DK)", "anycast.censurfridns.dk"),
]
def patch_layout(path: pathlib.Path) -> bool:
text = path.read_text(encoding="utf-8")
if MARKER in text:
return False
# Insert provider radios after off, before opportunistic (keep provider+hostname)
includes = []
for key, _, _ in PROVIDERS:
includes.append(
f' <!-- {MARKER} -->\n'
f' <include\n'
f' android:id="@+id/private_dns_mode_{key}"\n'
f' layout="@layout/preference_widget_dialog_radiobutton"/>\n'
)
block = "".join(includes)
# Remove ALL existing cloudflare includes (re-added once via PROVIDERS)
text2 = re.sub(
r'\s*(?:<!--.*?-->\s*)?<include\s*\n\s*android:id="@\+id/private_dns_mode_cloudflare"\s*\n\s*layout="@layout/preference_widget_dialog_radiobutton"/>\s*',
"\n",
text,
flags=re.DOTALL,
)
text2 = re.sub(
r'\s*<include\s+android:id="@\+id/private_dns_mode_cloudflare"[^/]*/>\s*',
"\n",
text2,
)
needle = (
' <include\n'
' android:id="@+id/private_dns_mode_opportunistic"\n'
' layout="@layout/preference_widget_dialog_radiobutton"/>'
)
if needle not in text2:
# alternate formatting
needle = 'android:id="@+id/private_dns_mode_opportunistic"'
idx = text2.find(needle)
if idx < 0:
raise SystemExit(f"FAIL: opportunistic radio not in {path}")
# find start of include element
start = text2.rfind("<include", 0, idx)
text2 = text2[:start] + block + text2[start:]
else:
text2 = text2.replace(needle, block + needle, 1)
path.write_text(text2, encoding="utf-8")
return True
def patch_cm_strings(path: pathlib.Path) -> bool:
text = path.read_text(encoding="utf-8")
if MARKER in text and "private_dns_hostname_quadnine" in text:
return False
# Replace/extend cloudflare hostname to GOLDEN security.cloudflare-dns.com
text = re.sub(
r'<string name="private_dns_hostname_cloudflare"[^>]*>.*?</string>',
'<string name="private_dns_hostname_cloudflare" translatable="false">'
"security.cloudflare-dns.com</string>",
text,
count=1,
)
text = re.sub(
r'<string name="private_dns_mode_cloudflare"[^>]*>.*?</string>',
'<string name="private_dns_mode_cloudflare" translatable="false">'
"Cloudflare (US)</string>",
text,
count=1,
)
lines = [f" <!-- {MARKER}: AXP GOLDEN Private DNS presets -->"]
for key, label, host in PROVIDERS:
if key == "cloudflare":
continue # already present
lines.append(
f' <string name="private_dns_mode_{key}" translatable="false">{label}</string>'
)
lines.append(
f' <string name="private_dns_hostname_{key}" translatable="false">{host}</string>'
)
insert = "\n".join(lines) + "\n"
if "</resources>" not in text:
raise SystemExit(f"FAIL: no </resources> in {path}")
text = text.replace("</resources>", insert + "</resources>", 1)
path.write_text(text, encoding="utf-8")
return True
def write_preference_java(path: pathlib.Path) -> None:
"""Rewrite PrivateDnsModeDialogPreference with data-driven GOLDEN presets."""
# Keep copyright / package; replace body after class declaration via full file.
providers_java = ",\n".join(
f' new Provider("{key}", R.id.private_dns_mode_{key}, '
f"R.string.private_dns_mode_{key}, R.string.private_dns_hostname_{key})"
for key, _, _ in PROVIDERS
)
# Assign custom mode ints starting at 4 (LOS cloudflare used 4)
mode_consts = "\n".join(
f" private static final int PRIVATE_DNS_MODE_{key.upper()} = {4 + i};"
for i, (key, _, _) in enumerate(PROVIDERS)
)
map_puts = "\n".join(
f" PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_{key.upper()}, "
f"R.id.private_dns_mode_{key});"
for key, _, _ in PROVIDERS
)
content = f'''/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivitySettingsManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.CustomDialogPreferenceCompat;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.material.textfield.TextInputLayout;
import com.google.common.net.InternetDomainName;
import java.util.HashMap;
import java.util.Map;
/**
* Dialog to set the Private DNS.
* {MARKER}: AXP GOLDEN provider presets (hostnames from GOLDEN Settings.apk).
*/
public class PrivateDnsModeDialogPreference extends CustomDialogPreferenceCompat implements
RadioGroup.OnCheckedChangeListener, TextWatcher {{
public static final String ANNOTATION_URL = "url";
private static final String TAG = "PrivateDnsModeDialog";
private static final Map<Integer, Integer> PRIVATE_DNS_MAP;
{mode_consts}
private static final Provider[] PROVIDERS = new Provider[] {{
{providers_java}
}};
static {{
PRIVATE_DNS_MAP = new HashMap<>();
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OFF, R.id.private_dns_mode_off);
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPPORTUNISTIC, R.id.private_dns_mode_opportunistic);
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.id.private_dns_mode_provider);
{map_puts}
}}
private static final class Provider {{
final String key;
final int radioId;
final int labelRes;
final int hostnameRes;
Provider(String key, int radioId, int labelRes, int hostnameRes) {{
this.key = key;
this.radioId = radioId;
this.labelRes = labelRes;
this.hostnameRes = hostnameRes;
}}
}}
@VisibleForTesting
TextInputLayout mHostnameLayout;
@VisibleForTesting
EditText mHostnameText;
@VisibleForTesting
RadioGroup mRadioGroup;
@VisibleForTesting
int mMode;
public PrivateDnsModeDialogPreference(Context context) {{
super(context);
}}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs) {{
super(context, attrs);
}}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {{
super(context, attrs, defStyleAttr);
}}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {{
super(context, attrs, defStyleAttr, defStyleRes);
}}
private final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo(
ANNOTATION_URL, (widget) -> {{
final Context context = widget.getContext();
final Intent intent = HelpUtils.getHelpIntent(context,
context.getString(R.string.help_uri_private_dns),
context.getClass().getName());
if (intent != null) {{
try {{
widget.startActivityForResult(intent, 0);
}} catch (ActivityNotFoundException e) {{
Log.w(TAG, "Activity was not found for intent, " + intent.toString());
}}
}}
}});
private static int modeForRadioId(int radioId) {{
if (radioId == R.id.private_dns_mode_off) return PRIVATE_DNS_MODE_OFF;
if (radioId == R.id.private_dns_mode_opportunistic) return PRIVATE_DNS_MODE_OPPORTUNISTIC;
if (radioId == R.id.private_dns_mode_provider) return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
for (int i = 0; i < PROVIDERS.length; i++) {{
if (PROVIDERS[i].radioId == radioId) {{
return 4 + i;
}}
}}
return PRIVATE_DNS_MODE_OPPORTUNISTIC;
}}
private static Provider providerForMode(int mode) {{
int idx = mode - 4;
if (idx >= 0 && idx < PROVIDERS.length) {{
return PROVIDERS[idx];
}}
return null;
}}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {{
super.onBindViewHolder(holder);
if (isDisabledByAdmin()) {{
holder.itemView.setEnabled(true);
}}
setSaveButtonListener();
}}
@Override
protected void onBindDialogView(View view) {{
final Context context = getContext();
mMode = ConnectivitySettingsManager.getPrivateDnsMode(context);
if (mMode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {{
final String host = ConnectivitySettingsManager.getPrivateDnsHostname(context);
for (int i = 0; i < PROVIDERS.length; i++) {{
if (host != null && host.equals(context.getString(PROVIDERS[i].hostnameRes))) {{
mMode = 4 + i;
break;
}}
}}
}}
mRadioGroup = view.findViewById(R.id.private_dns_radio_group);
mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mMode, R.id.private_dns_mode_opportunistic));
mRadioGroup.setOnCheckedChangeListener(this);
final RadioButton offRadioButton = view.findViewById(R.id.private_dns_mode_off);
offRadioButton.setText(com.android.settingslib.R.string.private_dns_mode_off);
for (Provider p : PROVIDERS) {{
final RadioButton rb = view.findViewById(p.radioId);
if (rb != null) {{
rb.setText(p.labelRes);
}}
}}
final RadioButton opportunisticRadioButton =
view.findViewById(R.id.private_dns_mode_opportunistic);
opportunisticRadioButton.setText(
com.android.settingslib.R.string.private_dns_mode_opportunistic);
final RadioButton providerRadioButton = view.findViewById(R.id.private_dns_mode_provider);
providerRadioButton.setText(com.android.settingslib.R.string.private_dns_mode_provider);
mHostnameLayout = view.findViewById(R.id.private_dns_mode_provider_hostname_layout);
mHostnameText = view.findViewById(R.id.private_dns_mode_provider_hostname);
if (mHostnameText != null) {{
mHostnameText.setText(ConnectivitySettingsManager.getPrivateDnsHostname(context));
mHostnameText.addTextChangedListener(this);
}}
final TextView helpTextView = view.findViewById(R.id.private_dns_help_info);
helpTextView.setMovementMethod(LinkMovementMethod.getInstance());
final Intent helpIntent = HelpUtils.getHelpIntent(context,
context.getString(R.string.help_uri_private_dns),
context.getClass().getName());
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(context,
ANNOTATION_URL, helpIntent);
if (linkInfo.isActionable()) {{
helpTextView.setText(AnnotationSpan.linkify(
context.getText(R.string.private_dns_help_message), linkInfo));
}} else {{
helpTextView.setText("");
}}
updateDialogInfo();
}}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {{
mMode = modeForRadioId(checkedId);
updateDialogInfo();
}}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {{
}}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {{
}}
@Override
public void afterTextChanged(Editable s) {{
updateDialogInfo();
}}
@Override
public void performClick() {{
EnforcedAdmin enforcedAdmin = getEnforcedAdmin();
if (enforcedAdmin == null) {{
super.performClick();
}} else {{
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), enforcedAdmin);
}}
}}
private EnforcedAdmin getEnforcedAdmin() {{
return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
getContext(), UserManager.DISALLOW_CONFIG_PRIVATE_DNS, UserHandle.myUserId());
}}
private boolean isDisabledByAdmin() {{
return getEnforcedAdmin() != null;
}}
private void updateDialogInfo() {{
final boolean modeProvider = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME == mMode;
if (mHostnameLayout != null) {{
mHostnameLayout.setEnabled(modeProvider);
mHostnameLayout.setErrorEnabled(false);
}}
}}
private void setSaveButtonListener() {{
View.OnClickListener onClickListener = v -> doSaveButton();
DialogInterface.OnShowListener onShowListener = dialog -> {{
if (dialog == null) {{
Log.e(TAG, "The DialogInterface is null!");
return;
}}
Button saveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
if (saveButton == null) {{
Log.e(TAG, "Can't get the save button!");
return;
}}
saveButton.setOnClickListener(onClickListener);
}};
setOnShowListener(onShowListener);
}}
@VisibleForTesting
void doSaveButton() {{
Context context = getContext();
int modeToSet = mMode;
Provider preset = providerForMode(mMode);
if (preset != null) {{
ConnectivitySettingsManager.setPrivateDnsHostname(context,
context.getString(preset.hostnameRes));
modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
}} else if (mMode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {{
if (mHostnameLayout == null || mHostnameText == null) {{
Log.e(TAG, "Can't find hostname resources!");
return;
}}
if (mHostnameText.getText().isEmpty()) {{
mHostnameLayout.setError(context.getString(R.string.private_dns_field_require));
Log.w(TAG, "The hostname is empty!");
return;
}}
if (!InternetDomainName.isValid(mHostnameText.getText().toString())) {{
mHostnameLayout.setError(context.getString(R.string.private_dns_hostname_invalid));
Log.w(TAG, "The hostname is invalid!");
return;
}}
ConnectivitySettingsManager.setPrivateDnsHostname(context,
mHostnameText.getText().toString());
}}
ConnectivitySettingsManager.setPrivateDnsMode(context, modeToSet);
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
.action(context, SettingsEnums.ACTION_PRIVATE_DNS_MODE, modeToSet);
Dialog dialog = getDialog();
if (dialog != null) {{
dialog.dismiss();
}}
}}
}}
'''
# Fix f-string doubled braces - we used {{ for literal braces in the template
# But MARKER was intended as comment - replace
content = content.replace("{MARKER}", MARKER)
path.write_text(content, encoding="utf-8")
def main() -> int:
root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
layout = root / "packages/apps/Settings/res/layout/private_dns_mode_dialog.xml"
cm = root / "packages/apps/Settings/res/values/cm_strings.xml"
java = root / (
"packages/apps/Settings/src/com/android/settings/network/"
"PrivateDnsModeDialogPreference.java"
)
for p in (layout, cm, java):
if not p.is_file():
print(f"FAIL: missing {p}", file=sys.stderr)
return 1
if MARKER in java.read_text(encoding="utf-8") and "quadnine" in layout.read_text(
encoding="utf-8"
):
print("OK: private DNS already applied")
return 0
patch_layout(layout)
patch_cm_strings(cm)
write_preference_java(java)
print(f"OK: private DNS providers applied ({len(PROVIDERS)} presets)")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,41 @@
# Apply early TARGET_UNOFFICIAL_BUILD_ID to device product makefile.
# Must run before breakfast: inherit-product of vendor/lineage parses version.mk
# immediately; vendor/extra inherit-product is too late.
#
# Usage (on rom-dev01 tree):
# python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_unofficial_build_id.py
from __future__ import annotations
from pathlib import Path
import os
import sys
MARKER = "TARGET_UNOFFICIAL_BUILD_ID := AXP-Resurection-Pro"
NEEDLE = "# Inherit some common Lineage stuff.\n"
INSERT = (
"# sunfish-a16: must precede vendor/lineage version.mk (via inherit below)\n"
f"{MARKER}\n\n"
)
def main() -> int:
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
mk = top / "device/google/sunfish/lineage_sunfish.mk"
if not mk.is_file():
print(f"FAIL: missing {mk}", file=sys.stderr)
return 1
text = mk.read_text()
if MARKER in text:
print(f"OK: already applied ({mk})")
return 0
if NEEDLE not in text:
print(f"FAIL: needle not found in {mk}", file=sys.stderr)
return 1
mk.write_text(text.replace(NEEDLE, INSERT + NEEDLE, 1))
print(f"OK: patched {mk}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,67 @@
# US-S4-1 — drop Lineage product OpenSSH binaries / rsync / procmem
**Track**: AXP Resurection Pro (Magisk), unlocked sunfish.
**Not**: slim variant; not bootloader lock; not Yama (kernel).
## AXP reference (mandatory)
Oracle: `AXP.OS-22.2-20260324-PRO-sunfish.zip` → `payload.bin` → `product.img`
(+ GOLDEN `ro.lineage.version=22.2-2026.003-sunfish-pro`, Android 15):
| Path | AXP PRO (A15) |
|------|----------------|
| `/bin/sshd`, `ssh`, `scp`, `sftp`, `ssh-keygen`, `start-ssh` | **absent** |
| `/bin/rsync`, `/bin/procmem` | **absent** |
| `/etc/init/init.openssh.rc` | **present** (`mkdir /data/ssh*`) |
### Was this an A16 Lineage add?
**No.** Lineage `vendor/lineage/config/common.mk` has shipped the Openssh
block since 2012; lineage-20 / 21 / 22.2 all still include it. AXP PRO on
A15 therefore **actively omitted** the binaries (same lineage generation
still lists them upstream). Public `AXP.OS/android_vendor_lineage`
`upstream_*` mirrors keep the Lineage Openssh block — use the **PRO zip /
GOLDEN**, not that mirror, as product oracle.
## What we ship
Match AXP PRO product surface by stripping Lineage `common.mk` package
adds (see `apply_009_drop_openssh_packages.py`) — a plain
`PRODUCT_PACKAGES := $(filter-out …)` inherit did **not** drop `sshd` from
ship `20261002T125259Z`:
- Remove packages: `scp`, `sftp`, `ssh`, `sshd`, `sshd_config`, `ssh-keygen`,
`start-ssh`, `rsync`, `procmem` (debug)
- **Keep** `init.openssh.rc` copy (AXP has it)
Remote shell / file transfer on this track remains **adb** (+ Magisk); the
rc only creates `/data/ssh*` and does not start `sshd` without the binary.
## Verify (LAB after flash)
```text
adb shell ls /product/bin/sshd # expect: No such file
adb shell ls /product/bin/rsync # expect: No such file
adb shell ls /product/etc/init/init.openssh.rc # expect: present (AXP)
adb shell pidof magiskd # expect: non-empty
```
## US-S4-2 — drop Jelly + Gallery2 (AXP PRO product)
**Oracle**: same PRO zip — `product/app` has **no** Jelly / Gallery2;
`system_ext` ships Fossify + `noDefaultBrowserWebView`. Inventory:
`docs/009-axp-pro-product-apps-20260324.md`.
- `apply_009_drop_jelly_gallery2.py` — remove Lineage `PRODUCT_PACKAGES`
adds for Jelly / Gallery2
- `NoLineageJelly` uninstallable override module (AXP stub class)
- Keep `FossifyGallery` (already overrides Gallery/Gallery2/Glimpse)
LAB after flash: no `/product/app/Jelly`, no `/product/app/Gallery2`,
FossifyGallery present.
## Out of scope
- `CONFIG_SECURITY_YAMA` (kernel / US-S5 — **done** on `162840Z`)
- Switching lunch to `user` (US-S6)
- NeoLauncher / Aosmium (wont / exception)
@@ -0,0 +1,89 @@
# 009 signing / AVB (userspace Gate G)
Clone AXP/GOLDEN posture: **`user` + `release-keys`**, device **unlocked**
(`orange`). Not IOdes locked-bootloader.
Full checklist: `docs/009-hardening-signing-checklist.md`.
## Stages
| Stage | What |
|-------|------|
| US-S1 | Generate keys → `/srv/rom/secrets/android-certs/` |
| US-S2 | Wire `PRODUCT_DEFAULT_DEV_CERTIFICATE` into bacon |
| US-S3 | Project AVB key (`AVB_CUSTOM_KEY_PATH`); decide `WITH_AVB` |
| US-S4+ | Userspace harden / CVE — separate patches |
## Generate (rom-dev01 only)
```bash
# from synced repo copy or laptop scp of scripts/
sudo -u android-build bash /srv/rom/src/lineage/vendor/sunfish-a16/../../../../\
# prefer:
bash /path/to/AXP_Resurection/scripts/009-generate-release-keys.sh
```
Script writes under `/srv/rom/secrets/android-certs/` when that parent is
writable, otherwise `$HOME/.android-certs` (Lineage default). On rom-dev01
(2026-09-29) secrets/ is root-owned without group write → keys live in
`/home/android-build/.android-certs/`. Refuses overwrite. Reminds about
encrypted offline backup before first signed ship bacon.
## Wire (US-S2 — after keys exist)
In the bacon environment (not committed secrets):
```bash
export PRODUCT_DEFAULT_DEV_CERTIFICATE=$HOME/.android-certs/releasekey
# after root relocate: /srv/rom/secrets/android-certs/releasekey
# lunch lineage_sunfish-userdebug # first signed build
# later: lineage_sunfish-user
```
`009-bacon-axp-stage1-combo4.sh` auto-exports when `$HOME/.android-certs/releasekey.pk8` exists.
## AVB (US-S3)
Default tree falls back to AOSP `external/avb/test/data/testkey_rsa2048.pem`
(and LAB has been observed with `testkey_rsa4096`) plus
`BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --flags 3` unless `WITH_AVB=true`
(`device/google/sunfish/BoardConfigLineage.mk`).
Project key generation is in `009-generate-release-keys.sh`
(`avb.pem` + `avb_pkmd.bin`). **Wire in tree** (soong-safe path), not only env:
`vendor/sunfish-a16/BoardConfig.mk` sets `BOARD_AVB_KEY_PATH` /
`BOARD_AVB_ALGORITHM=SHA256_RSA4096` when `certs/avb.pem` exists. Keep
`WITH_AVB` unset → vbmeta **flags 3** =
`HASHTREE_DISABLED|VERIFICATION_DISABLED` (libavb) → Magisk preroot on
unlocked/orange. That is **signing infrastructure**, not runtime AVB harden.
Validate after bacon (script fail-closed) on **target_files `IMAGES/`** (the
OTA pair). Do not use `out/target/product/$DEVICE/{boot,vbmeta}.img` alone —
those make images can be stock and disagree with the Magisk payload in the zip.
Gates: pubkey sha1 == `avb_pkmd.bin` (not AOSP testkey); flags 3;
`verify_image --key` + `--expected_chain_partition vbmeta_system:1:avb_pkmd.bin`;
`magiskboot cpio test` ≠ 0; LAB stays `flash.locked=0` + Magisk works.
**OEM unlocking:** leave enabled as a best-effort escape hatch (not a recovery
guarantee). Do not relock the bootloader on this Magisk track.
## Branding
`TARGET_UNOFFICIAL_BUILD_ID=AXP-Resurection-Pro` is set at the **top** of
`device/google/sunfish/lineage_sunfish.mk` (before inheriting
`vendor/lineage` → `version.mk`). Helper:
`apply_009_unofficial_build_id.py`. **Pro** = full Magisk resurrection track
(not a future slim/minimal variant). Mirrors GOLDEN’s `…-sunfish-pro` /
releasetype `PRO` intent without patching `vendor/lineage/config/version.mk`.
`branding.mk` adds `ro.axp.resurection.track=pro`. Ship scripts also publish
`AXP-Resurection-Pro-<ver>-<date>-sunfish.zip` as a hardlink/copy of the
Lineage `bacon` zip (no rebuild solely to rename).
## Never
- Commit `*.pk8`, private `*.pem`, or pass files.
- Relock LAB/GOLDEN as a project requirement (OEM unlock-always-on does not
make Magisk+relock safe).
- Flash old `kh-cve` / `kh-full` zips from 2026-09-18.
@@ -0,0 +1,22 @@
# Stage1 build fixes (FR-017)
> **2026-09-26 audit:** The FORTIFY BUG stubs below are a historical link
> workaround, **not an accepted fix**. The latest ksync FORTIFY image fails
> boot according to the owner. Clang exclusion in Kconfig references an
> undefined CC_IS_CLANG symbol. See
> [the FORTIFY audit](../../../../docs/009-fortify-boot-audit-20260926.md)
> before reusing this workaround.
Applied on host `msm-4.14` when Stage1 Graphene/AH hit compile/link issues:
1. **slab 0014 misfuzz** — Graphene patch targeted `slab_equal_or_root`;
fuzz applied to `is_root_cache`. Reverted `is_root_cache` to `return true`.
LOS already has `slab_equal_or_root` → `s == p`.
2. **FORTIFY + Clang LTO (historical, invalid as ship fix)** —
`CONFIG_FORTIFY_SOURCE=y` caused modpost undefined `__read_overflow*` /
`__write_overflow` in qcacld/techpack. Temporary `BUG()` stubs were
added in `lib/string.c` to force a link; LAB then failed to boot.
**Do not reuse.** Real work: Clang FORTIFY port — see
`docs/009-fortify-clang-port.md` and `docs/009-fortify-boot-audit-20260926.md`.
See `docs/009-kernel-hardening-map.md`.
@@ -0,0 +1,83 @@
# 009 kernel hardening Stage 1 (FR-017)
**Source**: AXP `kernel_patches` @ `df3cab2155b3` (GrapheneOS / AndroidHardening
series in Divest-era layout).
**Base**: LOS `kernel/google/msm-4.14` @ `28f9290ae067` (`4.14.357-openela`).
**Map**: `docs/009-kernel-hardening-map.md`.
## Files
| File | Role |
|------|------|
| `stage1.list` | Ordered allowlist (30 patches) |
| `apply_stage1.sh` | Applies list onto a kernel tree |
## Host usage (`rom-dev01`)
```bash
# dry-run
bash vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1.sh \
/srv/rom/src/lineage/kernel/google/msm-4.14 --dry-run
# apply (dirties the lineage kernel checkout)
bash vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1.sh \
/srv/rom/src/lineage/kernel/google/msm-4.14
```
Optional: `AXP_KERNEL_PATCHES=/path/to/kernel_patches`.
After apply, flip `sunfish_defconfig` options that the new Kconfig allows
(at least `CONFIG_FORTIFY_SOURCE=y`); see map `KH-CFG`.
## Boot-OK pack (2026-09-28 LAB) — prefer for Magisk bacon
Stage1 Graphene alone historically hung at the Google logo on LOS23/Clang.
The pack that boots with Magisk-track `boot-make` is **combo4 + Clang FORTIFY**
(no Stage1 mega-pack):
```bash
# on a reset msm-4.14 @ 28f9290ae067:
bash vendor/sunfish-a16/patches/009-kernel-hardening/apply_combo4_fortify.sh \
/path/to/msm-4.14
```
That applies `fortify-clang/` (incl. nested `__underlying_strlen` fix) +
driver bounds + defconfig:
`FORTIFY_SOURCE`, `INIT_ON_*`, `SECURITY_DMESG_RESTRICT`,
`SLAB_FREELIST_HARDENED`. Bacon entrypoint:
`scripts/009-bacon-axp-combo4-fortify.sh`.
## Stage1 + combo4 (full bacon, Wi‑Fi coherent)
```bash
# reset msm-4.14 @ 28f9290ae067, then:
bash vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1_combo4.sh \
/path/to/msm-4.14
# bacon: scripts/009-bacon-axp-stage1-combo4.sh
```
Order: Stage1 (30) → slab BUILD-FIXES if needed → combo4+FORTIFY.
Ship via **sideload of the zip** (matching vendor/`wlan.ko`). Do not flash
only the new boot onto an old slot.
**LAB accepted 2026-09-29:** see `docs/009-gate-g-kernel-attribution.md`.
## GPS / location (FR-018 — do not regress)
Owner wants **precise, fast GPS** (stock LOS behaviour). **Never** apply AXP
location lockdown, SUPL/IMSI stripping that hurts TTFF, or extra location
caps vs LOS 23.2. Stage1 list, `cve-strict.list`, and denylist must stay
free of location-lockdown series (`KH-LOC` = skip). If a future CVE hunk
touches GNSS/GPS paths only for a real security fix, keep LOS-equivalent
behaviour and document; do not “harden location” as a side goal.
## Not in Stage 1 alone
- CVE mega — now staged via `apply_stage1_combo4_cve.sh` + `cve-strict.list`
(~503 AXP allowlist from 2026-09-18 strict pass; `cve-denylist.txt` skipped).
Bacon: `scripts/009-bacon-axp-stage1-combo4-cve.sh` (test-keys; after
boot-OK Stage1 pack). **Do not** flash `20260918*-axp-kh-cve/full`.
- LinuxIncrementals
- Failed probe patches (port later)
- Location lockdown (FR-018)
- Iodé kernel (no sunfish 4.14 fork found)
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
# Apply the LAB-verified combo4 + Clang FORTIFY pack onto a clean msm-4.14 tree.
#
# Boot-OK on sunfish LAB 2026-09-28 (ksync combo4-fortify-fix). This is NOT
# the full Graphene Stage1 pack (Stage1 alone historically hung at Google logo).
#
# Usage: apply_combo4_fortify.sh <kernel-tree>
# Expects a virgin / reset tree (apply_fortify_clang.py is not idempotent).
set -e
K=${1:?kernel tree}
HERE=$(cd "$(dirname "$0")" && pwd)
FC="$HERE/fortify-clang"
test -d "$K/include/linux"
test -f "$FC/apply_fortify_clang.py"
test -f "$FC/0001-driver-bounds-rtac-fils.patch"
python3 "$FC/apply_fortify_clang.py" "$K"
patch -p1 --forward --batch -d "$K" < "$FC/0001-driver-bounds-rtac-fils.patch"
DEF="$K/arch/arm64/configs/sunfish_defconfig"
test -f "$DEF"
# Drop any prior toggles we manage, then enable the boot-OK set.
for k in \
FORTIFY_SOURCE \
INIT_ON_ALLOC_DEFAULT_ON \
INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT \
SLAB_FREELIST_HARDENED \
SLAB_HARDENED
do
sed -i "/CONFIG_${k}/d" "$DEF"
done
# Keep KernelSU off on this Magisk track.
sed -i '/CONFIG_KSU/d' "$DEF"
echo '# CONFIG_KSU is not set' >> "$DEF"
for k in \
CONFIG_FORTIFY_SOURCE \
CONFIG_INIT_ON_ALLOC_DEFAULT_ON \
CONFIG_INIT_ON_FREE_DEFAULT_ON \
CONFIG_SECURITY_DMESG_RESTRICT \
CONFIG_SLAB_FREELIST_HARDENED
do
echo "${k}=y" >> "$DEF"
done
echo "OK: combo4+FORTIFY applied on $K"
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
# Apply AXP CVE allowlist onto msm-4.14 (after Stage1+combo4).
# Source: AXP kernel_patches + cve_checker strict list / bisect slices.
#
# Safety (2026-09-29 review):
# - Default: ABORT on first patch failure (no continue-with-partial-tree).
# - Keep *.rej / *.orig under CVE_REJECT_DIR (do not silently delete).
# - Treat large line-offsets / fuzz as WARN or FAIL (mis-apply risk).
# - Set CVE_STRICT_CONTINUE=1 only for forensic runs (explicit).
set -euo pipefail
KP="${AXP_KERNEL_PATCHES:-/srv/rom/references/axp/kernel_patches}"
KERNEL_DIR="${1:-/srv/rom/src/lineage/kernel/google/msm-4.14}"
HERE="$(cd "$(dirname "$0")" && pwd)"
LIST="${CVE_STRICT_LIST:-$HERE/cve-strict.list}"
DENY="${CVE_DENYLIST:-$HERE/cve-denylist.txt}"
LOG_DIR="${CVE_APPLY_LOG_DIR:-/srv/rom/artifacts/009-axp-los23-port/cve-stage}"
CONTINUE="${CVE_STRICT_CONTINUE:-0}"
# Reject apply if GNU patch reports fuzz >= this (0 = disable fuzz fail)
MAX_FUZZ="${CVE_MAX_FUZZ:-1}"
# Reject if any hunk offset abs(lines) >= this (0 = disable)
MAX_ABS_OFFSET="${CVE_MAX_ABS_OFFSET:-200}"
DRY_RUN=0
[[ "${2:-}" == "--dry-run" ]] && DRY_RUN=1
if [[ ! -d "$KERNEL_DIR/include/linux" ]]; then
echo "error: kernel dir missing: $KERNEL_DIR" >&2
exit 1
fi
if [[ ! -d "$KP" ]]; then
echo "error: AXP kernel_patches missing: $KP" >&2
exit 1
fi
if [[ ! -f "$LIST" ]]; then
echo "error: list missing: $LIST" >&2
exit 1
fi
mkdir -p "$LOG_DIR"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
LOG="$LOG_DIR/apply-cve-strict-${STAMP}.log"
FAIL_LIST="$LOG_DIR/apply-cve-strict-${STAMP}.fail.txt"
REJECT_DIR="${CVE_REJECT_DIR:-$LOG_DIR/rejects-${STAMP}}"
mkdir -p "$REJECT_DIR"
: >"$FAIL_LIST"
declare -A DENY_MAP=()
if [[ -f "$DENY" ]]; then
while IFS= read -r d || [[ -n "$d" ]]; do
[[ -z "$d" || "$d" =~ ^# ]] && continue
if [[ "$d" == *"/kernel_patches/"* ]]; then
DENY_MAP["${d##*/kernel_patches/}"]=1
else
DENY_MAP["$d"]=1
fi
done <"$DENY"
fi
ok=0
fail=0
skip=0
miss=0
quality_fail=0
echo "=== CVE strict apply kernel=$KERNEL_DIR dry_run=$DRY_RUN continue=$CONTINUE max_fuzz=$MAX_FUZZ max_abs_offset=$MAX_ABS_OFFSET ===" | tee "$LOG"
while IFS= read -r rel || [[ -n "$rel" ]]; do
[[ -z "$rel" || "$rel" =~ ^# ]] && continue
if [[ -n "${DENY_MAP[$rel]:-}" ]]; then
echo "SKIP_DENY $rel" | tee -a "$LOG"
skip=$((skip + 1))
continue
fi
p="$KP/$rel"
if [[ ! -f "$p" ]]; then
echo "MISS $rel" | tee -a "$LOG"
miss=$((miss + 1))
echo "$rel" >>"$FAIL_LIST"
if [[ "$CONTINUE" != "1" ]]; then
echo "error: missing patch (abort)" >&2
exit 1
fi
continue
fi
args=(-p1 --batch -d "$KERNEL_DIR" --forward --no-backup-if-mismatch)
if [[ "$DRY_RUN" -eq 1 ]]; then
args+=(--dry-run)
fi
patch_log=$(mktemp)
set +e
patch "${args[@]}" <"$p" >"$patch_log" 2>&1
prc=$?
set -e
cat "$patch_log" >>"$LOG"
# Quality: fuzz / large offsets even when exit 0
fuzz_max=0
abs_off_max=0
eval "$(python3 -c '
import re, sys
text = open(sys.argv[1], errors="replace").read()
fuzz = off = 0
for m in re.finditer(r"with fuzz (\d+)", text):
fuzz = max(fuzz, int(m.group(1)))
for m in re.finditer(r"\(offset ([+-]?\d+) lines\)", text):
off = max(off, abs(int(m.group(1))))
print(f"fuzz_max={fuzz}")
print(f"abs_off_max={off}")
' "$patch_log")"
rm -f "$patch_log"
qbad=0
if [[ "$MAX_FUZZ" -gt 0 && "$fuzz_max" -ge "$MAX_FUZZ" ]]; then
echo "QUALITY_FAIL fuzz=$fuzz_max>=$MAX_FUZZ $rel" | tee -a "$LOG"
qbad=1
fi
if [[ "$MAX_ABS_OFFSET" -gt 0 && "$abs_off_max" -ge "$MAX_ABS_OFFSET" ]]; then
echo "QUALITY_FAIL abs_offset=$abs_off_max>=$MAX_ABS_OFFSET $rel" | tee -a "$LOG"
qbad=1
fi
if [[ "$prc" -eq 0 && "$qbad" -eq 0 ]]; then
echo "OK $rel (fuzz_max=$fuzz_max abs_off_max=$abs_off_max)" | tee -a "$LOG"
ok=$((ok + 1))
continue
fi
# Failure path: preserve rejects
echo "FAIL $rel patch_rc=$prc fuzz_max=$fuzz_max abs_off_max=$abs_off_max" | tee -a "$LOG"
echo "$rel" >>"$FAIL_LIST"
fail=$((fail + 1))
[[ "$qbad" -eq 1 ]] && quality_fail=$((quality_fail + 1))
safe=$(echo "$rel" | tr '/' '_')
dest="$REJECT_DIR/$safe"
mkdir -p "$dest"
# move any rej/orig created under kernel tree
while IFS= read -r -d '' rej; do
mv "$rej" "$dest/" 2>/dev/null || cp -a "$rej" "$dest/" || true
done < <(find "$KERNEL_DIR" -name '*.rej' -print0 2>/dev/null)
while IFS= read -r -d '' orig; do
mv "$orig" "$dest/" 2>/dev/null || cp -a "$orig" "$dest/" || true
done < <(find "$KERNEL_DIR" -name '*.orig' -print0 2>/dev/null)
cp -a "$p" "$dest/patch" 2>/dev/null || true
echo "rejects_kept=$dest" | tee -a "$LOG"
if [[ "$CONTINUE" != "1" ]]; then
echo "error: abort on FAIL (set CVE_STRICT_CONTINUE=1 to keep going)" >&2
echo "CVE strict ABORT OK=$ok FAIL=$fail QUALITY_FAIL=$quality_fail SKIP_DENY=$skip MISS=$miss log=$LOG" | tee -a "$LOG"
exit 1
fi
done <"$LIST"
echo "CVE strict done OK=$ok FAIL=$fail QUALITY_FAIL=$quality_fail SKIP_DENY=$skip MISS=$miss dry_run=$DRY_RUN continue=$CONTINUE log=$LOG rejects=$REJECT_DIR" | tee -a "$LOG"
if [[ "$fail" -gt 0 && "$CONTINUE" != "1" ]]; then
exit 1
fi
if [[ "$ok" -eq 0 && "$DRY_RUN" -eq 0 ]]; then
echo "error: zero CVE patches applied" >&2
exit 1
fi
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Apply FR-017 Stage 1 AXP Graphene/AH patches to LOS msm-4.14.
# Attribute: AXP kernel_patches (GrapheneOS / AndroidHardening series via Divest-era layout).
set -euo pipefail
KP="${AXP_KERNEL_PATCHES:-/srv/rom/references/axp/kernel_patches}"
KERNEL_DIR="${1:-/srv/rom/src/lineage/kernel/google/msm-4.14}"
LIST="$(cd "$(dirname "$0")" && pwd)/stage1.list"
DRY_RUN=0
if [[ "${2:-}" == "--dry-run" ]]; then
DRY_RUN=1
fi
if [[ ! -d "$KERNEL_DIR" ]]; then
echo "error: kernel dir missing: $KERNEL_DIR" >&2
exit 1
fi
if [[ ! -d "$KP" ]]; then
echo "error: AXP kernel_patches missing: $KP" >&2
exit 1
fi
ok=0
fail=0
while IFS= read -r rel || [[ -n "$rel" ]]; do
[[ -z "$rel" || "$rel" =~ ^# ]] && continue
p="$KP/$rel"
if [[ ! -f "$p" ]]; then
echo "FAIL missing $rel"
fail=$((fail + 1))
continue
fi
args=(-p1 --batch -d "$KERNEL_DIR")
if [[ "$DRY_RUN" -eq 1 ]]; then
args+=(--dry-run)
fi
if patch "${args[@]}" < "$p"; then
echo "OK $rel"
ok=$((ok + 1))
else
echo "FAIL $rel"
fail=$((fail + 1))
find "$KERNEL_DIR" -name '*.rej' -delete 2>/dev/null || true
find "$KERNEL_DIR" -name '*.orig' -delete 2>/dev/null || true
exit 1
fi
done < "$LIST"
echo "Stage1 done OK=$ok FAIL=$fail dry_run=$DRY_RUN"
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Stage1 Graphene/AH (30 patches) + combo4 Clang FORTIFY pack on a clean msm-4.14.
#
# Order: Stage1 first (virgin tree), then apply_combo4_fortify.sh (Clang POS +
# nested __underlying_strlen + driver bounds + combo4 defconfig).
# Expects reset tree @ 28f9290ae067 (apply_fortify_clang.py is not idempotent).
#
# Full bacon with this pack ships matching vendor modules (Wi-Fi). Boot-only
# flash onto an older vendor can break wlan CRC — prefer sideload of the zip.
set -e
K=${1:?kernel tree}
HERE=$(cd "$(dirname "$0")" && pwd)
test -d "$K/include/linux"
test -x "$HERE/apply_stage1.sh" || chmod +x "$HERE/apply_stage1.sh"
test -x "$HERE/apply_combo4_fortify.sh" || chmod +x "$HERE/apply_combo4_fortify.sh"
bash "$HERE/apply_stage1.sh" "$K"
# Historical Stage1 slab/0014 misfuzz: if is_root_cache was corrupted, restore.
python3 - <<'PY' "$K"
from pathlib import Path
import sys
p = Path(sys.argv[1]) / "mm/slab.h"
t = p.read_text()
bad = """static inline bool is_root_cache(struct kmem_cache *s)
{
#ifdef CONFIG_SLAB_HARDENED
\treturn p == s;
#else
\treturn true;
#endif
}"""
good = """static inline bool is_root_cache(struct kmem_cache *s)
{
\treturn true;
}"""
if bad in t:
p.write_text(t.replace(bad, good, 1))
print("OK: reverted Stage1 slab/0014 misfuzz in is_root_cache")
else:
print("OK: is_root_cache looks unmisfuzzed (no BUILD-FIXES edit)")
PY
bash "$HERE/apply_combo4_fortify.sh" "$K"
echo "OK: Stage1 + combo4+FORTIFY applied on $K"
@@ -0,0 +1,12 @@
#!/bin/bash
# Stage1 + combo4+FORTIFY + AXP CVE strict allowlist on clean msm-4.14.
# Boot-OK baseline first (LAB 2026-09-29), then CVE mega that compiled 2026-09-18.
# FR-018: no location lockdown — keep GPS precise/fast (LOS defaults).
set -e
K=${1:?kernel tree}
HERE=$(cd "$(dirname "$0")" && pwd)
test -d "$K/include/linux"
bash "$HERE/apply_stage1_combo4.sh" "$K"
bash "$HERE/apply_cve_strict.sh" "$K"
echo "OK: Stage1 + combo4+FORTIFY + CVE-strict applied on $K (no location lockdown)"
@@ -0,0 +1,31 @@
#!/bin/bash
# US-S5-1 — enable remaining Graphene/AXP-class defconfig knobs on msm-4.14.
#
# Oracle: AXP/Graphene kernel hardening expects Yama + slab freelist random
# (combo4 already ships SLAB_FREELIST_HARDENED). LOS sunfish_defconfig left both
# off; CONFIG_LSM already lists yama but SECURITY_YAMA was unset so the LSM
# never built.
#
# Does NOT apply high-offset CVE leftovers (nf_tables deliberately skipped) or
# CVE-2022-49980 (denylist until lock-scope port).
#
# Usage: apply_uss5_1_defconfig.sh <kernel-tree>
set -euo pipefail
K=${1:?kernel tree}
DEF="$K/arch/arm64/configs/sunfish_defconfig"
test -f "$DEF"
test -f "$K/security/yama/Kconfig"
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM; do
sed -i "/CONFIG_${k}/d" "$DEF"
# drop "# CONFIG_FOO is not set" lines too (sed above only matches CONFIG_FOO=)
sed -i "/# CONFIG_${k} is not set/d" "$DEF"
done
{
echo "CONFIG_SECURITY_YAMA=y"
echo "CONFIG_SLAB_FREELIST_RANDOM=y"
} >> "$DEF"
echo "OK: US-S5-1 defconfig (YAMA + SLAB_FREELIST_RANDOM) on $K"
@@ -0,0 +1 @@
CVE-2022-49980/ANY/0001.patch
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more