Skip to content

Latest commit

 

History

History
187 lines (159 loc) · 12.7 KB

kernel-modules.md

File metadata and controls

187 lines (159 loc) · 12.7 KB

Kernel module contracts and verification

Source baseline

The Android Common Kernel baseline is android15-6.6 at 2bdbbebc3bff5804c139a0806aeb96187b3cbb24. Bionic is pinned to 731631f300090436d7f5df80d50b6275c8c60a93, and AOSP system/core to a3b721a32242006b59cb12bd62c9133632af3a2d.

These source revisions establish the contracts below. They do not establish compatibility with a particular vendor kernel or security policy.

Contract Primary reference
Loaded module rows, missing reference counts, permanent markers and states ACK procfs.c
Module permission, image validation, loading and removal checks ACK main.c
ELF section-table layout, offsets and sizes System V ELF sections
Release-prefix comparison when symbol CRCs exist ACK version.c
Parameter getter output and appended newline ACK params.c
Module holder links and per-module taint flags ACK sysfs.c and main.c
KernelSU module target name used by the application protection list KernelSU Kbuild
Module name buffer, including its terminating NUL ACK module.h and moduleparam.h
Kbuild replacement of hyphens and commas in module names ACK Makefile.lib
ARM64 UAPI syscall numbers ACK asm-generic/unistd.h and ARM64 inclusion
Bionic register transfer and errno handling ARM64 syscall.S
AOSP file-based module loading and non-forced removal libmodprobe_ext.cpp
GKI, vendor modules and symbol restrictions AOSP kernel modules overview

Runtime operations

The feature contract is version 3. Backend and WebUI must be updated together; the feature registry rejects mismatched contracts before exposing operations. The shared command-envelope API remains version 1.

Execution is restricted to Android AArch64 root processes using the real / sysroot. The adapter uses libc's target-specific constants. Compile-time assertions check AArch64 numbers 105 (init_module), 106 (delete_module) and 273 (finit_module), along with the 64-bit C long width. Bionic performs the register transfer to x8 and x0–x5 and translates kernel errors into errno.

Loading uses init_module with the same owned image bytes that were inspected and hashed. This avoids reopening a mutable path between digest verification and the syscall. In contrast, AOSP libmodprobe uses finit_module on an opened file. These interfaces have different security-hook paths; a policy can reject the memory-image operation even when a file-based load is permitted. There is no automatic fallback to another loading interface after rejection.

Preflight requires a little-endian ARM64 ELF64 image and one nonempty vermagic entry. The release-prefix comparison is advisory: the kernel can ignore this prefix when symbol CRCs are available. A matching prefix does not establish ABI, symbol or signature compatibility. ELF metadata is descriptive, not proof of trusted code. The displayed name uses the first name metadata entry. Without one it is a filename estimate, replacing hyphens and commas with underscores as in Kbuild. The interface marks inferred names and repeated name entries explicitly. The kernel's embedded module structure ultimately determines its runtime name; neither the filename nor descriptive metadata attests to that identity.

Removal requests use O_NONBLOCK; O_TRUNC is never passed. Live state, zero references and absence of dependents/holders are preliminary checks. The kernel still checks permissions, references and exit support at the actual syscall. Missing reference counts (-) remain unknown and prevent an unload offer. Entries visible only in sysfs are not classified as unloadable modules. Missing or partially unreadable holder listings remain unknown and prevent removal; an empty successfully read directory is a distinct state. The details view retains known dependents and reports incomplete holder state. Its taint field comes from the individual module, not the global /proc/sys/kernel/tainted bitmask. An empty readable taint attribute means no module taint flags; an unreadable one is unknown.

Application policy blocks removal of kernelsu, including when it reports zero references. The backend applies this rule to direct CLI/worker requests as well as WebUI requests, with no override field. unload_protected is supplied by the backend so the UI does not maintain a second name list. The list currently contains only the official KernelSU target; it does not discover every root fork or critical vendor driver. An unlisted name or zero reference count does not establish safety.

The WebUI requires a confirmation dialog for each mutation. The request's confirmed flag expresses caller intent; a root CLI caller can set it directly, so it does not attest that a person saw the dialog.

The ARM64 name buffer holds 56 bytes including NUL, so mutations require names of at most 55 bytes. Browsing permits up to 128 bytes for sysfs entries. Supported name characters are ASCII letters, digits, underscores, hyphens, periods and plus signs; . and .. are rejected as path components. This is an application input policy, not a claim that the kernel enforces the same character whitelist.

