@@ -160,16 +160,17 @@ _sanity_check_python_fd_sequence(PyObject *fd_sequence)
160
160
161
161
/* Is fd found in the sorted Python Sequence? */
162
162
static int
163
- _is_fd_in_sorted_fd_sequence (int fd , PyObject * fd_sequence )
163
+ _is_fd_in_sorted_fd_sequence (int fd , int * fd_sequence ,
164
+ Py_ssize_t fd_sequence_len )
164
165
{
165
166
/* Binary search. */
166
167
Py_ssize_t search_min = 0 ;
167
- Py_ssize_t search_max = PyTuple_GET_SIZE ( fd_sequence ) - 1 ;
168
+ Py_ssize_t search_max = fd_sequence_len - 1 ;
168
169
if (search_max < 0 )
169
170
return 0 ;
170
171
do {
171
172
long middle = (search_min + search_max ) / 2 ;
172
- long middle_fd = PyLong_AsLong ( PyTuple_GET_ITEM ( fd_sequence , middle )) ;
173
+ long middle_fd = fd_sequence [ middle ] ;
173
174
if (fd == middle_fd )
174
175
return 1 ;
175
176
if (fd > middle_fd )
@@ -180,24 +181,56 @@ _is_fd_in_sorted_fd_sequence(int fd, PyObject *fd_sequence)
180
181
return 0 ;
181
182
}
182
183
184
+ /*
185
+ * Do all the Python C API calls in the parent process to turn the pass_fds
186
+ * "py_fds_to_keep" tuple into a C array. The caller owns allocation and
187
+ * freeing of the array.
188
+ *
189
+ * On error an unknown number of array elements may have been filled in.
190
+ * A Python exception has been set when an error is returned.
191
+ *
192
+ * Returns: -1 on error, 0 on success.
193
+ */
183
194
static int
184
- make_inheritable (PyObject * py_fds_to_keep , int errpipe_write )
195
+ convert_fds_to_keep_to_c (PyObject * py_fds_to_keep , int * c_fds_to_keep )
185
196
{
186
197
Py_ssize_t i , len ;
187
198
188
199
len = PyTuple_GET_SIZE (py_fds_to_keep );
189
200
for (i = 0 ; i < len ; ++ i ) {
190
201
PyObject * fdobj = PyTuple_GET_ITEM (py_fds_to_keep , i );
191
202
long fd = PyLong_AsLong (fdobj );
192
- assert (!PyErr_Occurred ());
193
- assert (0 <= fd && fd <= INT_MAX );
203
+ if (PyErr_Occurred ()) {
204
+ return -1 ;
205
+ }
206
+ if (fd < 0 || fd > INT_MAX ) {
207
+ PyErr_SetString (PyExc_ValueError ,
208
+ "fd out of range in fds_to_keep." );
209
+ return -1 ;
210
+ }
211
+ c_fds_to_keep [i ] = (int )fd ;
212
+ }
213
+ return 0 ;
214
+ }
215
+
216
+
217
+ /* This function must be async-signal-safe as it is called from child_exec()
218
+ * after fork() or vfork().
219
+ */
220
+ static int
221
+ make_inheritable (int * c_fds_to_keep , Py_ssize_t len , int errpipe_write )
222
+ {
223
+ Py_ssize_t i ;
224
+
225
+ for (i = 0 ; i < len ; ++ i ) {
226
+ int fd = c_fds_to_keep [i ];
194
227
if (fd == errpipe_write ) {
195
- /* errpipe_write is part of py_fds_to_keep . It must be closed at
228
+ /* errpipe_write is part of fds_to_keep . It must be closed at
196
229
exec(), but kept open in the child process until exec() is
197
230
called. */
198
231
continue ;
199
232
}
200
- if (_Py_set_inheritable_async_safe (( int ) fd , 1 , NULL ) < 0 )
233
+ if (_Py_set_inheritable_async_safe (fd , 1 , NULL ) < 0 )
201
234
return -1 ;
202
235
}
203
236
return 0 ;
@@ -233,7 +266,7 @@ safe_get_max_fd(void)
233
266
234
267
235
268
/* Close all file descriptors in the given range except for those in
236
- * py_fds_to_keep by invoking closer on each subrange.
269
+ * fds_to_keep by invoking closer on each subrange.
237
270
*
238
271
* If end_fd == -1, it's guessed via safe_get_max_fd(), but it isn't
239
272
* possible to know for sure what the max fd to go up to is for
@@ -243,19 +276,18 @@ safe_get_max_fd(void)
243
276
static int
244
277
_close_range_except (int start_fd ,
245
278
int end_fd ,
246
- PyObject * py_fds_to_keep ,
279
+ int * fds_to_keep ,
280
+ Py_ssize_t fds_to_keep_len ,
247
281
int (* closer )(int , int ))
248
282
{
249
283
if (end_fd == -1 ) {
250
284
end_fd = Py_MIN (safe_get_max_fd (), INT_MAX );
251
285
}
252
- Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE (py_fds_to_keep );
253
286
Py_ssize_t keep_seq_idx ;
254
- /* As py_fds_to_keep is sorted we can loop through the list closing
287
+ /* As fds_to_keep is sorted we can loop through the list closing
255
288
* fds in between any in the keep list falling within our range. */
256
- for (keep_seq_idx = 0 ; keep_seq_idx < num_fds_to_keep ; ++ keep_seq_idx ) {
257
- PyObject * py_keep_fd = PyTuple_GET_ITEM (py_fds_to_keep , keep_seq_idx );
258
- int keep_fd = PyLong_AsLong (py_keep_fd );
289
+ for (keep_seq_idx = 0 ; keep_seq_idx < fds_to_keep_len ; ++ keep_seq_idx ) {
290
+ int keep_fd = fds_to_keep [keep_seq_idx ];
259
291
if (keep_fd < start_fd )
260
292
continue ;
261
293
if (closer (start_fd , keep_fd - 1 ) != 0 )
@@ -295,7 +327,7 @@ _brute_force_closer(int first, int last)
295
327
}
296
328
297
329
/* Close all open file descriptors in the range from start_fd and higher
298
- * Do not close any in the sorted py_fds_to_keep list.
330
+ * Do not close any in the sorted fds_to_keep list.
299
331
*
300
332
* This version is async signal safe as it does not make any unsafe C library
301
333
* calls, malloc calls or handle any locks. It is _unfortunate_ to be forced
@@ -310,14 +342,16 @@ _brute_force_closer(int first, int last)
310
342
* it with some cpp #define magic to work on other OSes as well if you want.
311
343
*/
312
344
static void
313
- _close_open_fds_safe (int start_fd , PyObject * py_fds_to_keep )
345
+ _close_open_fds_safe (int start_fd , int * fds_to_keep , Py_ssize_t fds_to_keep_len )
314
346
{
315
347
int fd_dir_fd ;
316
348
317
349
fd_dir_fd = _Py_open_noraise (FD_DIR , O_RDONLY );
318
350
if (fd_dir_fd == -1 ) {
319
351
/* No way to get a list of open fds. */
320
- _close_range_except (start_fd , -1 , py_fds_to_keep , _brute_force_closer );
352
+ _close_range_except (start_fd , -1 ,
353
+ fds_to_keep , fds_to_keep_len ,
354
+ _brute_force_closer );
321
355
return ;
322
356
} else {
323
357
char buffer [sizeof (struct linux_dirent64 )];
@@ -336,7 +370,8 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
336
370
if ((fd = _pos_int_from_ascii (entry -> d_name )) < 0 )
337
371
continue ; /* Not a number. */
338
372
if (fd != fd_dir_fd && fd >= start_fd &&
339
- !_is_fd_in_sorted_fd_sequence (fd , py_fds_to_keep )) {
373
+ !_is_fd_in_sorted_fd_sequence (fd , fds_to_keep ,
374
+ fds_to_keep_len )) {
340
375
close (fd );
341
376
}
342
377
}
@@ -357,7 +392,7 @@ _unsafe_closer(int first, int last)
357
392
}
358
393
359
394
/* Close all open file descriptors from start_fd and higher.
360
- * Do not close any in the sorted py_fds_to_keep tuple.
395
+ * Do not close any in the sorted fds_to_keep tuple.
361
396
*
362
397
* This function violates the strict use of async signal safe functions. :(
363
398
* It calls opendir(), readdir() and closedir(). Of these, the one most
@@ -370,11 +405,13 @@ _unsafe_closer(int first, int last)
370
405
* http://womble.decadent.org.uk/readdir_r-advisory.html
371
406
*/
372
407
static void
373
- _close_open_fds_maybe_unsafe (int start_fd , PyObject * py_fds_to_keep )
408
+ _close_open_fds_maybe_unsafe (int start_fd , int * fds_to_keep ,
409
+ Py_ssize_t fds_to_keep_len )
374
410
{
375
411
DIR * proc_fd_dir ;
376
412
#ifndef HAVE_DIRFD
377
- while (_is_fd_in_sorted_fd_sequence (start_fd , py_fds_to_keep )) {
413
+ while (_is_fd_in_sorted_fd_sequence (start_fd , fds_to_keep ,
414
+ fds_to_keep_len )) {
378
415
++ start_fd ;
379
416
}
380
417
/* Close our lowest fd before we call opendir so that it is likely to
@@ -393,7 +430,8 @@ _close_open_fds_maybe_unsafe(int start_fd, PyObject* py_fds_to_keep)
393
430
proc_fd_dir = opendir (FD_DIR );
394
431
if (!proc_fd_dir ) {
395
432
/* No way to get a list of open fds. */
396
- _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
433
+ _close_range_except (start_fd , -1 , fds_to_keep , fds_to_keep_len ,
434
+ _unsafe_closer );
397
435
} else {
398
436
struct dirent * dir_entry ;
399
437
#ifdef HAVE_DIRFD
@@ -407,14 +445,16 @@ _close_open_fds_maybe_unsafe(int start_fd, PyObject* py_fds_to_keep)
407
445
if ((fd = _pos_int_from_ascii (dir_entry -> d_name )) < 0 )
408
446
continue ; /* Not a number. */
409
447
if (fd != fd_used_by_opendir && fd >= start_fd &&
410
- !_is_fd_in_sorted_fd_sequence (fd , py_fds_to_keep )) {
448
+ !_is_fd_in_sorted_fd_sequence (fd , fds_to_keep ,
449
+ fds_to_keep_len )) {
411
450
close (fd );
412
451
}
413
452
errno = 0 ;
414
453
}
415
454
if (errno ) {
416
455
/* readdir error, revert behavior. Highly Unlikely. */
417
- _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
456
+ _close_range_except (start_fd , -1 , fds_to_keep , fds_to_keep_len ,
457
+ _unsafe_closer );
418
458
}
419
459
closedir (proc_fd_dir );
420
460
}
@@ -442,16 +482,16 @@ _close_range_closer(int first, int last)
442
482
#endif
443
483
444
484
static void
445
- _close_open_fds (int start_fd , PyObject * py_fds_to_keep )
485
+ _close_open_fds (int start_fd , int * fds_to_keep , Py_ssize_t fds_to_keep_len )
446
486
{
447
487
#ifdef HAVE_ASYNC_SAFE_CLOSE_RANGE
448
488
if (_close_range_except (
449
- start_fd , INT_MAX , py_fds_to_keep ,
489
+ start_fd , INT_MAX , fds_to_keep , fds_to_keep_len ,
450
490
_close_range_closer ) == 0 ) {
451
491
return ;
452
492
}
453
493
#endif
454
- _close_open_fds_fallback (start_fd , py_fds_to_keep );
494
+ _close_open_fds_fallback (start_fd , fds_to_keep , fds_to_keep_len );
455
495
}
456
496
457
497
#ifdef VFORK_USABLE
@@ -544,7 +584,7 @@ child_exec(char *const exec_array[],
544
584
Py_ssize_t extra_group_size , const gid_t * extra_groups ,
545
585
uid_t uid , int child_umask ,
546
586
const void * child_sigmask ,
547
- PyObject * py_fds_to_keep ,
587
+ int * fds_to_keep , Py_ssize_t fds_to_keep_len ,
548
588
PyObject * preexec_fn ,
549
589
PyObject * preexec_fn_args_tuple )
550
590
{
@@ -554,7 +594,7 @@ child_exec(char *const exec_array[],
554
594
/* Buffer large enough to hold a hex integer. We can't malloc. */
555
595
char hex_errno [sizeof (saved_errno )* 2 + 1 ];
556
596
557
- if (make_inheritable (py_fds_to_keep , errpipe_write ) < 0 )
597
+ if (make_inheritable (fds_to_keep , fds_to_keep_len , errpipe_write ) < 0 )
558
598
goto error ;
559
599
560
600
/* Close parent's pipe ends. */
@@ -676,7 +716,7 @@ child_exec(char *const exec_array[],
676
716
/* close FDs after executing preexec_fn, which might open FDs */
677
717
if (close_fds ) {
678
718
/* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
679
- _close_open_fds (3 , py_fds_to_keep );
719
+ _close_open_fds (3 , fds_to_keep , fds_to_keep_len );
680
720
}
681
721
682
722
/* This loop matches the Lib/os.py _execvpe()'s PATH search when */
@@ -750,7 +790,7 @@ do_fork_exec(char *const exec_array[],
750
790
Py_ssize_t extra_group_size , const gid_t * extra_groups ,
751
791
uid_t uid , int child_umask ,
752
792
const void * child_sigmask ,
753
- PyObject * py_fds_to_keep ,
793
+ int * fds_to_keep , Py_ssize_t fds_to_keep_len ,
754
794
PyObject * preexec_fn ,
755
795
PyObject * preexec_fn_args_tuple )
756
796
{
@@ -801,7 +841,8 @@ do_fork_exec(char *const exec_array[],
801
841
close_fds , restore_signals , call_setsid , pgid_to_set ,
802
842
gid , extra_group_size , extra_groups ,
803
843
uid , child_umask , child_sigmask ,
804
- py_fds_to_keep , preexec_fn , preexec_fn_args_tuple );
844
+ fds_to_keep , fds_to_keep_len ,
845
+ preexec_fn , preexec_fn_args_tuple );
805
846
_exit (255 );
806
847
return 0 ; /* Dead code to avoid a potential compiler warning. */
807
848
}
@@ -881,6 +922,8 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
881
922
Py_ssize_t extra_group_size = 0 ;
882
923
int need_after_fork = 0 ;
883
924
int saved_errno = 0 ;
925
+ int * c_fds_to_keep = NULL ;
926
+ Py_ssize_t fds_to_keep_len = PyTuple_GET_SIZE (py_fds_to_keep );
884
927
885
928
PyInterpreterState * interp = PyInterpreterState_Get ();
886
929
if ((preexec_fn != Py_None ) && (interp != PyInterpreterState_Main ())) {
@@ -1031,6 +1074,15 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
1031
1074
#endif /* HAVE_SETREUID */
1032
1075
}
1033
1076
1077
+ c_fds_to_keep = PyMem_RawMalloc (fds_to_keep_len * sizeof (int ));
1078
+ if (c_fds_to_keep == NULL ) {
1079
+ PyErr_SetString (PyExc_MemoryError , "failed to malloc c_fds_to_keep" );
1080
+ goto cleanup ;
1081
+ }
1082
+ if (convert_fds_to_keep_to_c (py_fds_to_keep , c_fds_to_keep ) < 0 ) {
1083
+ goto cleanup ;
1084
+ }
1085
+
1034
1086
/* This must be the last thing done before fork() because we do not
1035
1087
* want to call PyOS_BeforeFork() if there is any chance of another
1036
1088
* error leading to the cleanup: code without calling fork(). */
@@ -1073,7 +1125,8 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
1073
1125
close_fds , restore_signals , call_setsid , pgid_to_set ,
1074
1126
gid , extra_group_size , extra_groups ,
1075
1127
uid , child_umask , old_sigmask ,
1076
- py_fds_to_keep , preexec_fn , preexec_fn_args_tuple );
1128
+ c_fds_to_keep , fds_to_keep_len ,
1129
+ preexec_fn , preexec_fn_args_tuple );
1077
1130
1078
1131
/* Parent (original) process */
1079
1132
if (pid == (pid_t )- 1 ) {
@@ -1103,6 +1156,10 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
1103
1156
PyOS_AfterFork_Parent ();
1104
1157
1105
1158
cleanup :
1159
+ if (c_fds_to_keep != NULL ) {
1160
+ PyMem_RawFree (c_fds_to_keep );
1161
+ }
1162
+
1106
1163
if (saved_errno != 0 ) {
1107
1164
errno = saved_errno ;
1108
1165
/* We can't call this above as PyOS_AfterFork_Parent() calls back
0 commit comments