Repository navigation
Linux kernel version 5.15.197-g08015331567-dirty adapted for IQOO Neo9 - #300
Closed
linux-tools wants to merge 35 commits into
Closed
linux-tools wants to merge 35 commits into
linux-tools wants to merge 35 commits into
Conversation
Introduce a controller for behaviors outside the vulnerability path (vendor guard neutralization, environment fixups), decoupled from the frontend x backend x middleware axes: - AncillaryPolicy concept + neutral defaults and the AncillaryStage entry points (PreSpawn/PostSpawn/PreHandoff). - Header-only AncillaryController<M> that dispatches the enabled behaviors; the write/read primitives are injected by the backend. - VrGuardPolicy registered as the first behavior, gated off (skeleton). - Host test for the registry/gate/dispatch signature; Makefile wiring.
Introduce a controller for behaviors outside the vulnerability path (vendor guard neutralization, environment fixups), decoupled from the frontend x backend x middleware axes: - AncillaryPolicy concept + neutral defaults and the AncillaryStage entry points (PreSpawn/PostSpawn/PreHandoff). - Header-only AncillaryController<M> that dispatches the enabled behaviors; the write/read primitives are injected by the backend. - VrGuardPolicy registered as the first behavior, gated off (skeleton). - Host test for the registry/gate/dispatch signature; Makefile wiring.
The route did one setsockopt and one walk per stage. The copy landing on the
caller's stack races skb page recycling, so a single call is a single lottery
ticket, and on a non-resident (one-shot) multicast write a miss is not
recoverable: w1_attempts is forced to 1 for this middleware and the miss
usually panics the kernel.
Repeat the poison/walk instead, arming from the point where the earlier copies
have overwritten each other, and hold the waiter thread on yield between the
copy and the walk (no syscall may run there or the poisoned frame is
clobbered). The counts come from route.multicast_waiter {attempts,
arm_sequence, arm_hold}; 0 keeps the compiled-in default, so existing profiles
are unchanged.
Measured on vivo 6.1.145 (iQOO 12, compact waiter): every stage of a completed
run now reports attempts=16 calls=1 success=1, i.e. the first armed poison
lands, where the single-shot form failed the same stage repeatedly.
Implements VrGuardPolicy per docs/analysis/ancillary-controller-guide.md.
- plan_vr_guard(): pure, host-testable arithmetic over the resolved profile
(__tracepoint_sys_exit image offset + offsetof(struct tracepoint, funcs));
nullopt when either fact is absent, so a profile that describes no tracepoint
can never produce a write.
- enabled(): the profile gate only (meta.vr_guard). Runtime applicability is
decided at apply() time from /proc/modules, as the guide requires: the same
GKI release ships from many vendors, so the release is not a device identity.
- apply<Middleware>(): PreSpawn only. SELinux is permissive and no victim exists
yet, so one write covers every process the run brings up, ksud included.
- The write arrives through AncillaryContext as a plain function pointer
(AncillaryZeroFn); the backend supplies it through a new
Cve2026_43499Policy::zero_word<M>() adapter. The behavior never names the
middleware, the attack path gains no indirect dispatch, and the header stays
host-compilable (the host test passes a stub).
- Profile contract: meta.vr_guard (gate), offset.vr_sys_exit_tp (symbol),
vr_guard.tracepoint_funcs (struct layout). Nothing is derived from the kernel
version: struct tracepoint gained probestub in 6.6 and moved funcs from 0x40 to
0x48, so the offset is a per-image fact.
Verified on vivo 6.1.145 (iQOO 12 / PD2307, ~4 min uptime, screen untouched):
[*] vr guard: neutralizing __tracepoint_sys_exit.funcs
image=ffffffc0823cb1c0 target=ffffff802a3cb1c0 width=8
[+] vr guard: sys_exit probe disabled (attempt 1)
[+] child is root!
[+] KernelSU ready
image = KIMAGE_TEXT_BASE + 0x23cb180 (symbol) + 0x40 (layout); target is its
direct-map alias. Measured layout for this image (BTF): struct tracepoint is
0x48 bytes with funcs at 0x40, regfunc 0x30, unregfunc 0x38, no probestub. su
works afterwards and the device stays up. The existing per-task tag strip only
covered the exploit child, which is why ksud and the shells it spawned were
still killed (upstream YuKongA#154, YuKongA#201).
Still to come in this PR (see the description): the extractor emitting
tracepoint_funcs from BTF, and the profile-core mirror so the app can author the
three fields.
Host tests: ancillary_test extended with the gate, the fail-closed plan and the
target arithmetic. The plan is deliberately gate-free: the guide keeps gate,
applicability and plan as three separate facts.
Completes the profile contract the guide asks for (section 6).
Extractor (tools/extract_rs):
- The funcs offset is now read from the image's BTF (`struct tracepoint`,
member `funcs`) instead of being a constant: on this 6.1.145 image BTF reports
sizeof(struct tracepoint) = 0x48 with funcs at 0x40, regfunc 0x30, unregfunc
0x38 and no probestub; a 6.6 image reports funcs at 0x48. Nothing consults the
release, which is what the guide requires.
- `__tracepoint_sys_exit` joins the symbol table as an optional symbol, and
--format conf emits `recommend_vr_guard = 1`, `offset.vr_sys_exit_tp` and a
`vr_guard { tracepoint_funcs }` section when the image yields them. Verified
against this device's own boot.img: 37532032 / 64, matching what the device run
used.
profile-core / app:
- NativeProfile.kt carries the gate (`recommend_vr_guard` -> meta.vr_guard), the
symbol (offset.vr_sys_exit_tp) and the layout section (vr_guard.tracepoint_funcs)
on both the writer and the reader path. The gate and the symbol are written
only when non-zero, so every existing profile keeps byte-identical output and
the frozen golden is unchanged.
- ProfileResolver accepts the two new top-level keys.
- ProfileRoundTripTest gains a vr.ko fixture: gate, layout and symbol survive a
toBinary/fromBinary round trip, and a profile without the layout decodes as
absent (fail-closed).
- Built-in profile for 6.1.145-android14-11-maybe-dirty (vivo iQOO 12 / PD2307),
with `recommend_vr_guard = 1` and the BTF-derived layout, registered in
index.conf.
Test status: `cargo test` 34/34; `:app:testDebugUnitTest` + `:profile-core:test`
all green (79 app tests), including the previously failing frozen golden — the
zero-omission above is what keeps it byte-identical.
Exported profile decode check (app-side exporter, this device):
[meta] vr_guard = 1
[offset] vr_sys_exit_tp = 37532032
[vr_guard] tracepoint_funcs = 64
The vr.ko gate/symbol/layout facts and the multicast tuning values were added as new struct members, which grew sizeof(kernel_offsets) and moved every member after them: the attack functions load those members by offset, so the disassembly gate (engineering-standards §8.2) flagged the shifted offsets as hard operand failures. Put them in padding that already exists instead: KernelMisc's tail carries the gate, the symbol offset and offsetof(struct tracepoint, funcs); the four bytes of kernel_offsets' tail padding carry the multicast tuning (attempts, arm_sequence, arm_hold). sizeof and every offset are unchanged - host probe on both trees: kernel_offsets 448, TargetProfile 712, misc@248 geometry@288 execution@328, KernelMisc 40, tail pad 4. Also bind the ancillary write adapter to the session global. Passing its session parameter through the AncillaryZeroFn function pointer stopped LTO from proving the argument constant at the new call site, which changed attack_write's register allocation (126 -> 130 instructions, one extra callee-saved register). With the adapter bound to g_exploit_session every call site passes the same constant again and do_one_write is IDENTICAL (strict, 126). cmp_disasm, baseline deff0b1 (md5 27879fa7) vs this branch (md5 02f2be01), branch tool plus the origin/main two-level tool: - owner_thread, do_one_write (all three middleware instantiations): IDENTICAL (strict). - waiter_thread / consumer_thread / run_main_route_threads: differences are adrp page encodings and rodata string offsets only; all 102 differing immediates resolve to the same member of the same object or the same string literal (0 exceptions). - do_kernel5_fake_lock_route: 174 -> 222, the intended multicast re-poison loop; the poison/walk body and the prepare/recycle order are unchanged. - multicast_owner_worker / multicast_waiter_worker: not instantiated in this branch's route (the tool reports them missing on both sides, as recorded in kernel-phys-offset-plan.md). Full record, including the device gate and its open items: docs/analysis/vr-guard-plan.md.
resolveMerged seeded the merge base with the tuning-execution map instance itself, and deepMergeValues writes into the maps it is handed. The first builtin that overrides an execution value (6.1.145-android14-11-maybe-dirty, heap.prepare_max_attempts = 12) therefore wrote that value into the shared preset, and the exporter — which merges every builtin in one process — handed it to every following profile until one set its own value. ExporterAgreementTest compares the exported set against the app's own documents and caught the drift. Copy the preset when seeding the base; each merge stays isolated.
route.multicast_waiter.{attempts,arm_sequence,arm_hold} are in the native
field table (the route reads them for the poison/walk repetition) but the
Kotlin mirror never wrote them, so the knob was unreachable from the app and
the exporter. Mirror them like the tcp route's tuning, with the native field
widths: written only when the profile provides them, absent or 0 keeps the
compiled route default, and every existing profile stays byte-identical.
Test vectors extend both the round-trip fixture and the route-section test.
ProfileMigrationEquivalenceTest requires the fixture to carry every current 6.x builtin, and the fixture-size tripwire moves from 52 to 53. The new entry carries the release's real values and declares its multicast route: the remote/main era could not express that route, and the nested-route form is passed through the converter untouched, so the legacy import resolves to the same document as the builtin.
The branch-level PR note (head/base, scale, changes, the two fixes the new builtin surfaced, verification with fresh results, reviewer items, open risks) plus the plan doc's control-flow diagrams, the refreshed verification matrix and the commit references after the reword.
Cold-boot, lock-screen, single-route (multicast_waiter) run of the final candidate binary (build/native/ghostlock, sha256 7d85c26b...) with the built-in 6.1.145-android14-11-maybe-dirty profile on the V2307A/PD2307 device: vr guard: neutralizing __tracepoint_sys_exit.funcs -> probe disabled child is root! -> handoff -> KernelSU ready; su -c id = uid=0(root) The plan doc's pending items are closed and its gate section now records the run, the per-attempt statistics (everything that failed is archived too, all route-stage panics — the KERNEL-PANIC-01 class) and the observation that the low-noise window right after boot was much better than the 240s+ window.
origin/main moved past this branch's base (kernel_phys_offset, MTK physical addresses, per-route field universe with explicit nulls). The note records how this PR's presence semantics map onto that rule when the branch lands on main, so the reviewer can decide whether to align it now.
…tion The three poison/walk knobs belong to the multicast route branch, so the editor treats them like its other fields: a route switch seeds them as placeholders, the field labels map them, and values outside the native widths (u8/u8/u16) are reported instead of being wrapped by the typed cast. Tests: switching to multicast seeds the tuning fields; 256 attempts is surfaced as invalid while the documented recipe values (128/16/20000) pass.
…ates The schema's multicast section, the 5.x reference template and the bundled 5.15 template now carry attempts / arm_sequence / arm_hold (8/8/16 bits, absent or 0 keeps the compiled default), and the PR note records the editor/validation follow-up.
The per-cluster pairing drops the last core of an odd-sized cluster and can never produce a pair that straddles two frequency groups — which is exactly the pair measured as stable on the iQOO 12 kernel line (main=4, consumer=5). Add it explicitly, guarded on both cores reporting a frequency, and make it the default; every other pair stays one tap away.
The note now answers "which approaches did this use" directly: the branch's own guide and skeleton, the in-tree per-task tag clear, the kernel-side funcs==NULL iterator behavior, the reference kit's measured parameters on this kernel line, and BTF-derived layout. It also compares against abdulla-li's YuKongA#141 and YuKongA#220/YuKongA#221 (base main, version-hardcoded layout, no device evidence) so the reviewer can see what this PR adds rather than duplicating it.
The note's head/scale line predated the last two commits; it now states the built-from C++ revision and the pushed scale, and the plan doc's checklist records the opened PR (YuKongA#228).
Cite the diffstat only; the commit count changes with every metadata commit and is visible on the PR page anyway.
Review finding: the post-loop status accepted `setsockopt(...) == 0` as success. A return code only means the copy landed — the warm-up attempts never arm the consumer at all — so the route could report ROUTE_OK without any verified write and let the caller skip its retry on an unverified target. Judge the result by the consumer's success signal alone, matching select_stack, and keep the setsockopt return in the log line.
Review findings (two): - the transport field is a u8, so a BTF offset at or above 0x100 would be narrowed onto a different tracepoint member instead of refusing the profile; the conf output now emits the layout only for 1..=0xFF and otherwise omits the whole section, which keeps the guard fail-closed. - a kernel whose BTF has no `struct tracepoint` made every non-conf format fail, because only `struct_slab_cache` was exempt from the required-field check; `vr_tracepoint_funcs` joins it.
Review finding: the multicast tuning keys narrow to UByte/UShort before any validation runs, and the exporter (which calls validateMerged, not the app controller) could silently wrap attempts = 256 to 0. The shared validation now rejects out-of-range route tuning and vr_guard layouts for every caller, with tests.
Review finding: promoting a hard-coded (4, 5) to index 0 on every device whose cores 4 and 5 report a frequency silently changed the default for unrelated devices. The suggestion now comes from the profile, exactly like recommend_shizuku: BuiltinProfileCatalog carries each entry's execution.recommended_cpus, UserProfileStore exposes it for imported documents, and the repository offers it (adding the pair when the per-cluster pairing cannot express it) while an explicit user choice always wins.
Fixes the five review findings; the note lists each response and the plan doc records them with the refreshed disassembly count (do_kernel5 174 -> 224).
Asserts the review's scenario directly: an image whose BTF has no struct tracepoint passes require_fields with the optional set (every format still extracts) and fails without it.
Cold-boot, lock-screen, single-route run of the post-review binary (sha256 9cfd7c83...) on the V2307A/PD2307 device: first attempt PASS, su = root, KernelSU ready, SELinux back to enforcing. The new criterion is visible in the log (sockopt=-1 with success=1, so status=0 rests on the consumer's verified write alone). Plan doc and PR note updated with the record.
Same-window alternating rounds of the pre-fix and post-fix binaries: 2/10 vs 2/10 passes, 2 discordant pairs (1:1), exact p = 1.00 — the stricter route criterion does not cost success rate. The same pre-fix binary scored 70% in the afternoon and 20% at night, so the metric is dominated by device fatigue after repeated panic cycles and cross-session comparisons are invalid.
…omment
The note now opens with the three things the reviewer has to settle (the
multicast commit's scope, whether to align with main's newer profile
conventions now, and two API/type choices) instead of burying them at the end.
The built-in profile's CPU pair comment described the pre-review behaviour
("selected_cpus would be overwritten"); it now matches what the app does —
offer the pair until the user picks one.
ancillary: implement the VrGuardPolicy behavior (vr.ko sys_exit neutralization) Please do further test in dev branch, and upload pre-release to gather testers
# Conflicts: # AGENTS.md # app/src/main/kotlin/com/ghostlock/app/data/AndroidProfileConfigController.kt # app/src/main/kotlin/com/ghostlock/app/ui/FieldLabels.kt # profile-core/src/main/kotlin/com/ghostlock/app/data/profile/ProfileResolver.kt # src/Makefile
… (PD2463/iQOO15 verified) - Implemented vr.ko probe neutralization in CFI stage - Redirects sys_exit tracepoint probes to no-op probestub - Verified tracepoint constants on PD2463 and iQOO15 devices - Data-only writes, idempotent, KCFI compatible - Includes tracepoint constant extraction guide for vivo devices Verified on: - PD2463 (vivo iQOO Neo10 Pro+, SM8750, kernel 6.6.89-android15-8) - iQOO15 (vivo iQOO 15, SM8750, kernel 6.6.89-android15-8) Note: Host tests and disasm validation passed; real-device functional test pending.
feat: 添加 vivo vr.ko 反 Root 检测中和模块 (基于 CFI 重定向)
Collaborator
|
vr.ko bypass is going to be move to plugins system in future, now is deleting from main |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
补丁修复的问题
uname -r无 KMI token,ksud late-load选不到模块 → 新增 DIRECT 分支ksud insmod直连sepolicy patchsend/recv 两条增量规则(仅 DIRECT=1 时执行)__state@0x28写入产生僵尸 task(PD2338 实测复现)→ 改为GHOSTLOCK_VR_TAG_B显式 opt-in,默认跳过KSU_ENFORCE格式 bug:模板%d接char*→ 改%s配套内容
5.15.197-….conf+ index.conf 追加)PAGE_SIZE宏守卫、CPU 对 env 覆盖(均 opt-in,不影响其他设备)1.2.568重要约束
KSU 在 Neo9 必须自行编译:upstream 无任何现成
kernelsu-vivo.ko;模块绑定具体内核构建(vermagic 逐字匹配 +struct module布局由该内核.config决定)。交付包里的 .ko 仅对5.15.197-g708015331567-dirty有效;Neo9 任何内核版本变化都须用新内核树重编后重新部署。本人测试通过的ksu是ReSukiSU版本