Line 0
Link Here
|
|
|
1 |
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c |
2 |
index 07dd54dba9..ef1b7a8ad5 100644 |
3 |
--- Modules/_posixsubprocess.c |
4 |
+++ Modules/_posixsubprocess.c |
5 |
@@ -21,6 +21,8 @@ |
6 |
#include <dirent.h> |
7 |
#endif |
8 |
|
9 |
+#include "posixmodule.h" |
10 |
+ |
11 |
#ifdef _Py_MEMORY_SANITIZER |
12 |
# include <sanitizer/msan_interface.h> |
13 |
#endif |
14 |
@@ -222,7 +222,6 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) |
15 |
long end_fd = safe_get_max_fd(); |
16 |
Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE(py_fds_to_keep); |
17 |
Py_ssize_t keep_seq_idx; |
18 |
- int fd_num; |
19 |
/* As py_fds_to_keep is sorted we can loop through the list closing |
20 |
* fds inbetween any in the keep list falling within our range. */ |
21 |
for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) { |
22 |
@@ -230,15 +229,11 @@ _close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep) |
23 |
int keep_fd = PyLong_AsLong(py_keep_fd); |
24 |
if (keep_fd < start_fd) |
25 |
continue; |
26 |
- for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) { |
27 |
- close(fd_num); |
28 |
- } |
29 |
+ _Py_closerange(start_fd, keep_fd - 1); |
30 |
start_fd = keep_fd + 1; |
31 |
} |
32 |
if (start_fd <= end_fd) { |
33 |
- for (fd_num = start_fd; fd_num < end_fd; ++fd_num) { |
34 |
- close(fd_num); |
35 |
- } |
36 |
+ _Py_closerange(start_fd, end_fd); |
37 |
} |
38 |
} |
39 |
|
40 |
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c |
41 |
index 776a3d249a..f46df9ddf7 100644 |
42 |
--- Modules/posixmodule.c |
43 |
+++ Modules/posixmodule.c |
44 |
@@ -7667,6 +7667,78 @@ os_close_impl(PyObject *module, int fd) |
45 |
Py_RETURN_NONE; |
46 |
} |
47 |
|
48 |
+/* Our selection logic for which function to use is as follows: |
49 |
+ * 1. If close_range(2) is available, always prefer that; it's better for |
50 |
+ * contiguous ranges like this than fdwalk(3) which entails iterating over |
51 |
+ * the entire fd space and simply doing nothing for those outside the range. |
52 |
+ * 2. If closefrom(2) is available, we'll attempt to use that next if we're |
53 |
+ * closing up to sysconf(_SC_OPEN_MAX). |
54 |
+ * 2a. Fallback to fdwalk(3) if we're not closing up to sysconf(_SC_OPEN_MAX), |
55 |
+ * as that will be more performant if the range happens to have any chunk of |
56 |
+ * non-opened fd in the middle. |
57 |
+ * 2b. If fdwalk(3) isn't available, just do a plain close(2) loop. |
58 |
+ */ |
59 |
+#ifdef __FreeBSD__ |
60 |
+#define USE_CLOSEFROM |
61 |
+#endif /* __FreeBSD__ */ |
62 |
+ |
63 |
+#ifdef HAVE_FDWALK |
64 |
+#define USE_FDWALK |
65 |
+#endif /* HAVE_FDWALK */ |
66 |
+ |
67 |
+#ifdef USE_FDWALK |
68 |
+static int |
69 |
+_fdwalk_close_func(void *lohi, int fd) |
70 |
+{ |
71 |
+ int lo = ((int *)lohi)[0]; |
72 |
+ int hi = ((int *)lohi)[1]; |
73 |
+ |
74 |
+ if (fd >= hi) |
75 |
+ return 1; |
76 |
+ else if (fd >= lo) |
77 |
+ close(fd); |
78 |
+ return 0; |
79 |
+} |
80 |
+#endif /* USE_FDWALK */ |
81 |
+ |
82 |
+/* Closes all file descriptors in [first, last], ignoring errors. */ |
83 |
+void |
84 |
+_Py_closerange(int first, int last) |
85 |
+{ |
86 |
+ first = Py_MAX(first, 0); |
87 |
+ _Py_BEGIN_SUPPRESS_IPH |
88 |
+#ifdef HAVE_CLOSE_RANGE |
89 |
+ if (close_range(first, last, 0) == 0 || errno != ENOSYS) { |
90 |
+ /* Any errors encountered while closing file descriptors are ignored; |
91 |
+ * ENOSYS means no kernel support, though, |
92 |
+ * so we'll fallback to the other methods. */ |
93 |
+ } |
94 |
+ else |
95 |
+#endif /* HAVE_CLOSE_RANGE */ |
96 |
+#ifdef USE_CLOSEFROM |
97 |
+ if (last >= sysconf(_SC_OPEN_MAX)) { |
98 |
+ /* Any errors encountered while closing file descriptors are ignored */ |
99 |
+ closefrom(first); |
100 |
+ } |
101 |
+ else |
102 |
+#endif /* USE_CLOSEFROM */ |
103 |
+#ifdef USE_FDWALK |
104 |
+ { |
105 |
+ int lohi[2]; |
106 |
+ lohi[0] = first; |
107 |
+ lohi[1] = last + 1; |
108 |
+ fdwalk(_fdwalk_close_func, lohi); |
109 |
+ } |
110 |
+#else |
111 |
+ { |
112 |
+ for (int i = first; i <= last; i++) { |
113 |
+ /* Ignore errors */ |
114 |
+ (void)close(i); |
115 |
+ } |
116 |
+ } |
117 |
+#endif /* USE_FDWALK */ |
118 |
+ _Py_END_SUPPRESS_IPH |
119 |
+} |
120 |
|
121 |
/*[clinic input] |
122 |
os.closerange |
123 |
@@ -7682,12 +7754,8 @@ static PyObject * |
124 |
os_closerange_impl(PyObject *module, int fd_low, int fd_high) |
125 |
/*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ |
126 |
{ |
127 |
- int i; |
128 |
Py_BEGIN_ALLOW_THREADS |
129 |
- _Py_BEGIN_SUPPRESS_IPH |
130 |
- for (i = Py_MAX(fd_low, 0); i < fd_high; i++) |
131 |
- close(i); |
132 |
- _Py_END_SUPPRESS_IPH |
133 |
+ _Py_closerange(fd_low, fd_high - 1); |
134 |
Py_END_ALLOW_THREADS |
135 |
Py_RETURN_NONE; |
136 |
} |
137 |
diff --git a/Modules/posixmodule.h b/Modules/posixmodule.h |
138 |
index 1ec1833825..be2308ea86 100644 |
139 |
--- Modules/posixmodule.h |
140 |
+++ Modules/posixmodule.h |
141 |
@@ -19,6 +19,8 @@ PyAPI_FUNC(int) _Py_Gid_Converter(PyObject *, void *); |
142 |
#endif /* MS_WINDOWS */ |
143 |
#endif |
144 |
|
145 |
+PyAPI_FUNC(void) _Py_closerange(int first, int last); |
146 |
+ |
147 |
#ifdef __cplusplus |
148 |
} |
149 |
#endif |
150 |
diff --git a/configure b/configure |
151 |
index e39c16eee2..1f34b4de74 100755 |
152 |
--- configure |
153 |
+++ configure |
154 |
@@ -11420,9 +11420,9 @@ fi |
155 |
|
156 |
# checks for library functions |
157 |
for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ |
158 |
- clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \ |
159 |
- fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ |
160 |
- futimens futimes gai_strerror getentropy \ |
161 |
+ clock close_range confstr ctermid dup3 execv faccessat fchmod fchmodat fchown \ |
162 |
+ fchownat fdwalk fexecve fdopendir fork fpathconf fstatat ftime ftruncate \ |
163 |
+ futimesat futimens futimes gai_strerror getentropy \ |
164 |
getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \ |
165 |
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ |
166 |
initgroups kill killpg lchown linkat lstat lutimes mmap \ |
167 |
diff --git a/configure.ac b/configure.ac |
168 |
index cf280506bd..09886eaeef 100644 |
169 |
--- configure.ac |
170 |
+++ configure.ac |
171 |
@@ -3531,9 +3531,9 @@ fi |
172 |
|
173 |
# checks for library functions |
174 |
AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ |
175 |
- clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \ |
176 |
- fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ |
177 |
- futimens futimes gai_strerror getentropy \ |
178 |
+ clock close_range confstr ctermid dup3 execv faccessat fchmod fchmodat fchown \ |
179 |
+ fchownat fdwalk fexecve fdopendir fork fpathconf fstatat ftime ftruncate \ |
180 |
+ futimesat futimens futimes gai_strerror getentropy \ |
181 |
getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \ |
182 |
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ |
183 |
initgroups kill killpg lchown linkat lstat lutimes mmap \ |
184 |
diff --git a/pyconfig.h.in b/pyconfig.h.in |
185 |
index 2fbbb6d20d..a4488cbe32 100644 |
186 |
--- pyconfig.h.in |
187 |
+++ pyconfig.h.in |
188 |
@@ -128,6 +128,9 @@ |
189 |
/* Define to 1 if you have the `clock_settime' function. */ |
190 |
#undef HAVE_CLOCK_SETTIME |
191 |
|
192 |
+/* Define to 1 if you have the `close_range' function. */ |
193 |
+#undef HAVE_CLOSE_RANGE |
194 |
+ |
195 |
/* Define if the C compiler supports computed gotos. */ |
196 |
#undef HAVE_COMPUTED_GOTOS |
197 |
|
198 |
@@ -317,6 +320,9 @@ |
199 |
/* Define to 1 if you have the `fdopendir' function. */ |
200 |
#undef HAVE_FDOPENDIR |
201 |
|
202 |
+/* Define to 1 if you have the `fdwalk' function. */ |
203 |
+#undef HAVE_FDWALK |
204 |
+ |
205 |
/* Define to 1 if you have the `fexecve' function. */ |
206 |
#undef HAVE_FEXECVE |
207 |
|