Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use closefrom and remove our fds max limit. #4872

Merged
merged 3 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ CCAN_OBJS := \
ccan-bitmap.o \
ccan-bitops.o \
ccan-breakpoint.o \
ccan-closefrom.o \
ccan-crc32c.o \
ccan-crypto-hmac.o \
ccan-crypto-hkdf.o \
Expand Down Expand Up @@ -153,6 +154,7 @@ CCAN_HEADERS := \
$(CCANDIR)/ccan/cast/cast.h \
$(CCANDIR)/ccan/cdump/cdump.h \
$(CCANDIR)/ccan/check_type/check_type.h \
$(CCANDIR)/ccan/closefrom/closefrom.h \
$(CCANDIR)/ccan/compiler/compiler.h \
$(CCANDIR)/ccan/container_of/container_of.h \
$(CCANDIR)/ccan/cppmagic/cppmagic.h \
Expand Down Expand Up @@ -855,3 +857,5 @@ ccan-json_escape.o: $(CCANDIR)/ccan/json_escape/json_escape.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
ccan-closefrom.o: $(CCANDIR)/ccan/closefrom/closefrom.c
@$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<)
2 changes: 1 addition & 1 deletion ccan/README
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
CCAN imported from http://ccodearchive.net.

CCAN version: init-2507-g05ec8351
CCAN version: init-2519-gcc888f28
1 change: 1 addition & 0 deletions ccan/ccan/closefrom/LICENSE
69 changes: 69 additions & 0 deletions ccan/ccan/closefrom/_info
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "config.h"
#include <stdio.h>
#include <string.h>

/**
* closefrom - close all fds starting from specified fd.
*
* This code is an example of what to do in a child process to
* ensure that none of the (possibly sensitive) file descriptors
* in the parent remain in the child process.
*
* License: CC0 (Public domain)
* Author: ZmnSCPxj jxPCSnmZ <[email protected]>
*
* Example:
* #include <ccan/closefrom/closefrom.h>
* #include <ccan/err/err.h>
* #include <stdio.h>
* #include <sys/resource.h>
* #include <sys/time.h>
* #include <sys/types.h>
* #include <sys/wait.h>
* #include <unistd.h>
*
* int main(int argc, char **argv)
* {
* pid_t child;
*
* // If being emulated, then we might end up
* // looping over a large _SC_OPEN_MAX
* // (Some systems have it as INT_MAX!)
* // If so, closefrom_limit will lower this limit
* // to a value you specify, or if given 0 will
* // limit to 4096.
* // Call this as early as possible.
* closefrom_limit(0);
*
* // If we limited, we can query this so we can
* // print it in debug logs or something.
* if (closefrom_may_be_slow())
* printf("we limited ourselves to 4096 fds.\n");
*
* child = fork();
* if (child < 0)
* err(1, "Forking");
* if (child == 0) {
* closefrom(STDERR_FILENO + 1);
* // Insert your *whatever* code here.
* _exit(0);
* }
*
* waitpid(child, NULL, 0);
*
* return 0;
* }
*
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;

if (strcmp(argv[1], "depends") == 0) {
return 0;
}

return 1;
}
225 changes: 225 additions & 0 deletions ccan/ccan/closefrom/closefrom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/* CC0 license (public domain) - see LICENSE file for details */
#include <ccan/closefrom/closefrom.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>

/* See also:
* https://stackoverflow.com/a/918469
*
* The implementation below is not exhaustive of all the suggested above.
*/

#if !HAVE_CLOSEFROM

/* IBM AIX.
* https://www.ibm.com/docs/en/aix/7.2?topic=f-fcntl-dup-dup2-subroutine
*/
#if HAVE_F_CLOSEM

#include <fcntl.h>

void closefrom(int fromfd)
{
(void) fcntl(fromfd, F_CLOSEM, 0);
}

bool closefrom_may_be_slow(void)
{
return false;
}

#else /* !HAVE_F_CLOSEM */

#if HAVE_NR_CLOSE_RANGE
#include <sys/syscall.h>
#endif

