1 /* fdleak.c -- detect file descriptor leaks
2 Copyright (C) 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 /* config.h must be included first. */
29 #if defined HAVE_SYS_RESOURCE_H
30 # include <sys/resource.h>
36 #include "dirent-safer.h"
42 #include "extendbuf.h"
44 #include "safe-atoi.h"
48 # define _(Text) gettext (Text)
53 # define N_(String) gettext_noop (String)
55 /* See locate.c for explanation as to why not use (String) */
56 # define N_(String) String
59 /* In order to detect FD leaks, we take a snapshot of the open
60 * file descriptors which are not FD_CLOEXEC when the program starts.
61 * When the program exits, we discover if there are any new
62 * file descriptors which aren't FD_CLOEXEC.
64 static int *non_cloexec_fds;
65 static size_t num_cloexec_fds;
68 /* Determine the value of the largest open fd, on systems that
69 * offer /proc/self/fd. */
71 get_proc_max_fd (void)
73 const char *path = "/proc/self/fd";
75 /* We don't use readdir_r, because we cannot trust pathconf
76 * to tell us the maximum possible length of a path in
77 * a given directory (the manpage for readdir_r claims this
78 * is the approved method, but the manpage for pathconf indicates
79 * that _PC_NAME_MAX is not an upper limit). */
80 DIR *dir = opendir_safer (path);
86 while ((dent=readdir (dir)) != NULL)
88 if (dent->d_name[0] != '.'
89 || (dent->d_name[0] != 0
90 && dent->d_name[1] != 0 && dent->d_name[1] != '.'))
92 const int fd = safe_atoi (dent->d_name, literal_quoting_style);
107 /* Estimate the value of the largest possible file descriptor */
111 struct rlimit fd_limit;
114 open_max = get_proc_max_fd ();
118 open_max = sysconf (_SC_OPEN_MAX);
120 open_max = _POSIX_OPEN_MAX; /* underestimate */
122 /* We assume if RLIMIT_NOFILE is defined, all the related macros are, too. */
123 #if defined HAVE_GETRUSAGE && defined RLIMIT_NOFILE
124 if (0 == getrlimit (RLIMIT_NOFILE, &fd_limit))
126 if (fd_limit.rlim_cur == RLIM_INFINITY)
129 return (int) fd_limit.rlim_cur;
132 /* cannot determine the limit's value */
138 visit_open_fds (int fd_min, int fd_max,
139 int (*callback)(int, void*), void *cb_context)
141 enum { MAX_POLL = 64 };
142 struct pollfd pf[MAX_POLL];
145 while (fd_min < fd_max)
148 int limit = fd_max - fd_min;
149 if (limit > MAX_POLL)
152 for (i=0; i<limit; i++)
154 pf[i].events = POLLIN|POLLOUT;
156 pf[i].fd = fd_min + i;
158 rv = poll (pf, limit, 0);
166 for (j=0; j<limit; j++)
168 if (pf[j].revents != POLLNVAL)
170 if (0 != (rv = callback (pf[j].fd, cb_context)))
181 fd_is_cloexec (int fd)
183 const int flags = fcntl (fd, F_GETFD);
184 return flags & FD_CLOEXEC;
188 /* Faking closures in C is a bit of a pain. */
189 struct remember_fd_context
197 /* Record FD is it's not FD_CLOEXEC. */
199 remember_fd_if_non_cloexec (int fd, void *context)
201 if (fd_is_cloexec (fd))
207 struct remember_fd_context * const p = context;
208 void *newbuf = extendbuf (p->buf,
209 sizeof (p->buf[0])*(p->used+1),
214 p->buf[p->used] = fd;
226 remember_non_cloexec_fds (void)
228 int max_fd = get_max_fd ();
229 struct remember_fd_context cb_data;
231 cb_data.used = cb_data.allocated = 0;
233 if (max_fd < INT_MAX)
235 visit_open_fds (0, max_fd, remember_fd_if_non_cloexec, &cb_data);
237 non_cloexec_fds = cb_data.buf;
238 num_cloexec_fds = cb_data.used;
242 struct fd_leak_context
250 /* FD is open and not close-on-exec.
251 * If it's not in the list of non-cloexec file descriptors we saw before, it's a leak.
254 find_first_leak_callback (int fd, void *context)
256 if (!fd_is_cloexec (fd))
258 struct fd_leak_context *p = context;
259 while (p->lookup_pos < p->used)
261 if (p->prev_buf[p->lookup_pos] < fd)
265 else if (p->prev_buf[p->lookup_pos] == fd)
267 /* FD was open and still is, it's not a leak. */
275 /* We come here if p->prev_buf[p->lookup_pos] > fd, or
276 if we ran out of items in the lookup table.
277 Either way, this is a leak. */
279 return -1; /* No more callbacks needed. */
286 find_first_leaked_fd (const int* prev_non_cloexec_fds, size_t n)
288 struct fd_leak_context context;
289 int max_fd = get_max_fd ();
291 if (max_fd < INT_MAX)
293 context.prev_buf = prev_non_cloexec_fds;
295 context.lookup_pos = 0;
296 context.leaked_fd = -1;
297 visit_open_fds (0, max_fd, find_first_leak_callback, &context);
298 return context.leaked_fd;
301 /* Determine if O_CLOEXEC actually works (Savannah bug #29435:
302 fd_is_cloexec () does not work on Fedora buildhosts).
305 o_cloexec_works (void)
308 int fd = open ("/", O_RDONLY|O_CLOEXEC);
311 result = fd_is_cloexec (fd);
319 open_cloexec (const char *path, int flags, ...)
323 static bool cloexec_works = false;
324 static bool cloexec_status_known = false;
328 /* this code is copied from gnulib's open-safer.c. */
330 va_start (ap, flags);
332 /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
333 creates crashing code when 'mode_t' is smaller than 'int'. */
334 mode = va_arg (ap, PROMOTED_MODE_T);
339 /* Kernels usually ignore open flags they don't recognise, so it
340 * is possible this program was built against a library which
341 * defines O_CLOEXEC, but is running on a kernel that (silently)
342 * does not recognise it. We figure this out by just trying it,
345 if (!cloexec_status_known)
347 cloexec_works = o_cloexec_works ();
348 cloexec_status_known = true;
350 fd = open (path, flags|O_CLOEXEC, mode);
351 if ((fd >= 0) && !(O_CLOEXEC && cloexec_works))
353 set_cloexec_flag (fd, true);
359 forget_non_cloexec_fds (void)
361 free (non_cloexec_fds);
362 non_cloexec_fds = NULL;
368 complain_about_leaky_fds (void)
371 const int leaking_fd = find_first_leaked_fd (non_cloexec_fds, num_cloexec_fds);
377 _("File descriptor %d will leak; please report this as a bug, "
378 "remembering to include a detailed description of the simplest "
379 "way to reproduce this problem."),