I have done the following
Steps to reproduce
During testing of my Container Compose plugin, I found a deterministic descriptor leak on stock apple/container main at 48145ac7fb177d9fb14e015a0bdea4c642b36729.
- Create a temporary directory and a
DirectoryWatcher for that path.
- Invoke the watcher's initial-start path with a handler that throws.
- Repeat the failing start 32 times, matching the public watcher's retry behaviour without waiting 32 seconds.
- Enumerate
/dev/fd, resolve each descriptor with fcntl(F_GETPATH), and count only descriptors whose path is the watched directory.
- Observe exactly 32 leaked descriptors on stock
main.
The public startWatching loop retries once per second while no dispatch source exists, so a persistently failing initial handler leaks one descriptor per retry.
The same audit found that _startWatching treats descriptor 0 as an open failure even though open succeeds for every non-negative descriptor. If standard input is closed and open returns 0, the watcher rejects and leaks that valid descriptor.
Problem description
DirectoryWatcher._startWatching opens the watched directory before its initial enumeration and handler call. The dispatch source that normally owns and closes the descriptor is created only after those operations succeed. If either operation throws, the function returns without transferring ownership or closing the descriptor.
The failure path should close the descriptor before throwing. Descriptor 0 should be accepted as a successful open result. A regression test should count descriptors for the exact watched path rather than relying on a process-wide threshold, which can pass despite a leak or fail because unrelated tests opened files.
This is the focused DirectoryWatcher leak described by #1773. It is independently reproducible, but it does not by itself establish the VM virtiofs behaviour reported in #1097, so the proposed fix should not close #1097.
Environment
- OS: macOS 26.5.1 (25F80)
- Xcode: 26.6 (17F113)
- Swift: 6.3.3
- Container: source
main at 48145ac7fb177d9fb14e015a0bdea4c642b36729
Code of Conduct
I have done the following
mainbranch of this projectSteps to reproduce
During testing of my Container Compose plugin, I found a deterministic descriptor leak on stock
apple/containermainat48145ac7fb177d9fb14e015a0bdea4c642b36729.DirectoryWatcherfor that path./dev/fd, resolve each descriptor withfcntl(F_GETPATH), and count only descriptors whose path is the watched directory.main.The public
startWatchingloop retries once per second while no dispatch source exists, so a persistently failing initial handler leaks one descriptor per retry.The same audit found that
_startWatchingtreats descriptor0as an open failure even thoughopensucceeds for every non-negative descriptor. If standard input is closed andopenreturns0, the watcher rejects and leaks that valid descriptor.Problem description
DirectoryWatcher._startWatchingopens the watched directory before its initial enumeration and handler call. The dispatch source that normally owns and closes the descriptor is created only after those operations succeed. If either operation throws, the function returns without transferring ownership or closing the descriptor.The failure path should close the descriptor before throwing. Descriptor
0should be accepted as a successfulopenresult. A regression test should count descriptors for the exact watched path rather than relying on a process-wide threshold, which can pass despite a leak or fail because unrelated tests opened files.This is the focused
DirectoryWatcherleak described by #1773. It is independently reproducible, but it does not by itself establish the VM virtiofs behaviour reported in #1097, so the proposed fix should not close #1097.Environment
mainat48145ac7fb177d9fb14e015a0bdea4c642b36729Code of Conduct