diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2021-08-21 10:29:16 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2021-08-21 10:29:16 -0700 |
commit | f81144578ff24a70356faafb82e55de8f3e1292f (patch) | |
tree | 2f96cb311fe2116b902d23913287f1fe328c0124 | |
parent | 86c85c07c83f7ddc722b872ea0ff9e9b0f70bbc8 (diff) | |
download | libcap-f81144578ff24a70356faafb82e55de8f3e1292f.tar.gz |
Handle libcap allocation failures more explicitly and fix a memory leak.
This started out as a refactoring of a patch provided by Samanta Navarro.
Reworked, I noticed a latent memory leak in cap_iab_get_proc(), so I've
fixed that too.
Also, migrated a compile failure check to a more useful cap_test for a
highly unlikely corner case (future proofing). While there, noticed
and fixed the binary search test and code (not sure what it was testing
before).
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r-- | libcap/cap_proc.c | 20 | ||||
-rw-r--r-- | libcap/cap_test.c | 37 | ||||
-rw-r--r-- | libcap/cap_text.c | 29 | ||||
-rw-r--r-- | libcap/libcap.h | 2 |
4 files changed, 66 insertions, 22 deletions
diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c index e12c8e6..5e3f9f9 100644 --- a/libcap/cap_proc.c +++ b/libcap/cap_proc.c @@ -684,9 +684,25 @@ int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]) */ cap_iab_t cap_iab_get_proc(void) { - cap_iab_t iab = cap_iab_init(); - cap_t current = cap_get_proc(); + cap_iab_t iab; + cap_t current; + + iab = cap_iab_init(); + if (iab == NULL) { + _cap_debug("no memory for IAB tuple"); + return NULL; + } + + current = cap_get_proc(); + if (current == NULL) { + _cap_debug("no memory for cap_t"); + cap_free(iab); + return NULL; + } + cap_iab_fill(iab, CAP_IAB_INH, current, CAP_INHERITABLE); + cap_free(current); + cap_value_t c; for (c = cap_max_bits(); c; ) { --c; diff --git a/libcap/cap_test.c b/libcap/cap_test.c index a717217..e8eb647 100644 --- a/libcap/cap_test.c +++ b/libcap/cap_test.c @@ -1,3 +1,6 @@ +#define _GNU_SOURCE +#include <stdio.h> + #include "libcap.h" static cap_value_t top; @@ -15,15 +18,18 @@ static int test_cap_bits(void) { for (i = 0; vs[i] >= 0; i++) { cap_value_t ans; - top = i; - _binary_search(ans, cf, 0, __CAP_MAXBITS, 0); + top = vs[i]; + _binary_search(ans, cf, 0, __CAP_MAXBITS, -1); if (ans != top) { - if (top > __CAP_MAXBITS && ans == __CAP_MAXBITS) { - } else { - printf("test_cap_bits miscompared [%d] top=%d - got=%d\n", - i, top, ans); - failed = -1; + if (top == 0 && ans == -1) { + continue; + } + if (top > __CAP_MAXBITS && ans == -1) { + continue; } + printf("test_cap_bits miscompared [%d] top=%d - got=%d\n", + i, top, ans); + failed = -1; } } return failed; @@ -68,11 +74,28 @@ static int test_cap_flags(void) { return 0; } +static int test_short_bits(void) { + int result = 0; + char *tmp; + int n = asprintf(&tmp, "%d", __CAP_MAXBITS); + if (n <= 0) { + return -1; + } + if (strlen(tmp) > __CAP_NAME_SIZE) { + printf("cap_to_text buffer size reservation needs fixing (%ld > %d)\n", + strlen(tmp), __CAP_NAME_SIZE); + result = -1; + } + free(tmp); + return result; +} + int main(int argc, char **argv) { int result = 0; result = test_cap_bits() | result; result = test_cap_flags() | result; + result = test_short_bits() | result; if (result) { printf("cap_test FAILED\n"); diff --git a/libcap/cap_text.c b/libcap/cap_text.c index ff3699d..25c5ef5 100644 --- a/libcap/cap_text.c +++ b/libcap/cap_text.c @@ -219,7 +219,7 @@ cap_t cap_from_text(const char *str) /* cycle through list of actions */ do { - _cap_debug("next char = `%c'", *str); + _cap_debug("next char = '%c'", *str); if (*str && !isspace(*str)) { switch (*str++) { /* Effective, Inheritable, Permitted */ case 'e': @@ -309,20 +309,19 @@ int cap_from_name(const char *name, cap_value_t *value_p) */ char *cap_to_name(cap_value_t cap) { - if ((cap < 0) || (cap >= __CAP_BITS)) { -#if UINT_MAX != 4294967295U -# error Recompile with correctly sized numeric array -#endif - char *tmp, *result; - - (void) asprintf(&tmp, "%u", cap); - result = _libcap_strdup(tmp); - free(tmp); + char *tmp, *result; - return result; - } else { + if ((cap >= 0) && (cap < __CAP_BITS)) { return _libcap_strdup(_cap_names[cap]); } + if (asprintf(&tmp, "%u", cap) <= 0) { + _cap_debug("asprintf filed"); + return NULL; + } + + result = _libcap_strdup(tmp); + free(tmp); + return result; } /* @@ -348,6 +347,12 @@ static int getstateflags(cap_t caps, int capno) return f; } +/* + * This code assumes that the longest named capability is longer than + * the decimal text representation of __CAP_MAXBITS. This is very true + * at the time of writing and likely to remain so. However, we have + * a test in cap_text to validate it at build time. + */ #define CAP_TEXT_BUFFER_ZONE 100 char *cap_to_text(cap_t caps, ssize_t *length_p) diff --git a/libcap/libcap.h b/libcap/libcap.h index 67fa0d0..97a47ae 100644 --- a/libcap/libcap.h +++ b/libcap/libcap.h @@ -227,7 +227,7 @@ extern int capsetp(pid_t pid, cap_t cap_d); min = mid + 1; \ } \ } \ - val = min ? min : fallback; \ + val = min ? (min <= high ? min : fallback) : fallback; \ } while(0) /* |