Package: tint2
Version: 17.0.1-1.2
Severity: important
Tags: patch upstream
X-Debbugs-Cc: [email protected]

Dear Maintainer,

The tint2 launcher experiences significant delays when starting applications on 
Debian 13 (trixie) systems due to the interaction between tint2's file 
descriptor closing logic and the increased default ulimit values in newer 
Debian releases.

Observed Behavior
-----------------

When launching applications through tint2's panel launcher on Debian 13, there 
is a long delay before the application starts. During this time, a child 
process consumes significant CPU resources. This behavior affects all 
applications launched through tint2, including simple commands like `/bin/true`.

The issue does not occur on Debian 12 (bookworm) or other systems with lower 
ulimit values.

Root Cause
----------

tint2's close_all_fds() function in src/util/common.c iterates through all 
possible file descriptors from 3 to sysconf(_SC_OPEN_MAX):

void close_all_fds()
{
    long maxfd = sysconf(_SC_OPEN_MAX);
    for (int fd = 3; fd < maxfd; fd++) {
        close(fd);
    }
}

On Debian 13, the default ulimit value is 1,073,741,816, causing the loop to 
attempt closing approximately 1 billion file descriptors. The strace output 
shows the process iterating through these close() calls, each returning EBADF 
(bad file descriptor).

Impact
------

- Application launching through tint2 panel experiences multi minute delays
- Affects users of Debian 13 running tint2
- Particularly impacts containerized environments where high ulimits are common
- System-wide workaround: reduce ulimit via `/etc/security/limits.conf` or 
sysctl

Reproduction
------------

1. Install tint2 on Debian 13 (trixie) with kernel 6.6+
2. Verify ulimit: `ulimit -n` (will show ~1 billion)
3. Launch any application from tint2's panel launcher
4. Observe: Child process consumes CPU for several minutes
5. Verify with strace: `strace -p <child_pid>` shows repeated close() calls


Proposed Solution
-----------------

The attached patch changes the implementation to iterate only actually open 
file descriptors by reading `/proc/self/fd`:

Note
----

The upstream tint2 project is no longer accepting patches (frozen at version 
17.0.2), so this fix requires a Debian-specific patch.

-- System Information:
Debian Release: 13.1
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: arm64 (aarch64)

Kernel: Linux 6.12.41+deb13-cloud-arm64 (SMP w/2 CPU threads)
Locale: LANG=C.UTF-8, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages tint2 depends on:
ii  libc6                     2.41-12
pn  libcairo2                 <none>
pn  libgdk-pixbuf-2.0-0       <none>
ii  libglib2.0-0t64           2.84.4-3~deb13u1
pn  libgtk-3-0t64             <none>
pn  libimlib2t64              <none>
pn  libpango-1.0-0            <none>
pn  libpangocairo-1.0-0       <none>
pn  librsvg2-2                <none>
pn  libstartup-notification0  <none>
pn  libx11-6                  <none>
pn  libxcomposite1            <none>
pn  libxdamage1               <none>
pn  libxext6                  <none>
pn  libxinerama1              <none>
pn  libxrandr2                <none>
pn  libxrender1               <none>

tint2 recommends no packages.

tint2 suggests no packages.
--- a/src/util/common.c
+++ b/src/util/common.c
@@ -1088,11 +1088,31 @@ void reset_signals()
     signal(SIGCHLD, SIG_DFL);
 }

+// Close all file descriptors >= 3 by iterating only open FDs
 void close_all_fds()
 {
-    long maxfd = sysconf(_SC_OPEN_MAX);
-    for (int fd = 3; fd < maxfd; fd++) {
-        close(fd);
+    // Try /proc/self/fd (Linux) or /dev/fd (BSD)
+    const char *fd_path = "/proc/self/fd";
+    DIR *dir = opendir(fd_path);
+    if (!dir) {
+        fd_path = "/dev/fd";
+        dir = opendir(fd_path);
+    }
+
+    if (dir) {
+        int dir_fd = dirfd(dir);
+        struct dirent *entry;
+        while ((entry = readdir(dir)) != NULL) {
+            char *endptr;
+            long fd = strtol(entry->d_name, &endptr, 10);
+            if (*endptr == '\0' && fd >= 3 && fd != dir_fd) {
+                close(fd);
+            }
+        }
+        closedir(dir);
+    } else {
+        // Fallback if neither path available
+        long maxfd = sysconf(_SC_OPEN_MAX);
+        if (maxfd < 0 || maxfd > 65536) maxfd = 65536;
+        for (int fd = 3; fd < maxfd; fd++) close(fd);
     }
 }

Reply via email to