`gnome-shell`, `obs` running out of file descriptors, 1000s of `anon_inode:sync_file`

I think this problem is GPU related, so I’m reporting it here, but I don’t know for sure what the issue is.

samuel@aiko ~> cat /proc/10550/cmdline
/usr/bin/obs⏎                                                                                                                                                 samuel@aiko ~> ls -lah /proc/10550/fd/ | head -n 50
total 0
dr-x------ 2 samuel samuel 44K Jul 28 13:52 .
dr-xr-xr-x 9 samuel samuel   0 Jul 28 13:52 ..
lr-x------ 1 samuel samuel  64 Jul 28 13:54 0 -> /dev/null
lrwx------ 1 samuel samuel  64 Jul 28 13:54 1 -> socket:[223717]
lrwx------ 1 samuel samuel  64 Jul 28 13:54 10 -> anon_inode:[eventfd]
lrwx------ 1 samuel samuel  64 Jul 28 13:54 100 -> socket:[255212]
lr-x------ 1 samuel samuel  64 Jul 28 13:54 1000 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10000 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10001 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10002 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10003 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10004 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10005 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10006 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10007 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10008 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10009 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 1001 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10010 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10011 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10012 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10013 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10014 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10015 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10016 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10017 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10018 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10019 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 1002 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10020 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10021 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10022 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10023 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10024 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10025 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10026 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10027 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10028 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10029 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 1003 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10030 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10031 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10032 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10033 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10034 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10035 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10036 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10037 -> anon_inode:sync_file
lr-x------ 1 samuel samuel  64 Jul 28 13:54 10038 -> anon_inode:sync_file

Same for gnome-shell.

Here is the output from nvidia-smi on that computer:

samuel@aiko ~> nvidia-smi
Mon Jul 28 14:02:20 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 575.64.05              Driver Version: 575.64.05      CUDA Version: 12.9     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4090        Off |   00000000:01:00.0  On |                  Off |
|  0%   43C    P8             28W /  450W |    2083MiB /  24564MiB |      2%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A            1889      G   /usr/bin/gnome-shell                    504MiB |
|    0   N/A  N/A           10550      G   /usr/bin/obs                            487MiB |
+-----------------------------------------------------------------------------------------+

I’m using the latest release of gnome-shell and obs.

samuel@aiko ~> gnome-shell --version
GNOME Shell 48.0
samuel@aiko ~> obs --version
OBS Studio - 31.1.1

Okay, looks like it might be similar to Fd leak with explicit sync and kde plasma

I was able to reproduce the issue in real time, and tried to figure out where the file descriptors were leaking:

Here is an example of a leak:

samuel@aiko /p/1/fd> ls -lah 42926
lr-x------ 1 samuel samuel 64 Jul 28 14:26 42926 -> anon_inode:sync_file

I captured the strace output for that file descriptor:

[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42925, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 42925, O_CLOEXEC) = 42925
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42925, 42926, O_CLOEXEC) = 42926
[pid  1903] poll([{fd=42926, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=42926, revents=POLLIN}])
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42925, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 42925, O_CLOEXEC) = 42925
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42925, 42926, O_CLOEXEC) = 42926
[pid  1903] poll([{fd=42926, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=42926, revents=POLLIN}])
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(13792, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 13792, O_CLOEXEC) = 13792
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(13792, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 13792, O_CLOEXEC) = 13792
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(13792, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 42927, O_CLOEXEC) = 42927
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42927, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 42927, O_CLOEXEC) = 42927
[pid  1903] close(42926)                = 0
[pid  1903] dup(70)                     = 42926
[pid  1933] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(71, 42926, O_CLOEXEC)  = 42926
[pid  1903] dup3(42926, 71, O_CLOEXEC)  = 71
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(71, 42926, O_CLOEXEC)  = 42926
[pid  1903] dup3(42926, 71, O_CLOEXEC)  = 71
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(71, 42926, O_CLOEXEC)  = 42926
[pid  1903] dup3(42926, 42927, O_CLOEXEC) = 42927
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(42927, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 42927, O_CLOEXEC) = 42927
[pid  1903] close(42926)                = 0
[pid  1903] dup(70)                     = 42926
[pid  1933] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(13792, 42926, O_CLOEXEC) = 42926
[pid  1903] dup3(42926, 13792, O_CLOEXEC) = 13792
[pid  1903] dup3(42926, 70, O_CLOEXEC)  = 70
[pid  1903] close(42926)                = 0
[pid  1903] openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC) = 42926
[pid  1903] fcntl(42926, F_GETFD)       = 0x1 (flags FD_CLOEXEC)
[pid  1903] dup3(71, 42926, O_CLOEXEC)  = 42926
[pid  1903] dup3(42926, 71, O_CLOEXEC)  = 71
[pid  1903] dup3(42926, 70, O_CLOEXEC)  = 70
[pid  1903] close(42926)                = 0
[pid  1903] dup(70)                     = 42926

