collectd: ping - fix use-after-free when re-resolving a host
authorHannu Nyman <redacted>
Sun, 14 Jun 2026 07:03:41 +0000 (10:03 +0300)
committerHannu Nyman <redacted>
Sun, 14 Jun 2026 07:03:41 +0000 (10:03 +0300)
Add a patch fixing a possible crash, when a non-responding host is
tried to be pinged and retry attempts are limited by MaxMissed option.

> daemon.err: collectd[14133]: ping plugin: host 192.168.1.99 has not answered 3 PING requests, triggering resolve
> daemon.info: procd: Instance collectd::instance1 s in a crash loop 7 crashes, 3 seconds since last crash

Fixes: #29649
* upstream bug created: https://github.com/collectd/collectd/issues/4406
* older upstream bug: https://github.com/collectd/collectd/issues/3079

Signed-off-by: Hannu Nyman <redacted>
utils/collectd/Makefile
utils/collectd/patches/090-fix-ping-plugin-use-after-free-when-re-resolving-a-h.patch [new file with mode: 0644]

index 6f852b604aa83d0c297d4e78cc2b06e5719edd69..77138875d3abbbdd09d0785f396b1f95c6ac8594 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=collectd
 PKG_VERSION:=5.12.0
-PKG_RELEASE:=55
+PKG_RELEASE:=56
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://collectd.org/files/ \
diff --git a/utils/collectd/patches/090-fix-ping-plugin-use-after-free-when-re-resolving-a-h.patch b/utils/collectd/patches/090-fix-ping-plugin-use-after-free-when-re-resolving-a-h.patch
new file mode 100644 (file)
index 0000000..3d90e4a
--- /dev/null
@@ -0,0 +1,45 @@
+From 67e873a4eab151ce4808e84eaba6dbc924e86de4 Mon Sep 17 00:00:00 2001
+From: Nicolas Roche <nicolasroche898@gmail.com>
+Date: Fri, 5 Jun 2026 00:55:11
+Subject: [PATCH] ping plugin: fix use-after-free when re-resolving a host
+Signed-off-by: Nicolas Roche <nicolasroche898@gmail.com>
+
+ping_dispatch_all() iterates the pingobj host list and, when a host has
+missed ping_max_missed packets, re-resolves it by calling
+ping_host_remove() + ping_host_add(). ping_host_remove() frees the
+pinghost that the iterator currently points at, but the for-loop's
+increment then called ping_iterator_next(iter), dereferencing the freed
+node (iter->next) -- a use-after-free.
+
+It only crashes intermittently: the freed block usually still holds a
+valid ->next (or is reused and zeroed by the trailing ping_host_add),
+so the loop limps on; it segfaults when ping_host_add's strdup/getaddrinfo
+clobbers the freed block's ->next before it is re-read. Reproducible by
+monitoring a permanently-unreachable host with MaxMissed enabled.
+
+Fix: read the next pointer at the top of the loop body, before the
+re-resolve can free the current node. Loop body and semantics unchanged.
+
+Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
+---
+ src/ping.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/src/ping.c
++++ b/src/ping.c
+@@ -146,12 +146,13 @@ static int ping_dispatch_all(pingobj_t *
+   hostlist_t *hl;
+   int status;
+-  for (pingobj_iter_t *iter = ping_iterator_get(pingobj); iter != NULL;
+-       iter = ping_iterator_next(iter)) { /* {{{ */
++  for (pingobj_iter_t *iter = ping_iterator_get(pingobj), *next; iter != NULL;
++       iter = next) { /* {{{ */
+     char userhost[NI_MAXHOST];
+     double latency;
+     size_t param_size;
++    next = ping_iterator_next(iter); /* fetch next now: iter may be freed at line 213 (ping_host_remove) */
+     param_size = sizeof(userhost);
+     status = ping_iterator_get_info(iter,
+ #ifdef PING_INFO_USERNAME
git clone https://git.99rst.org/PROJECT