]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Minor] Backport ucl_object_iterate_end() from libucl to fix iterator leak
authorVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 16 Feb 2026 15:36:16 +0000 (15:36 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 16 Feb 2026 15:36:16 +0000 (15:36 +0000)
Add ucl_object_iterate_end() function to free resources associated with
an inline iterator when iteration is abandoned before completion. This
fixes a potential memory leak when iterating over UCL_OBJECT types where
internal heap state is allocated.

Changes backported from libucl 0.9.3:
- Add ucl_hash_iterate_free() to ucl_hash.c/h
- Add ucl_object_iterate_end() to ucl_util.c and ucl.h
- Add ucl_iterate_object_end macro alias

Note: Rspamd already had the security fixes for:
- heap-buffer-overflow in ucl_maybe_parse_number
- heap-buffer-overflow in ucl_parse_multiline_string
- Use-After-Free in ucl_hash_insert
- Invalid JSON emission when merging objects

contrib/libucl/ucl.h
contrib/libucl/ucl_hash.c
contrib/libucl/ucl_hash.h
contrib/libucl/ucl_util.c

index 65361ae19252ae81c9cd6b8e5242dc8849f1d024..91fd28adcb1f7e42d93cb64caa4946b39e6db23c 100644 (file)
@@ -881,6 +881,29 @@ UCL_EXTERN const ucl_object_t *ucl_object_iterate_with_error(const ucl_object_t
 #define ucl_iterate_object ucl_object_iterate
 #define ucl_object_iterate(ob, it, ev) ucl_object_iterate_with_error((ob), (it), (ev), NULL)
 
+/**
+ * Free resources associated with an inline iterator when iteration is
+ * abandoned before completion. Only needed for UCL_OBJECT iteration where
+ * internal heap state is allocated. Safe to call with a NULL iterator or
+ * on non-object types.
+ *
+ * Example usage:
+ * ucl_object_iter_t it = NULL;
+ * while ((cur = ucl_iterate_object(obj, &it, true))) {
+ *     if (error) {
+ *         ucl_object_iterate_end(obj, &it);
+ *         return;
+ *     }
+ * }
+ *
+ * @param obj the object being iterated
+ * @param iter pointer to the iterator to free
+ */
+UCL_EXTERN void ucl_object_iterate_end(const ucl_object_t *obj,
+                                                                          ucl_object_iter_t *iter);
+
+#define ucl_iterate_object_end ucl_object_iterate_end
+
 /**
  * Create new safe iterator for the specified object
  * @param obj object to iterate
index eb2053ba1b22d0c0fa7ea53fe04235c19a9e8b08..cda230d911a8475c2c557e1b43e6ab8b9cbf480c 100644 (file)
@@ -355,6 +355,15 @@ bool ucl_hash_iter_has_next(ucl_hash_t *hashlin, ucl_hash_iter_t iter)
        return it->cur != NULL;
 }
 
+void ucl_hash_iterate_free(ucl_hash_iter_t iter)
+{
+       struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *) (iter);
+
+       if (it != NULL) {
+               UCL_FREE(sizeof(*it), it);
+       }
+}
+
 
 const ucl_object_t *
 ucl_hash_search(ucl_hash_t *hashlin, const char *key, unsigned keylen)
index c2d5517bbb86c3dfad5441a4bd69ecea39e9ce68..42e2247a98d34eadd1873fd5759a28636c499ebf 100644 (file)
@@ -97,6 +97,12 @@ const void* ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *
  */
 bool ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter);
 
+/**
+ * Free resources associated with an iterator when iteration is abandoned
+ * before completion. Safe to call with NULL iterator.
+ */
+void ucl_hash_iterate_free(ucl_hash_iter_t iter);
+
 /**
  * Reserves space in hash
  * @return true on sucess, false on failure (e.g. ENOMEM)
index 5ea9f89ffc6062d8919dd01d5bbb0e64202a199e..b17d0b63acc8c038dbbc675d01e938062329fd4b 100644 (file)
@@ -2784,6 +2784,19 @@ ucl_object_iterate_with_error (const ucl_object_t *obj, ucl_object_iter_t *iter,
        return NULL;
 }
 
+void ucl_object_iterate_end(const ucl_object_t *obj, ucl_object_iter_t *iter)
+{
+       if (iter == NULL || *iter == NULL) {
+               return;
+       }
+
+       if (obj != NULL && obj->type == UCL_OBJECT) {
+               ucl_hash_iterate_free(*iter);
+       }
+
+       *iter = NULL;
+}
+
 enum ucl_safe_iter_flags {
        UCL_ITERATE_FLAG_UNDEFINED = 0,
        UCL_ITERATE_FLAG_INSIDE_ARRAY,