aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-08-21 10:29:16 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-08-21 10:29:16 -0700
commitf81144578ff24a70356faafb82e55de8f3e1292f (patch)
tree2f96cb311fe2116b902d23913287f1fe328c0124
parent86c85c07c83f7ddc722b872ea0ff9e9b0f70bbc8 (diff)
downloadlibcap-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.c20
-rw-r--r--libcap/cap_test.c37
-rw-r--r--libcap/cap_text.c29
-rw-r--r--libcap/libcap.h2
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)
/*