Skip to content

spinlock: handle missing raw-spin symbols for KP-owned locks - #302

Merged
Admirepowered merged 1 commit into
bmax121:mainfrom
qssn70:codex/fix-missing-raw-spinlock-fallback
Sep 1, 2026
Merged

Admirepowered merged 1 commit into
bmax121:mainfrom
qssn70:codex/fix-missing-raw-spinlock-fallback

Conversation

@qssn70

@qssn70 qssn70 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

KernelPatch assumes that the generic _raw_spin_* wrappers always exist as
callable kallsyms symbols. This is not true for kernels that inline their
spinlock wrappers.

When lookup fails, the kfunc pointer remains zero, but the existing
spin_lock() wrapper still calls it unconditionally. On the affected kernel,
the first task_ext_create() lock acquisition branches to address zero before
Android starts.

This was confirmed on Xiaomi Mi MIX 2S (polaris), Linux 4.9.327, with both
KernelPatch 0.13.5 and 0.13.8.

Confirmed root cause

The tested kernel has:

CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
CONFIG_SMP=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
CONFIG_QUEUED_SPINLOCKS=y

CONFIG_INLINE_SPIN_LOCK=y
CONFIG_INLINE_SPIN_LOCK_BH=y
CONFIG_INLINE_SPIN_LOCK_IRQ=y
CONFIG_INLINE_SPIN_LOCK_IRQSAVE=y
CONFIG_INLINE_SPIN_UNLOCK_BH=y
CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE=y

# CONFIG_DEBUG_SPINLOCK is not set
CONFIG_UNINLINE_SPIN_UNLOCK is not selected

The extracted kallsyms table contains 137,036 symbols, but none of these:

_raw_spin_lock
_raw_spin_unlock
_raw_spin_lock_irqsave
_raw_spin_unlock_irqrestore

Only queued_spin_lock_slowpath and queued_spin_unlock_wait are present;
they are not drop-in replacements for the generic wrappers.

Runtime isolation

The failure was isolated with device-side single-variable builds:

  • Initialization through bypass_selinux() boots successfully.
  • Adding task_observer() reproduces the boot failure.
  • A task-observer build containing only lock initialization and
    prepare_init_ext(init_task), with task hooks unreachable, still fails.
  • Lock initialization alone boots completely.
  • The otherwise identical slot-only build with a local TAS fallback boots
    completely (sys.boot_completed=1).

This excludes KALLSYMS parsing, the rest-init trampoline, task hooks,
copy_process, and earlier initialization stages as necessary causes.

Changes

  • Add kp_private_spin_lock() / kp_private_spin_unlock() for
    zero-initialized, KP-owned locks only.
  • Use the native _raw_spin_lock_irqsave /
    _raw_spin_unlock_irqrestore pair when both symbols resolve.
  • Otherwise use a local ARM64 fallback that:
    • saves DAIF and masks IRQs;
    • acquires with a 0/1 compare/exchange loop;
    • uses yield while contended;
    • releases with store-release;
    • restores the original DAIF value.
  • Move the task-observer and kstorage private locks to this compatibility API.

The generic linux/spinlock.h API is unchanged. The local 0/1 TAS fallback is
not exposed for kernel-owned ticket/qspinlock objects.

Both task-observer and kstorage are covered. Fixing only the first task slot
allocation would allow boot to progress until kstorage reaches the same missing
raw-spin path.

Validation

The affected-device prototype used the same DAIF/TAS fallback and completed
Android boot. The complete KernelPatch 0.13.8 candidate was reported stable
during normal use:

boot SHA-256:
80fac82815eedf162afb6b5babc04f9e023f425e43740a12cb056972fbaed975

On this kernel, the submitted private-helper refactor selects the same local
fallback backend.

The submitted commit was additionally verified with:

  • ARM64 make kpimg using Arm GNU Toolchain 12.2;
  • x86 make x86;
  • git diff --check;
  • object disassembly confirming:
    • the paired native irqsave/irqrestore symbol check;
    • DAIF save/mask/restore;
    • ldxr/stxr compare/exchange;
    • yield contention loop;
    • stlr release;
  • all taskob/kstorage normal and early-return paths release through the matching
    helper.

The refactor removes the public-header asm/atomic.h dependency from the
prototype and introduces no new READ_ONCE / WRITE_ONCE redefinition
warnings.

Scope

The confirmed root cause applies to the tested Polaris 4.9.327 kernel. Runtime
coverage on other PREEMPT ARM64 configurations remains to be validated.

Possibly related symptom reports: #21 and #301. Neither report contains enough
raw-spin evidence to claim the same root cause.

Related but different work:

@qssn70
qssn70 force-pushed the codex/fix-missing-raw-spinlock-fallback branch from 2bb94e3 to 76bb298 Compare August 31, 2026 11:36
@Admirepowered

Copy link
Copy Markdown
Collaborator

If _raw_spin_lock_irqsave is available, please use the unified path,or use no return function

@qssn70
qssn70 force-pushed the codex/fix-missing-raw-spinlock-fallback branch from 76bb298 to b778556 Compare August 31, 2026 12:24
@qssn70

qssn70 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Thanks. Updated to use unified irqsave semantics: the helper now uses the native _raw_spin_lock_irqsave / _raw_spin_unlock_irqrestore pair when both symbols are available, and otherwise falls back to the local DAIF/TAS implementation. The plain-spin branch has been removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants