Conversation
write_kstorage() looked up the existing node under rcu_read_lock() but replaced it under the group spinlock. Two writers racing on the same did could both find the same old node; the second hlist_replace_rcu() on an already-replaced node wrote through its poisoned ->pprev (LIST_POISON2), causing a kernel Oops (observed in apd during APatch re-authorization after a system_server soft restart). Move the lookup inside the spinlock so find+replace/add are atomic.
|
kstorage在什么情况下会导致这样的情况,自旋锁会导致大量写入被拦截排队。正常使用不会触发到 |
|
such as kill the system_server |
|
It will cause a kernel panic |
|
It cannot be reproduced and affects normal reading and writing, requiring manual handling of the reading and writing order |
|
Reopening this with the answers I should have given the first time. You asked the right question 1. Under what circumstances — the concrete concurrent-writer path
and there is a third writer: On top of that, the kernel itself has a cross-context entry: the LSM hooks When they do, both walk the same list and call This is not exotic on the affected device: at boot the same batch of 5 uids is added twice within 2. About the spinlock / "大量写入被拦截排队"I think this concern doesn't hold, for three reasons:
Also, writes to this group only happen on config change / authorization, not on a hot path. 3. About "cannot be reproduced"It's a race with a very narrow window, so being hard to reproduce is expected — I don't dispute that.
4. Disassembly from the device's running buildI pulled the boot partition and extracted the KP payload to confirm the code path. The lookup is outside the lock and the replace is inside it — so two racers can hold the same 5. About "requiring manual handling of the reading and writing order"If I understand the concern correctly, I think the missing property here is mutual exclusion, not
I'd like to get the correctness fix in, in whatever shape you prefer. If there's a specific scenario |
|
Follow-up: I could not reopen this PR — GitHub rejects both the GraphQL So the same patch has been resubmitted as #309, with the analysis above as its description. |
Problem
write_kstorage()had a race between finding an existing entry and replacingit:
Two writers racing on the same
didcould both find the sameoldnode.The first thread replaces it (
old->pprevbecomesLIST_POISON2); the secondthread then calls
hlist_replace_rcu()on the already-replaced node andWRITE_ONCE(*old->pprev, new)writes through the poisoned pointer(
dead000000000122), causing a kernel Oops.Observed in the field: apd (APatch daemon) crashed with
Unable to handle kernel paging request at virtual address dead000000000122(LIST_POISON2) during APatch re-authorization after a system_server soft
restart, exactly at
hlist_replace_rcu()'sold->pprev = LIST_POISON2.Fix
Move the lookup inside the group spinlock so find + replace/add are atomic
under the same lock.
remove_kstorage()already does its find+del under thelock and is unaffected.
Verified:
kstorage.ccompiles cleanly (aarch64, no new warnings).