Skip to content

Commit 5a84abb

Browse files
ZmnSCPxjcdecker
authored andcommitted
ccan: update to include closefrom
Signed-off-by: ZmnSCPxj jxPCSnmZ <[email protected]> Signed-off-by: Rusty Russell <[email protected]>
1 parent ed6eaf9 commit 5a84abb

File tree

14 files changed

+665
-27
lines changed

14 files changed

+665
-27
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ CCAN_OBJS := \
9696
ccan-bitmap.o \
9797
ccan-bitops.o \
9898
ccan-breakpoint.o \
99+
ccan-closefrom.o \
99100
ccan-crc32c.o \
100101
ccan-crypto-hmac.o \
101102
ccan-crypto-hkdf.o \
@@ -154,6 +155,7 @@ CCAN_HEADERS := \
154155
$(CCANDIR)/ccan/cast/cast.h \
155156
$(CCANDIR)/ccan/cdump/cdump.h \
156157
$(CCANDIR)/ccan/check_type/check_type.h \
158+
$(CCANDIR)/ccan/closefrom/closefrom.h \
157159
$(CCANDIR)/ccan/compiler/compiler.h \
158160
$(CCANDIR)/ccan/container_of/container_of.h \
159161
$(CCANDIR)/ccan/cppmagic/cppmagic.h \
@@ -857,3 +859,5 @@ ccan-json_escape.o: $(CCANDIR)/ccan/json_escape/json_escape.c
857859
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
858860
ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c
859861
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
862+
ccan-closefrom.o: $(CCANDIR)/ccan/closefrom/closefrom.c
863+
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)

ccan/README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
CCAN imported from http://ccodearchive.net.
22

3-
CCAN version: init-2507-g05ec8351
3+
CCAN version: init-2519-gcc888f28

ccan/ccan/closefrom/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../licenses/CC0

ccan/ccan/closefrom/_info

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "config.h"
2+
#include <stdio.h>
3+
#include <string.h>
4+
5+
/**
6+
* closefrom - close all fds starting from specified fd.
7+
*
8+
* This code is an example of what to do in a child process to
9+
* ensure that none of the (possibly sensitive) file descriptors
10+
* in the parent remain in the child process.
11+
*
12+
* License: CC0 (Public domain)
13+
* Author: ZmnSCPxj jxPCSnmZ <[email protected]>
14+
*
15+
* Example:
16+
* #include <ccan/closefrom/closefrom.h>
17+
* #include <ccan/err/err.h>
18+
* #include <stdio.h>
19+
* #include <sys/resource.h>
20+
* #include <sys/time.h>
21+
* #include <sys/types.h>
22+
* #include <sys/wait.h>
23+
* #include <unistd.h>
24+
*
25+
* int main(int argc, char **argv)
26+
* {
27+
* pid_t child;
28+
*
29+
* // If being emulated, then we might end up
30+
* // looping over a large _SC_OPEN_MAX
31+
* // (Some systems have it as INT_MAX!)
32+
* // If so, closefrom_limit will lower this limit
33+
* // to a value you specify, or if given 0 will
34+
* // limit to 4096.
35+
* // Call this as early as possible.
36+
* closefrom_limit(0);
37+
*
38+
* // If we limited, we can query this so we can
39+
* // print it in debug logs or something.
40+
* if (closefrom_may_be_slow())
41+
* printf("we limited ourselves to 4096 fds.\n");
42+
*
43+
* child = fork();
44+
* if (child < 0)
45+
* err(1, "Forking");
46+
* if (child == 0) {
47+
* closefrom(STDERR_FILENO + 1);
48+
* // Insert your *whatever* code here.
49+
* _exit(0);
50+
* }
51+
*
52+
* waitpid(child, NULL, 0);
53+
*
54+
* return 0;
55+
* }
56+
*
57+
*/
58+
int main(int argc, char *argv[])
59+
{
60+
/* Expect exactly one argument */
61+
if (argc != 2)
62+
return 1;
63+
64+
if (strcmp(argv[1], "depends") == 0) {
65+
return 0;
66+
}
67+
68+
return 1;
69+
}

