Merge pull request #26555 from poettering/cleanup-array
add gcc cleanup attribute based logic to destroy arrays
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
typedef void (*free_func_t)(void *p);
|
||||
typedef void* (*mfree_func_t)(void *p);
|
||||
typedef void (*free_array_func_t)(void *p, size_t n);
|
||||
|
||||
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
|
||||
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
|
||||
|
||||
@@ -111,3 +111,37 @@ static inline void erase_and_freep(void *p) {
|
||||
static inline void erase_char(char *p) {
|
||||
explicit_bzero_safe(p, sizeof(char));
|
||||
}
|
||||
|
||||
/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
|
||||
struct ArrayCleanup {
|
||||
void **parray;
|
||||
size_t *pn;
|
||||
free_array_func_t pfunc;
|
||||
};
|
||||
|
||||
static inline void array_cleanup(struct ArrayCleanup *c) {
|
||||
assert(c);
|
||||
|
||||
assert(!c->parray == !c->pn);
|
||||
|
||||
if (!c->parray)
|
||||
return;
|
||||
|
||||
if (*c->parray) {
|
||||
assert(c->pfunc);
|
||||
c->pfunc(*c->parray, *c->pn);
|
||||
*c->parray = NULL;
|
||||
}
|
||||
|
||||
*c->pn = 0;
|
||||
}
|
||||
|
||||
#define CLEANUP_ARRAY(array, n, func) \
|
||||
_cleanup_(array_cleanup) _unused_ struct ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
|
||||
.parray = (void**) &(array), \
|
||||
.pn = &(n), \
|
||||
.pfunc = (free_array_func_t) ({ \
|
||||
void (*_f)(typeof(array[0]) *a, size_t b) = func; \
|
||||
_f; \
|
||||
}), \
|
||||
}
|
||||
|
||||
+8
-11
@@ -2335,6 +2335,8 @@ static int reply_install_changes_and_free(
|
||||
bool bad = false, good = false;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(changes, n_changes, install_changes_free);
|
||||
|
||||
if (install_changes_have_modification(changes, n_changes)) {
|
||||
r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL);
|
||||
if (r < 0)
|
||||
@@ -2343,17 +2345,17 @@ static int reply_install_changes_and_free(
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &reply);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
if (carries_install_info >= 0) {
|
||||
r = sd_bus_message_append(reply, "b", carries_install_info);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(sss)");
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < n_changes; i++) {
|
||||
|
||||
@@ -2368,7 +2370,7 @@ static int reply_install_changes_and_free(
|
||||
changes[i].path,
|
||||
changes[i].source);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
good = true;
|
||||
}
|
||||
@@ -2376,18 +2378,13 @@ static int reply_install_changes_and_free(
|
||||
/* If there was a failed change, and no successful change, then return the first failure as proper
|
||||
* method call error. */
|
||||
if (bad && !good)
|
||||
return install_error(error, 0, changes, n_changes);
|
||||
return install_error(error, 0, TAKE_PTR(changes), n_changes);
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
install_changes_free(changes, n_changes);
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
|
||||
fail:
|
||||
install_changes_free(changes, n_changes);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int method_enable_unit_files_generic(
|
||||
|
||||
@@ -692,8 +692,8 @@ static int match_component_compare(const struct bus_match_component *a, const st
|
||||
return CMP(a->type, b->type);
|
||||
}
|
||||
|
||||
void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
|
||||
for (unsigned i = 0; i < n_components; i++)
|
||||
void bus_match_parse_free(struct bus_match_component *components, size_t n_components) {
|
||||
for (size_t i = 0; i < n_components; i++)
|
||||
free(components[i].value_str);
|
||||
|
||||
free(components);
|
||||
@@ -702,20 +702,22 @@ void bus_match_parse_free(struct bus_match_component *components, unsigned n_com
|
||||
int bus_match_parse(
|
||||
const char *match,
|
||||
struct bus_match_component **ret_components,
|
||||
unsigned *ret_n_components) {
|
||||
size_t *ret_n_components) {
|
||||
|
||||
struct bus_match_component *components = NULL;
|
||||
unsigned n_components = 0;
|
||||
size_t n_components = 0;
|
||||
int r;
|
||||
|
||||
assert(match);
|
||||
assert(ret_components);
|
||||
assert(ret_n_components);
|
||||
|
||||
CLEANUP_ARRAY(components, n_components, bus_match_parse_free);
|
||||
|
||||
while (*match != '\0') {
|
||||
const char *eq, *q;
|
||||
enum bus_match_node_type t;
|
||||
unsigned j = 0;
|
||||
size_t j = 0;
|
||||
_cleanup_free_ char *value = NULL;
|
||||
bool escaped = false, quoted;
|
||||
uint8_t u;
|
||||
@@ -724,16 +726,12 @@ int bus_match_parse(
|
||||
match += strspn(match, " ");
|
||||
|
||||
eq = strchr(match, '=');
|
||||
if (!eq) {
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (!eq)
|
||||
return -EINVAL;
|
||||
|
||||
t = bus_match_node_type_from_string(match, eq - match);
|
||||
if (t < 0) {
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (t < 0)
|
||||
return -EINVAL;
|
||||
|
||||
quoted = eq[1] == '\'';
|
||||
|
||||
@@ -741,14 +739,12 @@ int bus_match_parse(
|
||||
|
||||
if (*q == '\0') {
|
||||
|
||||
if (quoted) {
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
} else {
|
||||
if (value)
|
||||
value[j] = '\0';
|
||||
break;
|
||||
}
|
||||
if (quoted)
|
||||
return -EINVAL;
|
||||
|
||||
if (value)
|
||||
value[j] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!escaped) {
|
||||
@@ -772,10 +768,8 @@ int bus_match_parse(
|
||||
}
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(value, j + 2)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!GREEDY_REALLOC(value, j + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[j++] = *q;
|
||||
escaped = false;
|
||||
@@ -783,25 +777,21 @@ int bus_match_parse(
|
||||
|
||||
if (!value) {
|
||||
value = strdup("");
|
||||
if (!value) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (t == BUS_MATCH_MESSAGE_TYPE) {
|
||||
r = bus_message_type_from_string(value, &u);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
value = mfree(value);
|
||||
} else
|
||||
u = 0;
|
||||
|
||||
if (!GREEDY_REALLOC(components, n_components + 1)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!GREEDY_REALLOC(components, n_components + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
components[n_components++] = (struct bus_match_component) {
|
||||
.type = t,
|
||||
@@ -812,10 +802,8 @@ int bus_match_parse(
|
||||
if (q[quoted] == 0)
|
||||
break;
|
||||
|
||||
if (q[quoted] != ',') {
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (q[quoted] != ',')
|
||||
return -EINVAL;
|
||||
|
||||
match = q + 1 + quoted;
|
||||
}
|
||||
@@ -824,23 +812,17 @@ int bus_match_parse(
|
||||
typesafe_qsort(components, n_components, match_component_compare);
|
||||
|
||||
/* Check for duplicates */
|
||||
for (unsigned i = 0; i+1 < n_components; i++)
|
||||
if (components[i].type == components[i+1].type) {
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
for (size_t i = 0; i+1 < n_components; i++)
|
||||
if (components[i].type == components[i+1].type)
|
||||
return -EINVAL;
|
||||
|
||||
*ret_components = components;
|
||||
*ret_components = TAKE_PTR(components);
|
||||
*ret_n_components = n_components;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
bus_match_parse_free(components, n_components);
|
||||
return r;
|
||||
}
|
||||
|
||||
char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
|
||||
char *bus_match_to_string(struct bus_match_component *components, size_t n_components) {
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
size_t size = 0;
|
||||
int r;
|
||||
@@ -854,7 +836,7 @@ char *bus_match_to_string(struct bus_match_component *components, unsigned n_com
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < n_components; i++) {
|
||||
for (size_t i = 0; i < n_components; i++) {
|
||||
char buf[32];
|
||||
|
||||
if (i != 0)
|
||||
@@ -882,7 +864,7 @@ char *bus_match_to_string(struct bus_match_component *components, unsigned n_com
|
||||
int bus_match_add(
|
||||
struct bus_match_node *root,
|
||||
struct bus_match_component *components,
|
||||
unsigned n_components,
|
||||
size_t n_components,
|
||||
struct match_callback *callback) {
|
||||
|
||||
int r;
|
||||
@@ -890,7 +872,7 @@ int bus_match_add(
|
||||
assert(root);
|
||||
assert(callback);
|
||||
|
||||
for (unsigned i = 0; i < n_components; i++) {
|
||||
for (size_t i = 0; i < n_components; i++) {
|
||||
r = bus_match_add_compare_value(root,
|
||||
components[i].type,
|
||||
components[i].value_u8,
|
||||
@@ -1038,7 +1020,7 @@ void bus_match_dump(FILE *out, struct bus_match_node *node, unsigned level) {
|
||||
bus_match_dump(out, c, level + 1);
|
||||
}
|
||||
|
||||
enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
|
||||
enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, size_t n_components) {
|
||||
bool found_driver = false;
|
||||
|
||||
if (n_components <= 0)
|
||||
@@ -1052,7 +1034,7 @@ enum bus_match_scope bus_match_get_scope(const struct bus_match_component *compo
|
||||
* local messages, then we check if it only matches on the
|
||||
* driver. */
|
||||
|
||||
for (unsigned i = 0; i < n_components; i++) {
|
||||
for (size_t i = 0; i < n_components; i++) {
|
||||
const struct bus_match_component *c = components + i;
|
||||
|
||||
if (c->type == BUS_MATCH_SENDER) {
|
||||
|
||||
@@ -65,7 +65,7 @@ enum bus_match_scope {
|
||||
|
||||
int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m);
|
||||
|
||||
int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, struct match_callback *callback);
|
||||
int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, size_t n_components, struct match_callback *callback);
|
||||
int bus_match_remove(struct bus_match_node *root, struct match_callback *callback);
|
||||
|
||||
void bus_match_free(struct bus_match_node *node);
|
||||
@@ -75,8 +75,8 @@ void bus_match_dump(FILE *out, struct bus_match_node *node, unsigned level);
|
||||
const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l);
|
||||
enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n);
|
||||
|
||||
int bus_match_parse(const char *match, struct bus_match_component **ret_components, unsigned *ret_n_components);
|
||||
void bus_match_parse_free(struct bus_match_component *components, unsigned n_components);
|
||||
char *bus_match_to_string(struct bus_match_component *components, unsigned n_components);
|
||||
int bus_match_parse(const char *match, struct bus_match_component **ret_components, size_t *ret_n_components);
|
||||
void bus_match_parse_free(struct bus_match_component *components, size_t n_components);
|
||||
char *bus_match_to_string(struct bus_match_component *components, size_t n_components);
|
||||
|
||||
enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components);
|
||||
enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, size_t n_components);
|
||||
|
||||
@@ -54,7 +54,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
offset = end ? (size_t) (end - (char*) data + 1) : size;
|
||||
|
||||
struct bus_match_component *components;
|
||||
unsigned n_components;
|
||||
size_t n_components;
|
||||
r = bus_match_parse(line, &components, &n_components);
|
||||
if (IN_SET(r, -EINVAL, -ENOMEM)) {
|
||||
log_debug_errno(r, "Failed to parse line: %m");
|
||||
@@ -62,11 +62,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
}
|
||||
assert_se(r >= 0); /* We only expect EINVAL and ENOMEM errors, or success. */
|
||||
|
||||
log_debug("Parsed %u components.", n_components);
|
||||
CLEANUP_ARRAY(components, n_components, bus_match_parse_free);
|
||||
|
||||
log_debug("Parsed %zu components.", n_components);
|
||||
|
||||
_cleanup_free_ char *again = bus_match_to_string(components, n_components);
|
||||
if (!again) {
|
||||
bus_match_parse_free(components, n_components);
|
||||
log_oom();
|
||||
break;
|
||||
}
|
||||
@@ -75,7 +76,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
fprintf(g, "%s\n", again);
|
||||
|
||||
r = bus_match_add(&root, components, n_components, &slot.match_callback);
|
||||
bus_match_parse_free(components, n_components);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to add match: %m");
|
||||
break;
|
||||
|
||||
@@ -3505,7 +3505,7 @@ static int bus_add_match_full(
|
||||
void *userdata) {
|
||||
|
||||
struct bus_match_component *components = NULL;
|
||||
unsigned n_components = 0;
|
||||
size_t n_components = 0;
|
||||
sd_bus_slot *s = NULL;
|
||||
int r = 0;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static bool mask_contains(unsigned a[], unsigned n) {
|
||||
|
||||
static int match_add(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) {
|
||||
struct bus_match_component *components;
|
||||
unsigned n_components;
|
||||
size_t n_components;
|
||||
sd_bus_slot *s;
|
||||
int r;
|
||||
|
||||
@@ -48,22 +48,22 @@ static int match_add(sd_bus_slot *slots, struct bus_match_node *root, const char
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
CLEANUP_ARRAY(components, n_components, bus_match_parse_free);
|
||||
|
||||
s->userdata = INT_TO_PTR(value);
|
||||
s->match_callback.callback = filter;
|
||||
|
||||
r = bus_match_add(root, components, n_components, &s->match_callback);
|
||||
bus_match_parse_free(components, n_components);
|
||||
|
||||
return r;
|
||||
return bus_match_add(root, components, n_components, &s->match_callback);
|
||||
}
|
||||
|
||||
static void test_match_scope(const char *match, enum bus_match_scope scope) {
|
||||
struct bus_match_component *components = NULL;
|
||||
unsigned n_components = 0;
|
||||
size_t n_components = 0;
|
||||
|
||||
CLEANUP_ARRAY(components, n_components, bus_match_parse_free);
|
||||
|
||||
assert_se(bus_match_parse(match, &components, &n_components) >= 0);
|
||||
assert_se(bus_match_get_scope(components, n_components) == scope);
|
||||
bus_match_parse_free(components, n_components);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@@ -574,7 +574,6 @@ static int normalize_portable_changes(
|
||||
|
||||
PortableChange *changes = NULL;
|
||||
size_t n_changes = 0;
|
||||
int r = 0;
|
||||
|
||||
assert(ret_n_changes);
|
||||
assert(ret_changes);
|
||||
@@ -586,12 +585,13 @@ static int normalize_portable_changes(
|
||||
if (!changes)
|
||||
return -ENOMEM;
|
||||
|
||||
CLEANUP_ARRAY(changes, n_changes, portable_changes_free);
|
||||
|
||||
/* Corner case: only detached, nothing attached */
|
||||
if (n_changes_attached == 0) {
|
||||
memcpy(changes, changes_detached, sizeof(PortableChange) * n_changes_detached);
|
||||
*ret_changes = TAKE_PTR(changes);
|
||||
*ret_n_changes = n_changes_detached;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -608,17 +608,13 @@ static int normalize_portable_changes(
|
||||
_cleanup_free_ char *path = NULL, *source = NULL;
|
||||
|
||||
path = strdup(changes_detached[i].path);
|
||||
if (!path) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
if (changes_detached[i].source) {
|
||||
source = strdup(changes_detached[i].source);
|
||||
if (!source) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!source)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
changes[n_changes++] = (PortableChange) {
|
||||
@@ -633,10 +629,6 @@ static int normalize_portable_changes(
|
||||
*ret_changes = TAKE_PTR(changes);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
portable_changes_free(changes, n_changes);
|
||||
return r;
|
||||
}
|
||||
|
||||
int bus_image_common_reattach(
|
||||
|
||||
@@ -15,4 +15,41 @@ TEST(eqzero) {
|
||||
assert_se(!eqzero(longer));
|
||||
}
|
||||
|
||||
static void my_destructor(struct iovec *iov, size_t n) {
|
||||
/* not really a destructor, just something we can use to check if the destruction worked */
|
||||
memset(iov, 'y', sizeof(struct iovec) * n);
|
||||
}
|
||||
|
||||
TEST(cleanup_array) {
|
||||
struct iovec *iov, *saved_iov;
|
||||
size_t n, saved_n;
|
||||
|
||||
n = 7;
|
||||
iov = new(struct iovec, n);
|
||||
assert_se(iov);
|
||||
|
||||
memset(iov, 'x', sizeof(struct iovec) * n);
|
||||
|
||||
saved_iov = iov;
|
||||
saved_n = n;
|
||||
|
||||
{
|
||||
assert_se(memeqbyte('x', saved_iov, sizeof(struct iovec) * saved_n));
|
||||
assert_se(iov);
|
||||
assert_se(n > 0);
|
||||
|
||||
CLEANUP_ARRAY(iov, n, my_destructor);
|
||||
|
||||
assert_se(memeqbyte('x', saved_iov, sizeof(struct iovec) * saved_n));
|
||||
assert_se(iov);
|
||||
assert_se(n > 0);
|
||||
}
|
||||
|
||||
assert_se(memeqbyte('y', saved_iov, sizeof(struct iovec) * saved_n));
|
||||
assert_se(!iov);
|
||||
assert_se(n == 0);
|
||||
|
||||
free(saved_iov);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
Reference in New Issue
Block a user