From: Florian Forster Date: Sun, 14 May 2017 06:24:51 +0000 (+0200) Subject: perl plugin: Fix potential double-free. X-Git-Tag: collectd-5.7.2~2^2~5^2 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=867e977f14bdb55dd2c727a8b9b5f6aaac41e558;p=collectd.git perl plugin: Fix potential double-free. The av2notification_meta() function updated it's parameter by doing (*m) = malloc(); Afterwards, if an error occurred, it would call `free(*m);` and (potentially) return, leaving an invalid pointer stored in `m`. The caller would then try to free the returned pointer because it was non-NULL. This patch fixes this by doing several code cleanups: * The meta argument is not updated unnecessarily. Instead, a local variable is allocated and used within the loop and only on success is the return pointer updated. * Introduce additional local variables instead of re-using the `tmp` variable multiple times. * Name the variable pointing to the end of the linked list appropriately `tail` and remove one level of indirection. Reading code that is using pointers to pointers is unnecessarily hard. --- diff --git a/src/perl.c b/src/perl.c index 05a6ef9b..d01f5c7c 100644 --- a/src/perl.c +++ b/src/perl.c @@ -462,16 +462,15 @@ static int av2data_set(pTHX_ AV *array, char *name, data_set_t *ds) { * meta => [ { name => , value => }, ... ] * } */ -static int av2notification_meta(pTHX_ AV *array, notification_meta_t **meta) { - notification_meta_t **m = meta; +static int av2notification_meta(pTHX_ AV *array, notification_meta_t **ret_meta) { + notification_meta_t *tail = NULL; int len = av_len(array); for (int i = 0; i <= len; ++i) { SV **tmp = av_fetch(array, i, 0); - HV *hash; - if (NULL == tmp) + if (tmp == NULL) return -1; if (!(SvROK(*tmp) && (SVt_PVHV == SvTYPE(SvRV(*tmp))))) { @@ -480,42 +479,51 @@ static int av2notification_meta(pTHX_ AV *array, notification_meta_t **meta) { continue; } - hash = (HV *)SvRV(*tmp); + HV *hash = (HV *)SvRV(*tmp); - *m = smalloc(sizeof(**m)); + notification_meta_t *m = calloc(1, sizeof(*m)); + if (m == NULL) + return ENOMEM; - if (NULL == (tmp = hv_fetch(hash, "name", 4, 0))) { + SV **name = hv_fetch(hash, "name", strlen("name"), 0); + if (name == NULL) { log_warn("av2notification_meta: Skipping invalid " "meta information."); - free(*m); + sfree(m); continue; } - sstrncpy((*m)->name, SvPV_nolen(*tmp), sizeof((*m)->name)); + sstrncpy(m->name, SvPV_nolen(*name), sizeof(m->name)); - if (NULL == (tmp = hv_fetch(hash, "value", 5, 0))) { + SV **value = hv_fetch(hash, "value", strlen("value"), 0); + if (value == NULL) { log_warn("av2notification_meta: Skipping invalid " "meta information."); - free(*m); + sfree(m); continue; } - if (SvNOK(*tmp)) { - (*m)->nm_value.nm_double = SvNVX(*tmp); - (*m)->type = NM_TYPE_DOUBLE; - } else if (SvUOK(*tmp)) { - (*m)->nm_value.nm_unsigned_int = SvUVX(*tmp); - (*m)->type = NM_TYPE_UNSIGNED_INT; - } else if (SvIOK(*tmp)) { - (*m)->nm_value.nm_signed_int = SvIVX(*tmp); - (*m)->type = NM_TYPE_SIGNED_INT; + if (SvNOK(*value)) { + m->nm_value.nm_double = SvNVX(*value); + m->type = NM_TYPE_DOUBLE; + } else if (SvUOK(*value)) { + m->nm_value.nm_unsigned_int = SvUVX(*value); + m->type = NM_TYPE_UNSIGNED_INT; + } else if (SvIOK(*value)) { + m->nm_value.nm_signed_int = SvIVX(*value); + m->type = NM_TYPE_SIGNED_INT; } else { - (*m)->nm_value.nm_string = sstrdup(SvPV_nolen(*tmp)); - (*m)->type = NM_TYPE_STRING; + m->nm_value.nm_string = sstrdup(SvPV_nolen(*value)); + m->type = NM_TYPE_STRING; } - (*m)->next = NULL; - m = &((*m)->next); + m->next = NULL; + if (tail == NULL) + *ret_meta = m; + else + tail->next = m; + tail = m; } + return 0; } /* static int av2notification_meta (AV *, notification_meta_t *) */