Skip to content

Commit

Permalink
ccan: update to include closefrom
Browse files Browse the repository at this point in the history
Signed-off-by: ZmnSCPxj jxPCSnmZ <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
ZmnSCPxj authored and cdecker committed Oct 22, 2021
1 parent ed6eaf9 commit 5a84abb
Show file tree
Hide file tree
Showing 14 changed files with 665 additions and 27 deletions.
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 @@ -154,6 +155,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 @@ -857,3 +859,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

0 comments on commit 5a84abb

Please sign in to comment.