Process and file boundaries

Mutations run in an isolated worker with a private input file and private output file. A file lock serializes operations. Lock contention is distinguished from other I/O failures. The parent validates envelope version, command, action and the typed result, rather than accepting an arbitrary success object. Failure envelopes must accompany an unsuccessful process exit; a contradictory exit/envelope pair reports an unknown outcome. Result names must fit the ARM64 kernel buffer, including when a worker reports success. Successful removal results must identify the requested module. Directory permission changes operate on an opened directory descriptor; the final directory and lock components reject symlinks, and the lock must be a regular file. Ancestor directories remain part of the trusted application storage boundary.

The response budget is 30 seconds. Error paths terminate the userspace worker and attempt bounded reaping. An uninterruptible kernel init/exit routine may continue; timeout, missing output and abnormal termination report an unknown outcome. No mutation is retried automatically. Refreshing state does not prove rollback.

Inspection accepts regular, uncompressed .ko files up to 64 MiB. The ELF reader is isolated inside the feature and exposes a stable architecture enum. Invalid UTF-8 metadata is displayed with replacement characters and a warning; SHA-256 still covers original bytes. Parameter values preserve whitespace and remove only the one newline appended by the kernel getter. Directory scan limits and partial read failures are reported without discarding unrelated entries. Metadata is limited to 1 MiB, 8192 entries and 128 distinct keys of at most 128 bytes each. These are application resource budgets, not ELF or kernel limits; over-budget input is rejected rather than silently truncated. The metadata view mounts its contents only when expanded. Inspection requires one .modinfo section and rejects duplicate sections instead of silently selecting one. Invalid metadata offsets report invalid_input rather than a generic operation failure. These checks do not implement the kernel's full ELF, symbol, signature or ABI validation.

Load parameters have an application budget of 4096 UTF-8 bytes, separate from JavaScript string length and from kernel argument limits. Both backend and WebUI reject Unicode control characters (U+0000–U+001F and U+007F–U+009F). The UI reports invalid parameters before confirmation and preserves the input for correction.

Interface and localization

The interface follows the existing Material 3 Expressive component system and its logical spacing, rounded surfaces and state layers. Applicable guidance:

  • Material 3 lists: consistent content/action slots, scannable rows and segmented grouping.
  • Android grids and units and touch targets: scalable layouts and at least 48 CSS-pixel touch regions in this WebUI.
  • Apple accessibility and layout: legibility, focus, feedback, adaptation to larger text and reachable controls.
  • Apple right-to-left guidance: logical row alignment with separately isolated technical strings.
  • Apple alerts: mutation confirmation buttons name the actual action, retain cancellation and preserve their target text through the closing transition. Module names in confirmation sentences have independent left-to-right isolation.
  • WCAG text contrast: at least 4.5:1 for ordinary text, including active destructive labels and confirmation controls in both themes.

The feature provides 16 locales. Runtime states, empty states, action labels and metadata labels are translated; numbers follow the selected locale. Paths, module names and hashes retain their character direction without forcing the whole row to left alignment. Message keys, nonempty translations and named placeholders are checked across locales. File inspection supports Enter from the path field. Inspection progress is announced, and errors remain visible with a details action. Changing the path clears errors for the previous input revision. Invalid path and parameter fields link their accessible descriptions to the corresponding error. Cancel restores the initiating control's focus; accepted operations move focus to the active tab because mutations can disable or remove the initiating control.

Reproducible checks and limits

From the repository root:

cargo test -p duck-kernel-modules --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo check -p duck-kernel-modules --locked --target aarch64-linux-android
cargo check -p duckd --locked --no-default-features --features kernel-modules
cargo xtask line-limit

From ui/, after building the host duckd binary:

pnpm test
pnpm typecheck
pnpm lint
pnpm format:check
pnpm exec playwright test e2e/kernel-modules.spec.ts
pnpm exec playwright test e2e/layout.spec.ts e2e/screenshots.spec.ts

Coverage includes procfs variants, byte order/class checks, changed files, parameter whitespace, scan limits, worker protocol failures, bounded timeout cleanup, confirmation, pending/error feedback, keyboard focus, draft preservation, 200% text, narrow screens and RTL alignment. Browser tests use a simulated root host. Cross-compilation and host tests do not verify real-device module loading, unloading, module initialization or vendor LSM/signature acceptance.