gofer: hard-link aliases of inotify-watched files leak dentries+inodes, bypassing --dcache
- Dominant language
- Go
- Stars
- 19.3k
- Forks
- 2k
- Avg merge
- 3d 5h
- Merged PRs (30d)
- 264
Description
### Description
Aliases share the inode's watch set. A zero-ref alias with a live watch is never entered in the dentry cache but stays alive in parent.children. On final inotify_rm_watch, OnZeroWatches fires only on one target - siblings are never revisited, so they're neither cached nor destroyed. The leaked dentry+inode holds the file's host FD open in the sentry - sentry FDs grow per alias.
### Steps to reproduce
1. Watch a file (inotify_add_watch).
2. Create a hard link to it, drop the alias to zero refs.
3. Remove the watch (inotify_rm_watch).
Repro: https://gist.github.com/ildar-safarov/70fd110f0a93d5204adec11b9ea5de21
Run: `RUNSC=/path/to/runsc sudo -E ./repro-gofer-inotify-hardlink.py`
Expected output:
```
ctl (no watch): no leaks (0 gofer.dentry, 0 gofer.inode) fds: gofer=21 sentry=137
bug (watch): 6004 leaked objects (3002 gofer.dentry, 3002 gofer.inode) fds: gofer=21 sentry=6037
logs kept at: /tmp/gofer-hl-inotify.6egj9jwa
```
### runsc version
```shell
runsc version 602040cbcc58
spec: 1.2.1
```
### docker version (if using docker)
```shell
```
### uname
Linux [REDACTED] 6.8.0-137-generic #137-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 17 20:28:23 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
### kubectl (if using Kubernetes)
```shell
```
### repo state (if built from source)
release-20260831.0-12-g602040cbc
### runsc debug logs (if available)
```shell
```
Contributor guide
Assessment
This issue has not been assessed yet.