@@ -208,9 +208,10 @@ static int get_max_fd(void) {
208208 return (int ) (m - 1 );
209209}
210210
211- int close_all_fds (int except [], size_t n_except ) {
211+ int close_all_fds (const int except [], size_t n_except ) {
212212 static bool have_close_range = true; /* Assume we live in the future */
213213 _cleanup_closedir_ DIR * d = NULL ;
214+ struct dirent * de ;
214215 int r = 0 ;
215216
216217 assert (n_except == 0 || except );
@@ -226,104 +227,129 @@ int close_all_fds(int except[], size_t n_except) {
226227 /* Close everything. Yay! */
227228
228229 if (close_range (3 , -1 , 0 ) >= 0 )
229- return 0 ;
230+ return 1 ;
230231
231- if (ERRNO_IS_NOT_SUPPORTED (errno ) || ERRNO_IS_PRIVILEGE (errno ))
232- have_close_range = false;
233- else
232+ if (!ERRNO_IS_NOT_SUPPORTED (errno ) && !ERRNO_IS_PRIVILEGE (errno ))
234233 return - errno ;
235234
235+ have_close_range = false;
236236 } else {
237- typesafe_qsort (except , n_except , cmp_int );
237+ _cleanup_free_ int * sorted_malloc = NULL ;
238+ size_t n_sorted ;
239+ int * sorted ;
240+
241+ assert (n_except < SIZE_MAX );
242+ n_sorted = n_except + 1 ;
243+
244+ if (n_sorted > 64 ) /* Use heap for large numbers of fds, stack otherwise */
245+ sorted = sorted_malloc = new (int , n_sorted );
246+ else
247+ sorted = newa (int , n_sorted );
248+
249+ if (sorted ) {
250+ int c = 0 ;
251+
252+ memcpy (sorted , except , n_except * sizeof (int ));
253+
254+ /* Let's add fd 2 to the list of fds, to simplify the loop below, as this
255+ * allows us to cover the head of the array the same way as the body */
256+ sorted [n_sorted - 1 ] = 2 ;
238257
239- for (size_t i = 0 ; i < n_except ; i ++ ) {
240- int start = i == 0 ? 2 : MAX (except [i - 1 ], 2 ); /* The first three fds shall always remain open */
241- int end = MAX (except [i ], 2 );
258+ typesafe_qsort (sorted , n_sorted , cmp_int );
242259
243- assert (end >= start );
260+ for (size_t i = 0 ; i < n_sorted - 1 ; i ++ ) {
261+ int start , end ;
244262
245- if (end - start <= 1 )
246- continue ;
263+ start = MAX (sorted [i ], 2 ); /* The first three fds shall always remain open */
264+ end = MAX (sorted [i + 1 ], 2 );
265+
266+ assert (end >= start );
267+
268+ if (end - start <= 1 )
269+ continue ;
270+
271+ /* Close everything between the start and end fds (both of which shall stay open) */
272+ if (close_range (start + 1 , end - 1 , 0 ) < 0 ) {
273+ if (!ERRNO_IS_NOT_SUPPORTED (errno ) && !ERRNO_IS_PRIVILEGE (errno ))
274+ return - errno ;
247275
248- /* Close everything between the start and end fds (both of which shall stay open) */
249- if (close_range (start + 1 , end - 1 , 0 ) < 0 ) {
250- if (ERRNO_IS_NOT_SUPPORTED (errno ) || ERRNO_IS_PRIVILEGE (errno ))
251276 have_close_range = false;
252- else
253- return - errno ;
254- goto opendir_fallback ;
277+ break ;
278+ }
279+
280+ c += end - start - 1 ;
255281 }
256- }
257282
258- /* The loop succeeded. Let's now close everything beyond the end */
283+ if (have_close_range ) {
284+ /* The loop succeeded. Let's now close everything beyond the end */
259285
260- if (except [ n_except - 1 ] >= INT_MAX ) /* Don't let the addition below overflow */
261- return 0 ;
286+ if (sorted [ n_sorted - 1 ] >= INT_MAX ) /* Dont let the addition below overflow */
287+ return c ;
262288
263- int start = MAX (except [n_except - 1 ], 2 );
289+ if (close_range (sorted [n_sorted - 1 ] + 1 , -1 , 0 ) >= 0 )
290+ return c + 1 ;
264291
265- if ( close_range ( start + 1 , -1 , 0 ) >= 0 )
266- return 0 ;
292+ if (! ERRNO_IS_NOT_SUPPORTED ( errno ) && ! ERRNO_IS_PRIVILEGE ( errno ) )
293+ return - errno ;
267294
268- if (ERRNO_IS_NOT_SUPPORTED (errno ) || ERRNO_IS_PRIVILEGE (errno ))
269- have_close_range = false;
270- else
271- return - errno ;
295+ have_close_range = false;
296+ }
297+ }
272298 }
299+
300+ /* Fallback on OOM or if close_range() is not supported */
273301 }
274302
275- /* Fallback for when close_range() is not supported */
276- opendir_fallback :
277303 d = opendir ("/proc/self/fd" );
278- if (d ) {
279- struct dirent * de ;
304+ if (! d ) {
305+ int fd , max_fd ;
280306
281- FOREACH_DIRENT ( de , d , return - errno ) {
282- int fd = -1 , q ;
307+ /* When /proc isn't available (for example in chroots) the fallback is brute forcing through
308+ * the fd table */
283309
284- if ( safe_atoi ( de -> d_name , & fd ) < 0 )
285- /* Let's better ignore this, just in case */
286- continue ;
310+ max_fd = get_max_fd ();
311+ if ( max_fd < 0 )
312+ return max_fd ;
287313
288- if (fd < 3 )
289- continue ;
314+ /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
315+ * spin the CPU for a long time. */
316+ if (max_fd > MAX_FD_LOOP_LIMIT )
317+ return log_debug_errno (SYNTHETIC_ERRNO (EPERM ),
318+ "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds." ,
319+ max_fd );
290320
291- if ( fd == dirfd ( d ))
292- continue ;
321+ for ( fd = 3 ; fd >= 0 ; fd = fd < max_fd ? fd + 1 : -1 ) {
322+ int q ;
293323
294324 if (fd_in_set (fd , except , n_except ))
295325 continue ;
296326
297327 q = close_nointr (fd );
298- if (q < 0 && q != - EBADF && r >= 0 ) /* Valgrind has its own FD and doesn't want to have it closed */
328+ if (q < 0 && q != - EBADF && r >= 0 )
299329 r = q ;
300330 }
301331
302332 return r ;
303333 }
304334
305- /* Fallback for when /proc isn't available (for example in chroots) by brute-forcing through the file
306- * descriptor table. */
335+ FOREACH_DIRENT ( de , d , return - errno ) {
336+ int fd = -1 , q ;
307337
308- int max_fd = get_max_fd ();
309- if ( max_fd < 0 )
310- return max_fd ;
338+ if ( safe_atoi ( de -> d_name , & fd ) < 0 )
339+ /* Let's better ignore this, just in case */
340+ continue ;
311341
312- /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
313- * spin the CPU for a long time. */
314- if (max_fd > MAX_FD_LOOP_LIMIT )
315- return log_debug_errno (SYNTHETIC_ERRNO (EPERM ),
316- "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds." ,
317- max_fd );
342+ if (fd < 3 )
343+ continue ;
318344
319- for ( int fd = 3 ; fd >= 0 ; fd = fd < max_fd ? fd + 1 : -1 ) {
320- int q ;
345+ if ( fd == dirfd ( d ))
346+ continue ;
321347
322348 if (fd_in_set (fd , except , n_except ))
323349 continue ;
324350
325351 q = close_nointr (fd );
326- if (q < 0 && q != - EBADF && r >= 0 )
352+ if (q < 0 && q != - EBADF && r >= 0 ) /* Valgrind has its own FD and doesn't want to have it closed */
327353 r = q ;
328354 }
329355
0 commit comments