You can see it’s used over and over again, but for some reason after this point, it’s never closed.

Okay, I was able to capture a backtrace:

>  sudo strace -f -p 18497 -e trace=dup -s 256
... a lot ...
[pid 18497] dup(37)                     = 6081
[pid 18497] dup(37)                     = 6084
[pid 18497] dup(37)                     = 6085
[pid 18497] dup(37)                     = 6085
[pid 18497] dup(37)                     = 6084
[pid 18497] dup(37)                     = 6086
[pid 18497] dup(37)                     = 6086
[pid 18497] dup(37)                     = 6085
[pid 18497] dup(37)                     = 6087
[pid 18497] dup(37)                     = 6087
[pid 18497] dup(37)                     = 6086
[pid 18497] dup(37)                     = 6088
[pid 18497] dup(37)                     = 6088
[pid 18497] dup(37)                     = 6087
[pid 18497] dup(37)                     = 6089
[pid 18497] dup(37)                     = 6089
[pid 18497] dup(37)                     = 6088
[pid 18497] dup(37)                     = 6090
[pid 18497] dup(37)                     = 6090
[pid 18497] dup(37)                     = 6089
[pid 18497] dup(37)                     = 6091
[pid 18497] dup(37)                     = 6091
[pid 18497] dup(37)                     = 6090
... etc ...

We identified that dup(37) is leaking because:

samuel@aiko ~ [SIGINT]> ls -lah /proc/18497/fd/6090
lr-x------ 1 samuel samuel 64 Jul 28 14:53 /proc/18497/fd/6090 -> anon_inode:sync_file

This descriptor never goes away.

Here is the backtrace of the leaking dup:

> sudo gdb -p 18497
... snip ...
(gdb) break dup
Breakpoint 1 at 0x7f0935d17a70
(gdb) continue
Continuing.
[Thread 0x7f08c53fc6c0 (LWP 19717) exited]

