On Wed, Oct 28, 2026 at 07:46:44AM -0700, Martin Mirchev wrote:
> Hi,
> Here are the patches for GitHub issues #3545 and #3546, as Amaury
> suggested in #3547, against current master (8c84ab562). They are attached
> (git format-patch) and independent of each other.
> 1/2 BUG/MINOR: sample: fix NULL dereference in the ltime/utime converters
> conv_time_common() returns NULL when the expanded format does not fit
> in the trash chunk or when alloc_trash_chunk() fails, and its four
> callers dereference it. With ms_ltime("%N" x 3000) the config is
> accepted and the first request crashes the process. The converters
> now fail instead. Backport to 2.9 and later.

Thanks, I merged this patch. I only slightly edited the commit message
to correct the backport reference to 3.1 as this version is end of life.

> 2/2 BUG/MINOR: debug: fix "debug dev fd" crash on unknown address families
> sa2str() returns NULL for families other than AF_INET, AF_INET6 and
> AF_UNIX, and "debug dev fd" passes it to strlen(). With an inherited
> AF_NETLINK socket the command crashes the process. It now prints "?".
> Backport to 3.1 and later.
> Tested on 8c84ab562, release build (USE_OPENSSL=1): on master all four
> converters (ms_ltime, us_ltime, ms_utime, us_utime) and "debug dev fd" with
> an inherited netlink socket crash with SIGSEGV; with the patches each
> converter request returns 200, "debug dev fd" prints "laddr=? raddr=?" for
> the netlink fd, and the process keeps running.
> Both issues were found with a static analysis engine (Infer/Pulse) and
> verified manually.

Regarding this patch, there is several points that can be improved. In
my opinion the code is simpler with a single NULL check around both the
cleanup loop and the dump. Also, the commit message is not totally
accurate. First, I removed the reference to AF_NETLINK as I'm not sure
haproxy is really dealing with such sockets. Then, sa2str() can also
return NULL due to a memory allocation failure, so a crash could happen
even with supported address families. Finally, regarding the backport, I
think it should be picked up to 2.6, as the original "debug dev fd"
handler already contains the dump of a possible NULL value.

I have written a new version for the "debug dev fd" fix, attached as a
patch with this email. Please tell me if this suits you.

Thanks for your contribution,

-- 
Amaury Denoyelle
>From 81bf7ffcc4588e503647ce4aff3189cbff048e91 Mon Sep 17 00:00:00 2001
From: Amaury Denoyelle <[email protected]>
Date: Fri, 30 Oct 2026 10:53:08 +0100
Subject: [PATCH] BUG/MINOR: debug: fix "debug dev fd" crash on unknown address
 families

"debug dev fd" formats the local and peer addresses of each fd with
sa2str() and then replaces non-printable characters in the result. It
returns NULL for address families other than AF_INET, AF_INET6, AF_UNIX
and some AF_CUST_*. For supported address families, it can also return
NULL in case of a memory allocation failure.

Only an admin in expert mode can run the command.

Let's skip the cleanup and print "?" when sa2str() fails.

This fixes GitHub issue #3546.

This must be backported to all stable versions. In fact, strlen()
without guard was introduced in 3.1, which guarantees a crash in case of
an unsupported address. For older releases, only an unsafe
chunk_appendf() is present which may cause issues.
---
 src/debug.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/src/debug.c b/src/debug.c
index bc6355e12..69c1383b6 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -2304,13 +2304,18 @@ static int debug_iohandler_fd(struct appctx *appctx)
                                port = 0;
                        addrstr = sa2str(&sa, port, 0);
                        /* cleanup the output */
-                       for  (i = 0; i < strlen(addrstr); i++) {
-                               if (iscntrl((unsigned char)addrstr[i]) || 
!isprint((unsigned char)addrstr[i]))
-                                       addrstr[i] = '.';
-                       }
+                       if (addrstr) {
+                               for  (i = 0; i < strlen(addrstr); i++) {
+                                       if (iscntrl((unsigned char)addrstr[i]) 
|| !isprint((unsigned char)addrstr[i]))
+                                               addrstr[i] = '.';
+                               }
 
-                       chunk_appendf(&trash, " laddr=%s", addrstr);
-                       free(addrstr);
+                               chunk_appendf(&trash, " laddr=%s", addrstr);
+                               free(addrstr);
+                       }
+                       else {
+                               chunk_appendf(&trash, " laddr=?");
+                       }
                }
 
                salen = sizeof(sa);
@@ -2323,12 +2328,17 @@ static int debug_iohandler_fd(struct appctx *appctx)
                                port = 0;
                        addrstr = sa2str(&sa, port, 0);
                        /* cleanup the output */
-                       for  (i = 0; i < strlen(addrstr); i++) {
-                               if ((iscntrl((unsigned char)addrstr[i])) || 
!isprint((unsigned char)addrstr[i]))
-                                       addrstr[i] = '.';
+                       if (addrstr) {
+                               for  (i = 0; i < strlen(addrstr); i++) {
+                                       if ((iscntrl((unsigned 
char)addrstr[i])) || !isprint((unsigned char)addrstr[i]))
+                                               addrstr[i] = '.';
+                               }
+                               chunk_appendf(&trash, " raddr=%s", addrstr);
+                               free(addrstr);
+                       }
+                       else {
+                               chunk_appendf(&trash, " raddr=?");
                        }
-                       chunk_appendf(&trash, " raddr=%s", addrstr);
-                       free(addrstr);
                }
 
                chunk_appendf(&trash, "\n");
-- 
2.56.0

Reply via email to