@@ -138,16 +138,17 @@ _sanity_check_python_fd_sequence(PyObject *fd_sequence)
138
138
139
139
/* Is fd found in the sorted Python Sequence? */
140
140
static int
141
- _is_fd_in_sorted_fd_sequence (int fd , PyObject * fd_sequence )
141
+ _is_fd_in_sorted_fd_sequence (int fd , int * fd_sequence ,
142
+ Py_ssize_t fd_sequence_len )
142
143
{
143
144
/* Binary search. */
144
145
Py_ssize_t search_min = 0 ;
145
- Py_ssize_t search_max = PyTuple_GET_SIZE ( fd_sequence ) - 1 ;
146
+ Py_ssize_t search_max = fd_sequence_len - 1 ;
146
147
if (search_max < 0 )
147
148
return 0 ;
148
149
do {
149
150
long middle = (search_min + search_max ) / 2 ;
150
- long middle_fd = PyLong_AsLong ( PyTuple_GET_ITEM ( fd_sequence , middle )) ;
151
+ long middle_fd = fd_sequence [ middle ] ;
151
152
if (fd == middle_fd )
152
153
return 1 ;
153
154
if (fd > middle_fd )
@@ -158,24 +159,56 @@ _is_fd_in_sorted_fd_sequence(int fd, PyObject *fd_sequence)
158
159
return 0 ;
159
160
}
160
161
162
+ /*
163
+ * Do all the Python C API calls in the parent process to turn the pass_fds
164
+ * "py_fds_to_keep" tuple into a C array. The caller owns allocation and
165
+ * freeing of the array.
166
+ *
167
+ * On error an unknown number of array elements may have been filled in.
168
+ * A Python exception has been set when an error is returned.
169
+ *
170
+ * Returns: -1 on error, 0 on success.
171
+ */
161
172
static int
162
- make_inheritable (PyObject * py_fds_to_keep , int errpipe_write )
173
+ convert_fds_to_keep_to_c (PyObject * py_fds_to_keep , int * c_fds_to_keep )
163
174
{
164
175
Py_ssize_t i , len ;
165
176
166
177
len = PyTuple_GET_SIZE (py_fds_to_keep );
167
178
for (i = 0 ; i < len ; ++ i ) {
168
179
PyObject * fdobj = PyTuple_GET_ITEM (py_fds_to_keep , i );
169
180
long fd = PyLong_AsLong (fdobj );
170
- assert (!PyErr_Occurred ());
171
- assert (0 <= fd && fd <= INT_MAX );
181
+ if (fd == -1 && PyErr_Occurred ()) {
182
+ return -1 ;
183
+ }
184
+ if (fd < 0 || fd > INT_MAX ) {
185
+ PyErr_SetString (PyExc_ValueError ,
186
+ "fd out of range in fds_to_keep." );
187
+ return -1 ;
188
+ }
189
+ c_fds_to_keep [i ] = (int )fd ;
190
+ }
191
+ return 0 ;
192
+ }
193
+
194
+
195
+ /* This function must be async-signal-safe as it is called from child_exec()
196
+ * after fork() or vfork().
197
+ */
198
+ static int
199
+ make_inheritable (int * c_fds_to_keep , Py_ssize_t len , int errpipe_write )
200
+ {
201
+ Py_ssize_t i ;
202
+
203
+ for (i = 0 ; i < len ; ++ i ) {
204
+ int fd = c_fds_to_keep [i ];
172
205
if (fd == errpipe_write ) {
173
- /* errpipe_write is part of py_fds_to_keep . It must be closed at
206
+ /* errpipe_write is part of fds_to_keep . It must be closed at
174
207
exec(), but kept open in the child process until exec() is
175
208
called. */
176
209
continue ;
177
210
}
178
- if (_Py_set_inheritable_async_safe (( int ) fd , 1 , NULL ) < 0 )
211
+ if (_Py_set_inheritable_async_safe (fd , 1 , NULL ) < 0 )
179
212
return -1 ;
180
213
}
181
214
return 0 ;
@@ -211,7 +244,7 @@ safe_get_max_fd(void)
211
244
212
245
213
246
/* Close all file descriptors in the given range except for those in
214
- * py_fds_to_keep by invoking closer on each subrange.
247
+ * fds_to_keep by invoking closer on each subrange.
215
248
*
216
249
* If end_fd == -1, it's guessed via safe_get_max_fd(), but it isn't
217
250
* possible to know for sure what the max fd to go up to is for
@@ -221,19 +254,18 @@ safe_get_max_fd(void)
221
254
static int
222
255
_close_range_except (int start_fd ,
223
256
int end_fd ,
224
- PyObject * py_fds_to_keep ,
257
+ int * fds_to_keep ,
258
+ Py_ssize_t fds_to_keep_len ,
225
259
int (* closer )(int , int ))
226
260
{
227
261
if (end_fd == -1 ) {
228
262
end_fd = Py_MIN (safe_get_max_fd (), INT_MAX );
229
263
}
230
- Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE (py_fds_to_keep );
231
264
Py_ssize_t keep_seq_idx ;
232
- /* As py_fds_to_keep is sorted we can loop through the list closing
265
+ /* As fds_to_keep is sorted we can loop through the list closing
233
266
* fds in between any in the keep list falling within our range. */
234
- for (keep_seq_idx = 0 ; keep_seq_idx < num_fds_to_keep ; ++ keep_seq_idx ) {
235
- PyObject * py_keep_fd = PyTuple_GET_ITEM (py_fds_to_keep , keep_seq_idx );
236
- int keep_fd = PyLong_AsLong (py_keep_fd );
267
+ for (keep_seq_idx = 0 ; keep_seq_idx < fds_to_keep_len ; ++ keep_seq_idx ) {
268
+ int keep_fd = fds_to_keep [keep_seq_idx ];
237
269
if (keep_fd < start_fd )
238
270
continue ;
239
271
if (closer (start_fd , keep_fd - 1 ) != 0 )
@@ -273,7 +305,7 @@ _brute_force_closer(int first, int last)
273
305
}
274
306
275
307
/* Close all open file descriptors in the range from start_fd and higher
276
- * Do not close any in the sorted py_fds_to_keep list.
308
+ * Do not close any in the sorted fds_to_keep list.
277
309
*
278
310
* This version is async signal safe as it does not make any unsafe C library
279
311
* calls, malloc calls or handle any locks. It is _unfortunate_ to be forced
@@ -288,14 +320,16 @@ _brute_force_closer(int first, int last)
288
320
* it with some cpp #define magic to work on other OSes as well if you want.
289
321
*/
290
322
static void
291
- _close_open_fds_safe (int start_fd , PyObject * py_fds_to_keep )
323
+ _close_open_fds_safe (int start_fd , int * fds_to_keep , Py_ssize_t fds_to_keep_len )
292
324
{
293
325
int fd_dir_fd ;
294
326
295
327
fd_dir_fd = _Py_open_noraise (FD_DIR , O_RDONLY );
296
328
if (fd_dir_fd == -1 ) {
297
329
/* No way to get a list of open fds. */
298
- _close_range_except (start_fd , -1 , py_fds_to_keep , _brute_force_closer );
330
+ _close_range_except (start_fd , -1 ,
331
+ fds_to_keep , fds_to_keep_len ,
332
+ _brute_force_closer );
299
333
return ;
300
334
} else {
301
335
char buffer [sizeof (struct linux_dirent64 )];
@@ -314,7 +348,8 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
314
348
if ((fd = _pos_int_from_ascii (entry -> d_name )) < 0 )
315
349
continue ; /* Not a number. */
316
350
if (fd != fd_dir_fd && fd >= start_fd &&
317
- !_is_fd_in_sorted_fd_sequence (fd , py_fds_to_keep )) {
351
+ !_is_fd_in_sorted_fd_sequence (fd , fds_to_keep ,
352
+ fds_to_keep_len )) {
318
353
close (fd );
319
354
}
320
355
}
@@ -335,7 +370,7 @@ _unsafe_closer(int first, int last)
335
370
}
336
371
337
372
/* Close all open file descriptors from start_fd and higher.
338
- * Do not close any in the sorted py_fds_to_keep tuple.
373
+ * Do not close any in the sorted fds_to_keep tuple.
339
374
*
340
375
* This function violates the strict use of async signal safe functions. :(
341
376
* It calls opendir(), readdir() and closedir(). Of these, the one most
@@ -348,11 +383,13 @@ _unsafe_closer(int first, int last)
348
383
* http://womble.decadent.org.uk/readdir_r-advisory.html
349
384
*/
350
385
static void
351
- _close_open_fds_maybe_unsafe (int start_fd , PyObject * py_fds_to_keep )
386
+ _close_open_fds_maybe_unsafe (int start_fd , int * fds_to_keep ,
387
+ Py_ssize_t fds_to_keep_len )
352
388
{
353
389
DIR * proc_fd_dir ;
354
390
#ifndef HAVE_DIRFD
355
- while (_is_fd_in_sorted_fd_sequence (start_fd , py_fds_to_keep )) {
391
+ while (_is_fd_in_sorted_fd_sequence (start_fd , fds_to_keep ,
392
+ fds_to_keep_len )) {
356
393
++ start_fd ;
357
394
}
358
395
/* Close our lowest fd before we call opendir so that it is likely to
@@ -371,7 +408,8 @@ _close_open_fds_maybe_unsafe(int start_fd, PyObject* py_fds_to_keep)
371
408
proc_fd_dir = opendir (FD_DIR );
372
409
if (!proc_fd_dir ) {
373
410
/* No way to get a list of open fds. */
374
- _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
411
+ _close_range_except (start_fd , -1 , fds_to_keep , fds_to_keep_len ,
412
+ _unsafe_closer );
375
413
} else {
376
414
struct dirent * dir_entry ;
377
415
#ifdef HAVE_DIRFD
@@ -385,14 +423,16 @@ _close_open_fds_maybe_unsafe(int start_fd, PyObject* py_fds_to_keep)
385
423
if ((fd = _pos_int_from_ascii (dir_entry -> d_name )) < 0 )
386
424
continue ; /* Not a number. */
387
425
if (fd != fd_used_by_opendir && fd >= start_fd &&
388
- !_is_fd_in_sorted_fd_sequence (fd , py_fds_to_keep )) {
426
+ !_is_fd_in_sorted_fd_sequence (fd , fds_to_keep ,
427
+ fds_to_keep_len )) {
389
428
close (fd );
390
429
}
391
430
errno = 0 ;
392
431
}
393
432
if (errno ) {
394
433
/* readdir error, revert behavior. Highly Unlikely. */
395
- _close_range_except (start_fd , -1 , py_fds_to_keep , _unsafe_closer );
434
+ _close_range_except (start_fd , -1 , fds_to_keep , fds_to_keep_len ,
435
+ _unsafe_closer );
396
436
}
397
437
closedir (proc_fd_dir );
398
438
}
@@ -420,16 +460,16 @@ _close_range_closer(int first, int last)
420
460
#endif
421
461
422
462
static void
423
- _close_open_fds (int start_fd , PyObject * py_fds_to_keep )
463
+ _close_open_fds (int start_fd , int * fds_to_keep , Py_ssize_t fds_to_keep_len )
424
464
{
425
465
#ifdef HAVE_ASYNC_SAFE_CLOSE_RANGE
426
466
if (_close_range_except (
427
- start_fd , INT_MAX , py_fds_to_keep ,
467
+ start_fd , INT_MAX , fds_to_keep , fds_to_keep_len ,
428
468
_close_range_closer ) == 0 ) {
429
469
return ;
430
470
}
431
471
#endif
432
- _close_open_fds_fallback (start_fd , py_fds_to_keep );
472
+ _close_open_fds_fallback (start_fd , fds_to_keep , fds_to_keep_len );
433
473
}
434
474
435
475
#ifdef VFORK_USABLE
@@ -522,7 +562,7 @@ child_exec(char *const exec_array[],
522
562
int call_setgroups , size_t groups_size , const gid_t * groups ,
523
563
int call_setuid , uid_t uid , int child_umask ,
524
564
const void * child_sigmask ,
525
- PyObject * py_fds_to_keep ,
565
+ int * fds_to_keep , Py_ssize_t fds_to_keep_len ,
526
566
PyObject * preexec_fn ,
527
567
PyObject * preexec_fn_args_tuple )
528
568
{
@@ -532,7 +572,7 @@ child_exec(char *const exec_array[],
532
572
/* Buffer large enough to hold a hex integer. We can't malloc. */
533
573
char hex_errno [sizeof (saved_errno )* 2 + 1 ];
534
574
535
- if (make_inheritable (py_fds_to_keep , errpipe_write ) < 0 )
575
+ if (make_inheritable (fds_to_keep , fds_to_keep_len , errpipe_write ) < 0 )
536
576
goto error ;
537
577
538
578
/* Close parent's pipe ends. */
@@ -652,7 +692,7 @@ child_exec(char *const exec_array[],
652
692
/* close FDs after executing preexec_fn, which might open FDs */
653
693
if (close_fds ) {
654
694
/* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
655
- _close_open_fds (3 , py_fds_to_keep );
695
+ _close_open_fds (3 , fds_to_keep , fds_to_keep_len );
656
696
}
657
697
658
698
/* This loop matches the Lib/os.py _execvpe()'s PATH search when */
@@ -726,7 +766,7 @@ do_fork_exec(char *const exec_array[],
726
766
int call_setgroups , size_t groups_size , const gid_t * groups ,
727
767
int call_setuid , uid_t uid , int child_umask ,
728
768
const void * child_sigmask ,
729
- PyObject * py_fds_to_keep ,
769
+ int * fds_to_keep , Py_ssize_t fds_to_keep_len ,
730
770
PyObject * preexec_fn ,
731
771
PyObject * preexec_fn_args_tuple )
732
772
{
@@ -777,7 +817,8 @@ do_fork_exec(char *const exec_array[],
777
817
close_fds , restore_signals , call_setsid , pgid_to_set ,
778
818
call_setgid , gid , call_setgroups , groups_size , groups ,
779
819
call_setuid , uid , child_umask , child_sigmask ,
780
- py_fds_to_keep , preexec_fn , preexec_fn_args_tuple );
820
+ fds_to_keep , fds_to_keep_len ,
821
+ preexec_fn , preexec_fn_args_tuple );
781
822
_exit (255 );
782
823
return 0 ; /* Dead code to avoid a potential compiler warning. */
783
824
}
@@ -810,6 +851,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
810
851
int need_after_fork = 0 ;
811
852
int saved_errno = 0 ;
812
853
int allow_vfork ;
854
+ int * c_fds_to_keep = NULL ;
813
855
814
856
if (!PyArg_ParseTuple (
815
857
args , "OOpO!OOiiiiiiiiii" _Py_PARSE_PID "OOOiOp:fork_exec" ,
@@ -983,6 +1025,16 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
983
1025
#endif /* HAVE_SETREUID */
984
1026
}
985
1027
1028
+ Py_ssize_t fds_to_keep_len = PyTuple_GET_SIZE (py_fds_to_keep );
1029
+ c_fds_to_keep = PyMem_Malloc (fds_to_keep_len * sizeof (int ));
1030
+ if (c_fds_to_keep == NULL ) {
1031
+ PyErr_SetString (PyExc_MemoryError , "failed to malloc c_fds_to_keep" );
1032
+ goto cleanup ;
1033
+ }
1034
+ if (convert_fds_to_keep_to_c (py_fds_to_keep , c_fds_to_keep ) < 0 ) {
1035
+ goto cleanup ;
1036
+ }
1037
+
986
1038
/* This must be the last thing done before fork() because we do not
987
1039
* want to call PyOS_BeforeFork() if there is any chance of another
988
1040
* error leading to the cleanup: code without calling fork(). */
@@ -1025,7 +1077,8 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
1025
1077
close_fds , restore_signals , call_setsid , pgid_to_set ,
1026
1078
call_setgid , gid , call_setgroups , num_groups , groups ,
1027
1079
call_setuid , uid , child_umask , old_sigmask ,
1028
- py_fds_to_keep , preexec_fn , preexec_fn_args_tuple );
1080
+ c_fds_to_keep , fds_to_keep_len ,
1081
+ preexec_fn , preexec_fn_args_tuple );
1029
1082
1030
1083
/* Parent (original) process */
1031
1084
if (pid == -1 ) {
@@ -1055,6 +1108,10 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
1055
1108
PyOS_AfterFork_Parent ();
1056
1109
1057
1110
cleanup :
1111
+ if (c_fds_to_keep != NULL ) {
1112
+ PyMem_Free (c_fds_to_keep );
1113
+ }
1114
+
1058
1115
if (saved_errno != 0 ) {
1059
1116
errno = saved_errno ;
1060
1117
/* We can't call this above as PyOS_AfterFork_Parent() calls back
0 commit comments