ccan/ccan/closefrom/closefrom.c

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/* CC0 license (public domain) - see LICENSE file for details */
2+
#include <ccan/closefrom/closefrom.h>
3+
#include <dirent.h>
4+
#include <errno.h>
5+
#include <limits.h>
6+
#include <stdlib.h>
7+
#include <stdio.h>
8+
#include <sys/time.h>
9+
#include <sys/resource.h>
10+
#include <sys/types.h>
11+
#include <unistd.h>
12+
13+
/* See also:
14+
* https://stackoverflow.com/a/918469
15+
*
16+
* The implementation below is not exhaustive of all the suggested above.
17+
*/
18+
19+
#if !HAVE_CLOSEFROM
20+
21+
/* IBM AIX.
22+
* https://www.ibm.com/docs/en/aix/7.2?topic=f-fcntl-dup-dup2-subroutine
23+
*/
24+
#if HAVE_F_CLOSEM
25+
26+
#include <fcntl.h>
27+
28+
void closefrom(int fromfd)
29+
{
30+
(void) fcntl(fromfd, F_CLOSEM, 0);
31+
}
32+
33+
bool closefrom_may_be_slow(void)
34+
{
35+
return false;
36+
}
37+
38+
#else /* !HAVE_F_CLOSEM */
39+
40+
#if HAVE_NR_CLOSE_RANGE
41+
#include <sys/syscall.h>
42+
#endif
43+
44+
#define PROC_PID_FD_LEN \
45+
( 6 /* /proc/ */ \
46+
+ 20 /* 64-bit $PID */ \
47+
+ 3 /* /fd */ \
48+
+ 1 /* NUL */ \
49+
)
50+
51+
static bool can_get_maxfd(void)
52+
{
53+
#if HAVE_F_MAXFD
54+
int res = fcntl(0, F_MAXFD);
55+
if (res < 0)
56+
return false;
57+
else
58+
return true;
59+
#else
60+
return false;
61+
#endif
62+
}
63+
64+
/* Linux >= 5.9 */
65+
static bool can_close_range(void)
66+
{
67+
#if HAVE_NR_CLOSE_RANGE
68+
int res = syscall(__NR_close_range, INT_MAX, INT_MAX, 0);
69+
if (res < 0)
70+
return false;
71+
return true;
72+
#else
73+
return false;
74+
#endif
75+
}
76+
77+
/* On Linux, Solaris, AIX, Cygwin, and NetBSD. */
78+
static bool can_open_proc_pid_fd(void)
79+
{
80+
char dnam[PROC_PID_FD_LEN];
81+
DIR *dir;
82+
83+
sprintf(dnam, "/proc/%ld/fd", (long) getpid());
84+
dir = opendir(dnam);
85+
if (!dir)
86+
return false;
87+
closedir(dir);
88+
return true;
89+
}
90+
91+
/* On FreeBSD and MacOS. */
92+
static bool can_open_dev_fd(void)
93+
{
94+
DIR *dir;
95+
dir = opendir("/dev/fd");
96+
if (!dir)
97+
return false;
98+
closedir(dir);
99+
return true;
100+
}
101+
102+
bool closefrom_may_be_slow(void)
103+
{
104+
if (can_get_maxfd())
105+
return false;
106+
else if (can_close_range())
107+
return false;
108+
else if (can_open_proc_pid_fd())
109+
return false;
110+
else if (can_open_dev_fd())
111+
return false;
112+
else
113+
return true;
114+
}
115+
116+
/* It is possible that we run out of available file descriptors.
117+
* However, if we are going to close anyway, we could just try
118+
* closing file descriptors until we reach maxfd.
119+
*/
120+
static
121+
DIR *try_opendir(const char *dnam, int *fromfd, int maxfd)
122+
{
123+
DIR *dir;
124+
125+
do {
126+
dir = opendir(dnam);
127+
if (!dir && (errno == ENFILE || errno == EMFILE)) {
128+
if (*fromfd < maxfd)
129+
close((*fromfd)++);
130+
else
131+
break;
132+
}
133+
} while (!dir && (errno == ENFILE || errno == EMFILE));
134+
135+
return dir;
136+
}
137+
138+
void closefrom(int fromfd)
139+
{
140+
int saved_errno = errno;
141+
142+
int res;
143+
int maxfd;
144+
145+
char dnam[PROC_PID_FD_LEN];
146+
DIR *dir;
147+
struct dirent *entry;
148+
149+
(void) res;
150+
151+
if (fromfd < 0)
152+
goto quit;
153+
154+
#if HAVE_NR_CLOSE_RANGE
155+
res = syscall(__NR_close_range, fromfd, INT_MAX, 0);
156+
if (res == 0)
157+
goto quit;
158+
#endif
159+
160+
maxfd = sysconf(_SC_OPEN_MAX);
161+
162+
sprintf(dnam, "/proc/%ld/fd", (long) getpid());
163+
dir = try_opendir(dnam, &fromfd, maxfd);
164+
if (!dir)
165+
dir = try_opendir("/dev/fd", &fromfd, maxfd);
166+
167+
if (dir) {
168+
while ((entry = readdir(dir))) {
169+
long fd;
170+
char *endp;
171+
172+
fd = strtol(entry->d_name, &endp, 10);
173+
if (entry->d_name != endp && *endp == '\0' &&
174+
fd >= 0 && fd < INT_MAX && fd >= fromfd &&
175+
fd != dirfd(dir) )
176+
close(fd);
177+
}
178+
closedir(dir);
179+
goto quit;
180+
}
181+
182+
#if HAVE_F_MAXFD
183+
res = fcntl(0, F_MAXFD);
184+
if (res >= 0)
185+
maxfd = res + 1;
186+
#endif
187+
188+
/* Fallback. */
189+
for (; fromfd < maxfd; ++fromfd)
190+
close(fromfd);
191+
192+
quit:
193+
errno = saved_errno;
194+
}
195+
196+
#endif /* !HAVE_F_CLOSEM */
197+
198+
void closefrom_limit(unsigned int arg_limit)
199+
{
200+
rlim_t limit = (rlim_t) arg_limit;
201+
202+
struct rlimit nofile;
203+
204+
if (!closefrom_may_be_slow())
205+
return;
206+
207+
if (limit == 0)
208+
limit = 4096;
209+
210+
getrlimit(RLIMIT_NOFILE, &nofile);
211+
212+
/* Respect the max limit.
213+
* If we are not running as root then we cannot raise
214+
* it, but we *can* lower the max limit.
215+
*/
216+
if (nofile.rlim_max != RLIM_INFINITY && limit > nofile.rlim_max)
217+
limit = nofile.rlim_max;
218+
219+
nofile.rlim_cur = limit;
220+
nofile.rlim_max = limit;
221+
222+
setrlimit(RLIMIT_NOFILE, &nofile);
223+
}
224+
225+
#endif /* !HAVE_CLOSEFROM */

0 commit comments

Comments
 (0)