Thread 1 "gnome-shell" hit Breakpoint 1, 0x00007f0935d17a70 in dup () from /usr/lib/libc.so.6
(gdb) info registers
rax            0x7f091e935170      139677144404336
rbx            0x7ffd12a356a8      140724916147880
rcx            0x4841              18497
rdx            0x7f091e6c733b      139677141857083
rsi            0x556066acc0b0      93872527818928
rdi            0x25                37
rbp            0x556066acc0b0      0x556066acc0b0
rsp            0x7ffd12a35678      0x7ffd12a35678
r8             0x556066d1fe10      93872530259472
r9             0x0                 0
r10            0x0                 0
r11            0x556066d23ac0      93872530275008
r12            0x7ffd12a35698      140724916147864
r13            0x7ffd12a356a0      140724916147872
r14            0x0                 0
r15            0x5560697a78a0      93872574855328
rip            0x7f0935d17a70      0x7f0935d17a70 <dup>
eflags         0x213               [ CF AF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
--Type <RET> for more, q to quit, c to continue without paging--c
gs             0x0                 0
k0             0x40000             262144
k1             0x20001f            2097183
k2             0xffbfffdf          4290772959
k3             0x4000              16384
k4             0x0                 0
k5             0x1d8               472
k6             0x3b000             241664
k7             0x0                 0
fs_base        0x7f0930ab5d80      139677447970176
gs_base        0x0                 0
(gdb) bt
#0  0x00007f0935d17a70 in dup () from /usr/lib/libc.so.6
#1  0x00007f091e6a2f3d in ?? () from /usr/lib/libEGL_nvidia.so.0
#2  0x00007f091e64a156 in ?? () from /usr/lib/libEGL_nvidia.so.0
#3  0x00007f0935ffd963 in ?? () from /usr/lib/libmutter-16.so.0
#4  0x00007f0935988ae3 in cogl_onscreen_swap_buffers_with_damage () from /usr/lib/mutter-16/libmutter-cogl-16.so.0
#5  0x00007f0935eaaab9 in ?? () from /usr/lib/libmutter-16.so.0
#6  0x00007f0936018308 in ?? () from /usr/lib/libmutter-16.so.0
#7  0x00007f09362b3acc in ?? () from /usr/lib/mutter-16/libmutter-clutter-16.so.0
#8  0x00007f0936267d18 in ?? () from /usr/lib/mutter-16/libmutter-clutter-16.so.0
#9  0x00007f093653b87d in ?? () from /usr/lib/libglib-2.0.so.0
#10 0x00007f093653ccd7 in ?? () from /usr/lib/libglib-2.0.so.0
#11 0x00007f093653d097 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#12 0x00007f0935ee2f9b in meta_context_run_main_loop () from /usr/lib/libmutter-16.so.0
#13 0x00007f0935313ac6 in ?? () from /usr/lib/libffi.so.8
#14 0x00007f093531076b in ?? () from /usr/lib/libffi.so.8
#15 0x00007f093531306e in ffi_call () from /usr/lib/libffi.so.8
#16 0x00007f09363e5952 in ?? () from /usr/lib/libgjs.so.0
#17 0x00007f09363e634f in ?? () from /usr/lib/libgjs.so.0
#18 0x00007f09343682c2 in ?? () from /usr/lib/libmozjs-128.so
#19 0x00007f09343f26f1 in ?? () from /usr/lib/libmozjs-128.so
#20 0x00007f093446579f in JS::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::HandleValueArray const&, JS::MutableHandle<JS::Value>) () from /usr/lib/libmozjs-128.so
#21 0x00007f0936442f44 in ?? () from /usr/lib/libgjs.so.0
#22 0x00007f093644b86f in gjs_context_eval_module () from /usr/lib/libgjs.so.0
#23 0x00007f093644b9d9 in gjs_context_eval_module_file () from /usr/lib/libgjs.so.0
#24 0x000055603e6d9566 in ?? ()
#25 0x00007f0935c376b5 in ?? () from /usr/lib/libc.so.6
#26 0x00007f0935c37769 in __libc_start_main () from /usr/lib/libc.so.6
#27 0x000055603e6d99d5 in ?? ()

Note that rdi == 37 confirms dup(37). So, it looks like the issue is in libEGL_nvidia or libmutter or perhaps how libmutter is using EGL.

I’m going to try installing mutter-performance to see if that still has the same issue.

(Also filed a bug report here `gnome-shell` runs out of file descriptors (#8552) · Issues · GNOME / gnome-shell · GitLab)

Unfortunately, even mutter-performance and gnome-shell-performance did not fix the issue.

For reference, here is the script (fish) I was using to watch file descriptors being leaked:

for pid in (ls /proc/ | grep '^[0-9]')
  set fd_count (count /proc/$pid/fd/* ^/dev/null)
  echo (cat /proc/$pid/cmdline) fd_count: $fd_count
end | sort -nk3

I found a work around, but it’s not ideal.

So I was able to reproduce this issue with obs and pipe wire for sharing my desktop. I changed to GNOME Mutter Screen Cast and I am no longer leaking file descriptors. So it might be an interaction between PipeWire, Mutter and libEGL_nvidia. For sure, there is a leak, and libEGL_nvidia is the one calling dup.

The problem with this is that it seems to consume more CPU time (my fans are more noisy as a result). It looks like another CPU core is used just for the screen cast when using the GNOME Mutter Screen Cast capture.

We have similar issue reported internally 5352012 for tracking purpose.
Shall keep updated on the progress.