From: Amaury Denoyelle Date: Thu, 22 Jan 2026 14:20:31 +0000 (+0100) Subject: BUG/MINOR: proxy: fix deinit crash on defaults with duplicate name X-Git-Tag: v3.4-dev3~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac877a25ddf5f4a4dc1fbc0c5d225f806006e053;p=thirdparty%2Fhaproxy.git BUG/MINOR: proxy: fix deinit crash on defaults with duplicate name A defaults proxy instance may be move into the orphaned list when it is replaced by a newer section with the same name. This is attached via member as a single linked list entry. However, proxy free does not clear attach point. This causes a crash on deinit if orphaned list is not empty. First, all frontend/backend instances are freed. This triggers the release of every referenced defaults instances as their refcount reach zero, but orphaned list is not clean up. A loop is then conducted on orphaned list via proxy_destroy_all_unref_defaults(). This causes a segfault due to access on already freed entries. To fix this, this patch extends proxy_destroy_defaults(). If orphaned list is not empty, a loop is performed to remove a possible entry of the currently released defaults instance. This ensures that loop over orphaned list won't be able to access to already freed entries. This bug is pretty rare as it requires to have duplicate name in defaults sections, and also to use settings which forces defaults referencing, such as TCP/HTTP rules. This can be reproduced with the minimal config here : defaults def http-request return status 200 frontend fe bind :20080 defaults def Note that in fact orphaned list looping is not strictly necessary, as defaults instances are automatically removed via refcounting. This will be the purpose of a future patch. However, to limit the risk of regression on stable releases during backport, this patch uses the more direct approach for now. This must be backported up to 3.1. --- diff --git a/src/proxy.c b/src/proxy.c index dd7354352..e4e32a2a1 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -1639,13 +1639,34 @@ void proxy_free_defaults(struct proxy *defproxy) */ void proxy_destroy_defaults(struct proxy *px) { + struct proxy *prev; + if (!px) return; if (!(px->cap & PR_CAP_DEF)) return; BUG_ON(px->conf.refcount != 0); + cebis_item_delete((px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name, conf.name_node, id, px); + + /* If orphaned defaults list is not empty, it may contain instance. + * In this case it is necessary to manually remove it from the list. + */ + if (orphaned_default_proxies) { + if (orphaned_default_proxies == px) { + orphaned_default_proxies = px->next; + } + else { + for (prev = orphaned_default_proxies; + prev && prev->next != px; prev = prev->next) + ; + if (prev) + prev->next = px->next; + } + px->next = NULL; + } + proxy_free_defaults(px); free(px); }