fd-util: split out fallback logic for close_all_fds()
No functional change. Just refactoring. With this change, we can test each logic directly without seccomp or hiding procfs.
This commit is contained in:
+83
-79
@@ -248,7 +248,7 @@ int get_max_fd(void) {
|
||||
return (int) (m - 1);
|
||||
}
|
||||
|
||||
static int close_all_fds_frugal(const int except[], size_t n_except) {
|
||||
int close_all_fds_frugal(const int except[], size_t n_except) {
|
||||
int max_fd, r = 0;
|
||||
|
||||
assert(except || n_except == 0);
|
||||
@@ -282,6 +282,42 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int close_all_fds_by_proc(const int except[], size_t n_except) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r = 0;
|
||||
|
||||
d = opendir("/proc/self/fd");
|
||||
if (!d)
|
||||
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
int fd = -EBADF, q;
|
||||
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
|
||||
continue;
|
||||
|
||||
fd = parse_fd(de->d_name);
|
||||
if (fd < 0)
|
||||
/* Let's better ignore this, just in case */
|
||||
continue;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if (fd_in_set(fd, except, n_except))
|
||||
continue;
|
||||
|
||||
q = close_nointr(fd);
|
||||
if (q != -EBADF) /* Valgrind has its own FD and doesn't want to have it closed */
|
||||
RET_GATHER(r, q);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool have_close_range = true; /* Assume we live in the future */
|
||||
|
||||
static int close_all_fds_special_case(const int except[], size_t n_except) {
|
||||
@@ -348,8 +384,7 @@ int close_all_fds_without_malloc(const int except[], size_t n_except) {
|
||||
}
|
||||
|
||||
int close_all_fds(const int except[], size_t n_except) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
assert(n_except == 0 || except);
|
||||
|
||||
@@ -359,104 +394,73 @@ int close_all_fds(const int except[], size_t n_except) {
|
||||
if (r > 0) /* special case worked! */
|
||||
return 0;
|
||||
|
||||
if (have_close_range) {
|
||||
_cleanup_free_ int *sorted_malloc = NULL;
|
||||
size_t n_sorted;
|
||||
int *sorted;
|
||||
if (!have_close_range)
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
|
||||
/* In the best case we have close_range() to close all fds between a start and an end fd,
|
||||
* which we can use on the "inverted" exception array, i.e. all intervals between all
|
||||
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
|
||||
* where n is number of open fds to O(m⋅log(m)) where m is the number of fds to keep
|
||||
* open. Given that we assume n ≫ m that's preferable to us. */
|
||||
_cleanup_free_ int *sorted_malloc = NULL;
|
||||
size_t n_sorted;
|
||||
int *sorted;
|
||||
|
||||
assert(n_except < SIZE_MAX);
|
||||
n_sorted = n_except + 1;
|
||||
/* In the best case we have close_range() to close all fds between a start and an end fd, which we
|
||||
* can use on the "inverted" exception array, i.e. all intervals between all adjacent pairs from the
|
||||
* sorted exception array. This changes loop complexity from O(n) where n is number of open fds to
|
||||
* O(m⋅log(m)) where m is the number of fds to keep open. Given that we assume n ≫ m that's
|
||||
* preferable to us. */
|
||||
|
||||
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
|
||||
sorted = sorted_malloc = new(int, n_sorted);
|
||||
else
|
||||
sorted = newa(int, n_sorted);
|
||||
assert(n_except < SIZE_MAX);
|
||||
n_sorted = n_except + 1;
|
||||
|
||||
if (sorted) {
|
||||
memcpy(sorted, except, n_except * sizeof(int));
|
||||
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
|
||||
sorted = sorted_malloc = new(int, n_sorted);
|
||||
else
|
||||
sorted = newa(int, n_sorted);
|
||||
|
||||
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
|
||||
* allows us to cover the head of the array the same way as the body */
|
||||
sorted[n_sorted-1] = 2;
|
||||
if (!sorted) /* Fallback on OOM. */
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
|
||||
typesafe_qsort(sorted, n_sorted, cmp_int);
|
||||
memcpy(sorted, except, n_except * sizeof(int));
|
||||
|
||||
for (size_t i = 0; i < n_sorted-1; i++) {
|
||||
int start, end;
|
||||
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
|
||||
* allows us to cover the head of the array the same way as the body */
|
||||
sorted[n_sorted-1] = 2;
|
||||
|
||||
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
|
||||
end = MAX(sorted[i+1], 2);
|
||||
typesafe_qsort(sorted, n_sorted, cmp_int);
|
||||
|
||||
assert(end >= start);
|
||||
for (size_t i = 0; i < n_sorted-1; i++) {
|
||||
int start, end;
|
||||
|
||||
if (end - start <= 1)
|
||||
continue;
|
||||
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
|
||||
end = MAX(sorted[i+1], 2);
|
||||
|
||||
/* Close everything between the start and end fds (both of which shall stay open) */
|
||||
if (close_range(start + 1, end - 1, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
assert(end >= start);
|
||||
|
||||
have_close_range = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end - start <= 1)
|
||||
continue;
|
||||
|
||||
if (have_close_range) {
|
||||
/* The loop succeeded. Let's now close everything beyond the end */
|
||||
/* Close everything between the start and end fds (both of which shall stay open) */
|
||||
if (close_range(start + 1, end - 1, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
|
||||
return 0;
|
||||
|
||||
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) >= 0)
|
||||
return 0;
|
||||
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
have_close_range = false;
|
||||
}
|
||||
have_close_range = false;
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
}
|
||||
|
||||
/* Fallback on OOM or if close_range() is not supported */
|
||||
}
|
||||
|
||||
d = opendir("/proc/self/fd");
|
||||
if (!d)
|
||||
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
|
||||
/* The loop succeeded. Let's now close everything beyond the end */
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
int fd = -EBADF, q;
|
||||
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
|
||||
return 0;
|
||||
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
|
||||
continue;
|
||||
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
fd = parse_fd(de->d_name);
|
||||
if (fd < 0)
|
||||
/* Let's better ignore this, just in case */
|
||||
continue;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if (fd_in_set(fd, except, n_except))
|
||||
continue;
|
||||
|
||||
q = close_nointr(fd);
|
||||
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
|
||||
r = q;
|
||||
have_close_range = false;
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
}
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pack_fds(int fds[], size_t n_fds) {
|
||||
|
||||
@@ -112,6 +112,8 @@ int get_max_fd(void);
|
||||
|
||||
int close_all_fds(const int except[], size_t n_except);
|
||||
int close_all_fds_without_malloc(const int except[], size_t n_except);
|
||||
int close_all_fds_by_proc(const int except[], size_t n_except);
|
||||
int close_all_fds_frugal(const int except[], size_t n_except);
|
||||
|
||||
int pack_fds(int fds[], size_t n);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user