diff -ru a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,9 @@ AC_CHECK_HEADERS(pthread.h, [AC_SEARCH_LIBS(pthread_atfork, pthread)], [AC_MSG_WARN(pthread.h not found, disabling pthread_atfork.)]) +AC_CHECK_HEADERS(sys/vfs.h, [ + AC_CHECK_HEADERS(linux/magic.h, [] [AC_MSG_WARN(linux/magic.h is required in order to verify procfs.)]) + ], [AC_MSG_WARN(sys/vfs.h is required in order to verify procfs.)]) AC_C_CONST AC_C_INLINE diff -ru a/src/cap-ng.c b/src/cap-ng.c --- a/src/cap-ng.c +++ b/src/cap-ng.c @@ -44,6 +44,10 @@ #ifdef HAVE_LINUX_SECUREBITS_H #include #endif +#ifdef HAVE_LINUX_MAGIC_H +#include +#include +#endif # define hidden __attribute__ ((visibility ("hidden"))) unsigned int last_cap hidden = 0; @@ -168,6 +172,15 @@ m.state = CAPNG_NEW; } +static inline int test_cap(unsigned int cap) +{ + // prctl returns 0 or 1 for valid caps, -1 otherwise + return prctl(PR_CAPBSET_READ, cap) >= 0; +} + +// The maximum cap value is determined by VFS_CAP_U32 +#define MAX_CAP_VALUE (VFS_CAP_U32 * sizeof(__le32) * 8) + static void init_lib(void) __attribute__ ((constructor)); static void init_lib(void) { @@ -178,8 +191,15 @@ if (last_cap == 0) { int fd; + // Try to read last cap from procfs fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY); if (fd >= 0) { +#ifdef HAVE_LINUX_MAGIC_H + struct statfs st; + // Bail out if procfs is invalid or fstatfs fails + if (fstatfs(fd, &st) || st.f_type != PROC_SUPER_MAGIC) + goto fail; +#endif char buf[8]; int num = read(fd, buf, sizeof(buf) - 1); if (num > 0) { @@ -189,10 +209,25 @@ if (errno == 0) last_cap = val; } +fail: close(fd); } - if (last_cap == 0) - last_cap = CAP_LAST_CAP; + // Run a binary search over capabilities + if (last_cap == 0) { + // starting with last_cap=MAX_CAP_VALUE means we always know + // that cap1 is invalid after the first iteration + last_cap = MAX_CAP_VALUE; + unsigned int cap0 = 0, cap1 = MAX_CAP_VALUE; + + while (cap0 < last_cap) { + if (test_cap(last_cap)) + cap0 = last_cap; + else + cap1 = last_cap; + + last_cap = (cap0 + cap1) / 2U; + } + } } }