linux_dep: Fix CLOSE_RANGE_UNSHARE availability handling

If CLOSE_RANGE_UNSHARE isn't defined, we define a fallback version of
close_range() which is a (successful) no-op.  This is broken in several
ways:
 * It doesn't actually fix compile if using old kernel headers, because
   the caller of close_range() still directly uses CLOSE_RANGE_UNSHARE
   unprotected by ifdefs
 * Even if it did fix the compile, it means inconsistent behaviour between
   a compile time failure to find the value (we silently don't close files)
   and a runtime failure (we die with an error from close_range())
 * Silently not closing the files we intend to close for security reasons
   is probably not a good idea in any case

We don't want to simply error if close_range() or CLOSE_RANGE_UNSHARE isn't
available, because that would require running on kernel >= 5.9.  On the
other hand there's not really any other way to flush all possible fds
leaked by the parent (close() in a loop takes over a minute).  So in this
case print a warning and carry on.

As bonus this fixes a cppcheck error I see with some different options I'm
looking to apply in future.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2024-11-08 13:53:29 +11:00 committed by Stefano Brivio
parent d64f257243
commit 14dd70e2b3
2 changed files with 18 additions and 10 deletions

View file

@ -127,22 +127,18 @@ struct tcp_info_linux {
#include <linux/close_range.h> #include <linux/close_range.h>
#ifdef CLOSE_RANGE_UNSHARE /* Linux kernel >= 5.9 */
/* glibc < 2.34 and musl as of 1.2.5 need these */ /* glibc < 2.34 and musl as of 1.2.5 need these */
#ifndef SYS_close_range #ifndef SYS_close_range
#define SYS_close_range 436 #define SYS_close_range 436
#endif #endif
#ifndef CLOSE_RANGE_UNSHARE /* Linux kernel < 5.9 */
#define CLOSE_RANGE_UNSHARE (1U << 1)
#endif
__attribute__ ((weak)) __attribute__ ((weak))
/* cppcheck-suppress funcArgNamesDifferent */ /* cppcheck-suppress funcArgNamesDifferent */
int close_range(unsigned int first, unsigned int last, int flags) { int close_range(unsigned int first, unsigned int last, int flags) {
return syscall(SYS_close_range, first, last, flags); return syscall(SYS_close_range, first, last, flags);
} }
#else
/* No reasonable fallback option */
/* cppcheck-suppress funcArgNamesDifferent */
int close_range(unsigned int first, unsigned int last, int flags) {
return 0;
}
#endif
#endif /* LINUX_DEP_H */ #endif /* LINUX_DEP_H */

14
util.c
View file

@ -738,9 +738,21 @@ void close_open_files(int argc, char **argv)
rc = close_range(fd + 1, ~0U, CLOSE_RANGE_UNSHARE); rc = close_range(fd + 1, ~0U, CLOSE_RANGE_UNSHARE);
} }
if (rc) if (rc) {
if (errno == ENOSYS || errno == EINVAL) {
/* This probably means close_range() or the
* CLOSE_RANGE_UNSHARE flag is not supported by the
* kernel. Not much we can do here except carry on and
* hope for the best.
*/
warn(
"Can't use close_range() to ensure no files leaked by parent");
} else {
die_perror("Failed to close files leaked by parent"); die_perror("Failed to close files leaked by parent");
} }
}
}
/** /**
* snprintf_check() - snprintf() wrapper, checking for truncation and errors * snprintf_check() - snprintf() wrapper, checking for truncation and errors