Skip to content

android: defer packages.list rename refresh out of the rename lock - #326

Open
mountaser wants to merge 1 commit into
bmax121:mainfrom
mountaser:fix/defer-packages-list-rename-refresh
Open

mountaser wants to merge 1 commit into
bmax121:mainfrom
mountaser:fix/defer-packages-list-rename-refresh

Conversation

@mountaser

@mountaser mountaser commented Oct 7, 2026 •

Copy link
Copy Markdown

Summary

Queue the trusted-manager refresh with task_work_add() instead of running it inside the rename LSM hook, so no file I/O happens while /data/system is locked by the rename.

Problem

after_security_path_rename / after_security_inode_rename run inside vfs_rename() with the parent directory i_rwsem held for write, and did the whole refresh there (/data/app scan, APK signature read, packages.list(.tmp) read). A lookup in /data/system that misses the dcache takes the same i_rwsem in lookup_slow() and the task deadlocks against itself.

In 0.13.3/0.13.4 the packages.xml fallback did this and froze system_server on a Pixel 8 Pro during Play Store batch updates (bmax121/APatch#1371):

KP packages.list rename matched: /system/packages.list.tmp
KP no apk via iterate for com.example.apatch rc=-2, fallback to xml
   (the usual "KP /data/system/packages.xml size: N bytes" never follows)

task:PackageManager state:D
  down_read <- path_openat <- filp_open <- [KP] <- vfs_rename <- do_renameat2

a9224bf removed the packages.xml read, but main still does all the other I/O under the lock; it only works because the packages.list.tmp dentry happens to be cached.

Changes

  • Rename hook only queues task_work_add(current, ..., TWA_RESUME); the refresh runs on return to user space, after all VFS locks are dropped, in the same task. It reads the final packages.list (use_tmp=0).
  • If task_work_add can't be resolved the rename hook is not installed; if it fails at runtime the refresh is skipped and logged. uid_listener and reload-cfg still refresh from process context.
  • Only uid 1000 triggers it. Previously any app could trigger the /data/app scan by renaming its own .../system/packages.list.tmp.
  • All refreshes are serialized (running/again flags), so an older scan can't overwrite a newer result.
  • Drop the stale "fallback to xml" log text.

Testing

Same trigger on each build: drop caches, then rename packages.list.tmp -> packages.list as uid 1000.

Device Build Result
Xiaomi Mi 10, 4.19.157, no kCFI main + old packages.xml fallback hangs on the 1st iteration, mv in D state, stack above
Xiaomi Mi 10 this change, packages.xml read still present 10/10 completed, 0 blocked tasks; 20/20 XML reads completed
Xiaomi Mi 10 this change, 20 pm install with cold cache 20/20 ok, 20 refreshes rc=0 (manager trusted in the same event)
Pixel 8 Pro, 6.1.162 GKI, kCFI + SCS this change boots; hook cfi_handler rc: 0, bypass_kcfi done: 0; deferred callback runs under kCFI, no panic

Not included

  • lkm/manager/manager.c has the same synchronous kp_manager_scan(1) in its rename hook; a correct port also has to handle unload with a work item queued.
  • Separate hazard on >= 6.1: nested filp_open inside iterate_dir during the /data/app scan, addressed in a separate PR (android: never look up children while iterating /data/app #327).

The after-callback on security_path_rename/security_inode_rename ran the
whole trusted-manager refresh synchronously: /data/app scan, APK
signature read and packages.list(.tmp) read. It runs inside
do_renameat2()/vfs_rename() with the parent directory i_rwsem held for
write. Any lookup in that directory that misses the dcache goes through
lookup_slow()/open_last_lookups() and down_read()s the same i_rwsem, and
the task deadlocks against itself. In 0.13.3/0.13.4 the packages.xml
fallback opened /data/system/packages.xml from there, which wedged
system_server (PackageManager in D state in path_openat -> down_read
under vfs_rename) during Play Store batch updates. HEAD no longer reads
packages.xml, but it still does all of the I/O while holding
/data/system exclusively.

Queue the refresh with task_work_add(current, TWA_RESUME) instead, so it
runs when the renaming task returns to user space, after every VFS lock
is dropped, in the same task and credentials as before. It now reads the
final packages.list (use_tmp=0). If task_work_add cannot be resolved or
fails, skip the refresh and log it; the uid_listener event and
reload-cfg still refresh from process context.

Only uid 1000 (system_server) triggers the refresh. Before this, any app
could trigger the scan by renaming its own .../system/packages.list.tmp.

All trusted-manager refreshes are serialized through a running/again
flag pair. A refresh requested while one is in flight makes the runner
loop again, so an older scan cannot overwrite a newer result.

Also drop the stale "fallback to xml" wording from the log. The xml
fallback was removed in a9224bf.
@Admirepowered

Copy link
Copy Markdown
Collaborator

#322

@mountaser

Copy link
Copy Markdown
Author

#322

This isn't a duplicate of #322. #322 fixes which function gets hooked on LTO kernels. #326 fixes the deadlock from doing the refresh I/O under the rename's i_rwsem (bmax121/APatch#1371), which main still has.

As noted in the PR description, the two don't conflict and both are needed: #322's selinux_inode_rename hook goes through the same refresh path, so it picks up the deferral from #326.

#327 is separate from both. It fixes a second deadlock in the /data/app scan on the uid_listener/supercall path, which neither #322 nor #326 touches. All three merge cleanly together.

Could you reopen this?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The serialization can lose refresh requests, slot exhaustion can miss the latest rename, and synchronous callers may receive premature success.

3 open findings
What changed in this PR

Defers trusted-manager refreshes until after rename locks are released, preventing VFS lock self-deadlocks.

Changes:

  • Queues refreshes through task_work_add().
  • Restricts rename-triggered refreshes to system UID 1000.
  • Adds refresh serialization and updates logging.
File Description
kernel/​patch/​android/​userd.c Implements deferred, serialized trusted-manager refreshes.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1172 to +1173
smp_store_release(&trusted_manager_refresh_again, 1);
if (!smp_load_acquire(&trusted_manager_refresh_running)) continue;
Comment on lines +1174 to +1175
log_boot("trusted manager refresh coalesced\n");
return 0;
Comment on lines +1911 to +1913
/* every slot is queued on another renaming task; its refresh runs again */
smp_store_release(&trusted_manager_refresh_again, 1);
log_boot("packages.list rename: refresh slots busy, coalesced\n");
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.

3 participants