]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: checks: Dequeue checks on purge
authorOlivier Houchard <ohouchard@haproxy.com>
Mon, 8 Jun 2026 13:48:42 +0000 (15:48 +0200)
committerOlivier Houchard <cognet@ci0.org>
Mon, 8 Jun 2026 13:06:09 +0000 (15:06 +0200)
When tune.max-checks-per-thread is used, checks that should run are
queued, to avoid having too many checks running at the same time.
But if the check is about to be purged, because the server is being
deleted, we have to explicitly remove it from the queue as that memory is
about to be freed, otherwise it will cause a use-after-free.
Also, queued checks have not yet incremented th_ctx->running_checks, so
don't decrement it if we're queued.

This should be backported up to 3.0.

src/check.c

index 3864e7c771502ecdd048aafd8eeb3897a76cbbb2..8d0effa4cfd7e8d9ebc1659c591290027f9707f0 100644 (file)
@@ -1407,7 +1407,20 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
 
        check_release_buf(check, &check->bi);
        check_release_buf(check, &check->bo);
-       _HA_ATOMIC_DEC(&th_ctx->running_checks);
+
+       if (unlikely(LIST_INLIST(&check->check_queue))) {
+               /*
+                * If that check is still queued, and we're about to
+                * purge it, then remove it from the queue, as it is
+                * about to be freed.
+                * This can happen if a server is deleted while the check
+                * is queued.
+                */
+               if (check->state & CHK_ST_PURGE)
+                       LIST_DEL_INIT(&check->check_queue);
+       }
+       else
+               _HA_ATOMIC_DEC(&th_ctx->running_checks);
        _HA_ATOMIC_DEC(&th_ctx->active_checks);
        check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
        check->state &= ~CHK_ST_READY;
@@ -1566,6 +1579,7 @@ void free_check(struct check *check)
                ha_free(&check->tcpcheck);
        }
 
+       LIST_DEL_INIT(&check->check_queue);
        pool_free(pool_head_uniqueid, istptr(check->unique_id));
        check->unique_id = IST_NULL;
        ha_free(&check->pool_conn_name);