From: Érico Rolim Date: Sun, 15 Nov 2020 04:34:41 +0000 (-0300) Subject: lib/caputils: add fall back for last cap using prctl. X-Git-Tag: v2.36.2~45 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=15966a2cc4cb02bded8b54cfff97987f9b29484c;p=thirdparty%2Futil-linux.git lib/caputils: add fall back for last cap using prctl. This allows the rest of the programs using cap_last_cap to trust the value returned by it, since it will either be obtained from procfs (straight from kernel) or with prctl. Also checked if the file under /proc is actually mounted in a procfs. (cherry picked from commit 5d95818757941bc609e5aeec5e2218f7d35a6e19) --- diff --git a/lib/caputils.c b/lib/caputils.c index 17e9c01bb0..669330c50b 100644 --- a/lib/caputils.c +++ b/lib/caputils.c @@ -14,32 +14,59 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include +#include #include "caputils.h" +#include "procutils.h" #include "pathnames.h" +static int test_cap(unsigned int cap) +{ + /* prctl returns 0 or 1 for valid caps, -1 otherwise */ + return prctl(PR_CAPBSET_READ, cap, 0, 0, 0) >= 0; +} + int cap_last_cap(void) { - /* CAP_LAST_CAP is untrustworthy. */ - static int ret = -1; - int matched; + static int cap = -1; FILE *f; - if (ret != -1) - return ret; + if (cap != -1) + return cap; + /* try to read value from kernel, check that the path is + * indeed in a procfs mount */ f = fopen(_PATH_PROC_CAPLASTCAP, "r"); - if (!f) { - ret = CAP_LAST_CAP; /* guess */ - return ret; + if (f) { + int matched = 0; + + if (proc_is_procfs(fileno(f))) { + matched = fscanf(f, "%d", &cap); + } + fclose(f); + + /* we check if the cap after this one really isn't valid */ + if (matched == 1 && cap < INT_MAX && !test_cap(cap + 1)) + return cap; } - matched = fscanf(f, "%d", &ret); - fclose(f); + /* if it wasn't possible to read the file in /proc, + * fall back to binary search over capabilities */ - if (matched != 1) - ret = CAP_LAST_CAP; /* guess */ + /* starting with cap=INT_MAX means we always know + * that cap1 is invalid after the first iteration */ + unsigned int cap0 = 0, cap1 = INT_MAX; + cap = INT_MAX; + while ((int)cap0 < cap) { + if (test_cap(cap)) { + cap0 = cap; + } else { + cap1 = cap; + } + cap = (cap0 + cap1) / 2U; + } - return ret; + return cap; }