#define PROC_PID_FD_LEN \
( 6 /* /proc/ */ \
+ 20 /* 64-bit $PID */ \
+ 3 /* /fd */ \
+ 1 /* NUL */ \
)

static bool can_get_maxfd(void)
{
#if HAVE_F_MAXFD
int res = fcntl(0, F_MAXFD);
if (res < 0)
return false;
else
return true;
#else
return false;
#endif
}

/* Linux >= 5.9 */
static bool can_close_range(void)
{
#if HAVE_NR_CLOSE_RANGE
int res = syscall(__NR_close_range, INT_MAX, INT_MAX, 0);
if (res < 0)
return false;
return true;
#else
return false;
#endif
}

/* On Linux, Solaris, AIX, Cygwin, and NetBSD. */
static bool can_open_proc_pid_fd(void)
{
char dnam[PROC_PID_FD_LEN];
DIR *dir;

sprintf(dnam, "/proc/%ld/fd", (long) getpid());
dir = opendir(dnam);
if (!dir)
return false;
closedir(dir);
return true;
}

/* On FreeBSD and MacOS. */
static bool can_open_dev_fd(void)
{
DIR *dir;
dir = opendir("/dev/fd");
if (!dir)
return false;
closedir(dir);
return true;
}

bool closefrom_may_be_slow(void)
{
if (can_get_maxfd())
return false;
else if (can_close_range())
return false;
else if (can_open_proc_pid_fd())
return false;
else if (can_open_dev_fd())
return false;
else
return true;
}

/* It is possible that we run out of available file descriptors.
* However, if we are going to close anyway, we could just try
* closing file descriptors until we reach maxfd.
*/
static
DIR *try_opendir(const char *dnam, int *fromfd, int maxfd)
{
DIR *dir;

do {
dir = opendir(dnam);
if (!dir && (errno == ENFILE || errno == EMFILE)) {
if (*fromfd < maxfd)
close((*fromfd)++);
else
break;
}
} while (!dir && (errno == ENFILE || errno == EMFILE));

return dir;
}

void closefrom(int fromfd)
{
int saved_errno = errno;

int res;
int maxfd;

char dnam[PROC_PID_FD_LEN];
DIR *dir;
struct dirent *entry;

(void) res;

if (fromfd < 0)
goto quit;

#if HAVE_NR_CLOSE_RANGE
res = syscall(__NR_close_range, fromfd, INT_MAX, 0);
if (res == 0)
goto quit;
#endif

maxfd = sysconf(_SC_OPEN_MAX);

sprintf(dnam, "/proc/%ld/fd", (long) getpid());
dir = try_opendir(dnam, &fromfd, maxfd);
if (!dir)
dir = try_opendir("/dev/fd", &fromfd, maxfd);

if (dir) {
while ((entry = readdir(dir))) {
long fd;
char *endp;

fd = strtol(entry->d_name, &endp, 10);
if (entry->d_name != endp && *endp == '\0' &&
fd >= 0 && fd < INT_MAX && fd >= fromfd &&
fd != dirfd(dir) )
close(fd);
}
closedir(dir);
goto quit;
}

#if HAVE_F_MAXFD
res = fcntl(0, F_MAXFD);
if (res >= 0)
maxfd = res + 1;
#endif

/* Fallback. */
for (; fromfd < maxfd; ++fromfd)
close(fromfd);

quit:
errno = saved_errno;
}

#endif /* !HAVE_F_CLOSEM */

void closefrom_limit(unsigned int arg_limit)
{
rlim_t limit = (rlim_t) arg_limit;

struct rlimit nofile;

if (!closefrom_may_be_slow())
return;

if (limit == 0)
limit = 4096;

getrlimit(RLIMIT_NOFILE, &nofile);

/* Respect the max limit.
* If we are not running as root then we cannot raise
* it, but we *can* lower the max limit.
*/
if (nofile.rlim_max != RLIM_INFINITY && limit > nofile.rlim_max)
limit = nofile.rlim_max;

nofile.rlim_cur = limit;
nofile.rlim_max = limit;

setrlimit(RLIMIT_NOFILE, &nofile);
}

#endif /* !HAVE_CLOSEFROM */
Loading