diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-09-21 15:45:06 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-10-05 13:11:50 +0200 |
| commit | a019713118305c3d7a2c380590f7d206d25b21ae (patch) | |
| tree | 58148994c44f82d35bc57a0cdb3801cc8ab9b2db /tools/lib/python | |
| parent | ad016f0131ebfec250a3360ea75d35128b3f4aa8 (diff) | |
| download | linux-next-a019713118305c3d7a2c380590f7d206d25b21ae.tar.gz linux-next-a019713118305c3d7a2c380590f7d206d25b21ae.zip | |
fs: close files from the highest descriptor down
close_files(), __range_close() and close_cloexec_files() walk the
descriptor table from the lowest descriptor up. They used to call
filp_close(), which left the final __fput() to task work. task work is a
LIFO list, so the releases ran after the walk had finished and in the
opposite direction, highest descriptor first.
This dumb ordering is relevant for a bunch of broken but long-standing
cases. It matters whenever the ->flush() or ->release() of one file
waits for something that only the release of another file of the same
table provides. Then one of the two orders deadlocks and the other one
doesn't:
exit, fd 3 is one end of a pipe peer
------------------------------- ----
splice(socket -> pipe)
pipe_lock()
waits for data or EOF
close_files()
fd 3: pipe_release()
mutex_lock(&pipe->mutex) held by the peer
fd 5: the socket, not reached would be the peer's EOF
Programs create the thing that guards or wakes another thing first.
Hence, it gets the lower descriptor which is the layout that breaks:
(1) a tap device released before the AF_LLC socket that holds a
reference to it
(2) an unlinked fsdax file evicted before the pipe that holds its
vmspliced pages
(3) an overlayfs directory whose release queues up behind an unlink that
waits for a splice into the same directory
The exiting task is unkillable in all of them. All of that crap can
obviously also become a bug if you reorder the file descriptors.
Continue walking all three tables from the highest descriptor down. That
restores the order the deferred puts had. ->flush() moves with the
release. So it now runs highest descriptor first as well.
None of this fixes the underlying defects. For every one of these pairs
the mirrored layout deadlocked before and deadlocks
again now:
(1') splice() holding pipe->mutex across unbounded socket and tty I/O
(2') AF_LLC keeping a netdev reference without a NETDEV_UNREGISTER handler
(3') uninterruptible wait in dax_break_layout_final()
(4') ovl_splice_write() sleeping under the inode lock
It all predates the synchronous close and each should really get fixed.
Reported-by: Chris Mason <mason@kernel.org>
Link: https://patch.msgid.link/20260921-work-coredump-fixes-v3-17-8e4adb1619e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'tools/lib/python')
0 files changed, 0 insertions, 0 deletions
