@@ -210,18 +210,23 @@ safe_get_max_fd(void)
210
210
}
211
211
212
212
213
- /* Close all file descriptors in the range from start_fd and higher
214
- * except for those in py_fds_to_keep. If the range defined by
215
- * [start_fd, safe_get_max_fd()) is large this will take a long
216
- * time as it calls close() on EVERY possible fd.
213
+ /* Close all file descriptors in the given range except for those in
214
+ * py_fds_to_keep by invoking closer on each subrange.
217
215
*
218
- * It isn't possible to know for sure what the max fd to go up to
219
- * is for processes with the capability of raising their maximum.
216
+ * If end_fd == -1, it's guessed via safe_get_max_fd(), but it isn't
217
+ * possible to know for sure what the max fd to go up to is for
218
+ * processes with the capability of raising their maximum, or in case
219
+ * a process opened a high fd and then lowered its maximum.
220
220
*/
221
- static void
222
- _close_fds_by_brute_force (long start_fd , PyObject * py_fds_to_keep )
221
+ static int
222
+ _close_range_except (int start_fd ,
223
+ int end_fd ,
224
+ PyObject * py_fds_to_keep ,
225
+ int (* closer )(int , int ))
223
226
{
224
- long end_fd = safe_get_max_fd ();
227
+ if (end_fd == -1 ) {
228
+ end_fd = Py_MIN (safe_get_max_fd (), INT_MAX );
229
+ }
225
230
Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE (py_fds_to_keep );
226
231
Py_ssize_t keep_seq_idx ;
227
232
/* As py_fds_to_keep is sorted we can loop through the list closing
@@ -231,15 +236,17 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep)
231
236
int keep_fd = PyLong_AsLong (py_keep_fd );
232
237
if (keep_fd < start_fd )
233
238
continue ;
234
- _Py_closerange (start_fd , keep_fd - 1 );
239
+ if (closer (start_fd , keep_fd - 1 ) != 0 )
240
+ return -1 ;
235
241
start_fd = keep_fd + 1 ;
236
242
}
237
243
if (start_fd <= end_fd ) {
238
- _Py_closerange (start_fd , end_fd );
244
+ if (closer (start_fd , end_fd ) != 0 )
245
+ return -1 ;
239
246
}
247
+ return 0 ;
240
248
}
241
249
242
-
243
250
#if defined(__linux__ ) && defined(HAVE_SYS_SYSCALL_H )
244
251
/* It doesn't matter if d_name has room for NAME_MAX chars; we're using this
245
252
* only to read a directory of short file descriptor number names. The kernel
@@ -255,6 +262,16 @@ struct linux_dirent64 {
255
262
char d_name [256 ]; /* Filename (null-terminated) */
256
263
};
257
264
265
+ static int
266
+ _brute_force_closer (int first , int last )
267
+ {
268
+ for (int i = first ; i <= last ; i ++ ) {
269
+ /* Ignore errors */
270
+ (void )close (i );
271
+ }
272
+ return 0 ;
273
+ }
274
+
258
275
/* Close all open file descriptors in the range from start_fd and higher
259
276
* Do not close any in the sorted py_fds_to_keep list.
260
277
*
@@ -278,7 +295,7 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
278
295
fd_dir_fd = _Py_open_noraise (FD_DIR , O_RDONLY );
279
296
if (fd_dir_fd == -1 ) {
280
297
/* No way to get a list of open fds. */
281
- _close_fds_by_brute_force (start_fd , py_fds_to_keep );
298
+ _close_range_except (start_fd , -1 , py_fds_to_keep , _brute_force_closer );
282
299
return ;
283
300
} else {
284
301
char buffer [sizeof (struct linux_dirent64 )];
@@ -306,10 +323,16 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
306
323
}
307
324
}
308
325
309
- #define _close_open_fds _close_open_fds_safe
326
+ #define _close_open_fds_fallback _close_open_fds_safe
310
327
311
328
#else /* NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */
312
329
330
+ static int
331
+ _unsafe_closer (int first , int last )
332
+ {
333
+ _Py_closerange (first , last );
334
+ return 0 ;
335
+ }
313
336
314
337
/* Close all open file descriptors from start_fd and higher.
315
338
* Do not close any in the sorted py_fds_to_keep tuple.
@@ -325,7 +348,7 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
325
348
* http://womble.decadent.org.uk/readdir_r-advisory.html
326
349
*/
327
350
static void
328
- _close_open_fds_maybe_unsafe (long start_fd , PyObject * py_fds_to_keep )
351
+ _close_open_fds_maybe_unsafe (int start_fd , PyObject * py_fds_to_keep )
329
352
{
330
353
DIR * proc_fd_dir ;
331
354
#ifndef HAVE_DIRFD
@@ -348,7 +371,7 @@ _close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep)
348
371
proc_fd_dir = opendir (FD_DIR );
349
372
if (!proc_fd_dir ) {
350
373
/* No way to get a list of open fds. */
351
- _close_fds_by_brute_force (start_fd , py_fds_to_keep );
374
+ _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
352
375
} else {
353
376
struct dirent * dir_entry ;
354
377
#ifdef HAVE_DIRFD
@@ -369,16 +392,45 @@ _close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep)
369
392
}
370
393
if (errno ) {
371
394
/* readdir error, revert behavior. Highly Unlikely. */
372
- _close_fds_by_brute_force (start_fd , py_fds_to_keep );
395
+ _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
373
396
}
374
397
closedir (proc_fd_dir );
375
398
}
376
399
}
377
400
378
- #define _close_open_fds _close_open_fds_maybe_unsafe
401
+ #define _close_open_fds_fallback _close_open_fds_maybe_unsafe
379
402
380
403
#endif /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */
381
404
405
+ /* We can use close_range() library function only if it's known to be
406
+ * async-signal-safe.
407
+ *
408
+ * On Linux, glibc explicitly documents it to be a thin wrapper over
409
+ * the system call, and other C libraries are likely to follow glibc.
410
+ */
411
+ #if defined(HAVE_CLOSE_RANGE ) && \
412
+ (defined(__linux__ ) || defined(__FreeBSD__ ))
413
+ #define HAVE_ASYNC_SAFE_CLOSE_RANGE
414
+
415
+ static int
416
+ _close_range_closer (int first , int last )
417
+ {
418
+ return close_range (first , last , 0 );
419
+ }
420
+ #endif
421
+
422
+ static void
423
+ _close_open_fds (int start_fd , PyObject * py_fds_to_keep )
424
+ {
425
+ #ifdef HAVE_ASYNC_SAFE_CLOSE_RANGE
426
+ if (_close_range_except (
427
+ start_fd , INT_MAX , py_fds_to_keep ,
428
+ _close_range_closer ) == 0 ) {
429
+ return ;
430
+ }
431
+ #endif
432
+ _close_open_fds_fallback (start_fd , py_fds_to_keep );
433
+ }
382
434
383
435
#ifdef VFORK_USABLE
384
436
/* Reset dispositions for all signals to SIG_DFL except for ignored
0 commit comments