Hi again,
On Fri, Jan 16, 2026 at 08:58:37AM +0100, Willy Tarreau wrote:
> Hi Hyeonggeun,
>
> On Tue, Jan 13, 2026 at 07:29:29PM +0100, Willy Tarreau wrote:
> > On Wed, Jan 14, 2026 at 03:20:27AM +0900, Hyeonggeun Oh wrote:
> > > ---
> > >
> > > Hi Willy,
> > >
> > > This is a fix of segmentation fault error (20260113):
> > >
> > > The root cause was identified as a call to var_set() in the head of
> > > action_store() function which passed an unintialized smp structure. It
> > > led to
> > > var_set() accessing invalid mem addresses, resulting in a crash. I checked
> > > that this var_set() line is unnessary since there was confusion during the
> > > initial stages of code modification, and this confusion was not
> > > subsequently
> > > resolved.
> > (...)
> >
> > OK thank you, will try it again tomorrow and hopefully finish the merge
> > this time!
>
> So now it looks better and I could even start to test it. However I'm
> now seeing this:
>
> [ALERT] (10478) : config : parsing [test-set-var3.cfg:36] : error
> detected in frontend 'fe1' while parsing 'http-request return' rule : failed
> to parse sample expression <dump_all_vars()]]> : invalid args in fetch method
> 'dump_all_vars' : invalid scope '', must be one of: sess, txn, req, res, proc.
>
> for a config containing:
>
> http-request return status 200 hdr x-var "... all=%[dump_all_vars()]"
>
> But the doc says:
>
> dump_all_vars([<scope>][,<prefix>][,<delimiter>]) : string
> ...
> Arguments:
> - <scope> (optional): sess, txn, req, res, or proc. If omitted, the scope
> is determined by context (txn for streams, sess for sessions, proc
> otherwise).
>
> This makes me think that while the empty choice doesn't work in the
> config parser, it's probably more confusing than anything else: most
> users don't know where a stream is present (tcp-request content,
> tcp-response, http-request, http-response, redirect etc) or when only
> a session is present (tcp-request connection, tcp-request session, ...).
> In addition, those where neither are present probably only concern
> "setenv" directives in the global section. And having a stream (i.e.
> being in the process of handling a request) doesn't suddenly make
> session-level variables uninteresting. Thus in my opinion, either we
> consider than an empty scope requires to iterate over all scopes, or
> we make the scope mandatory and then it's just part of the prefix in
> the end (though there may be some value in keeping it separate so as
> to permit to check its validity).
>
> Since I had performed some minor edits (dropped remaining lone spaces
> left on some lines by your editor, and properly wrapped the commit
> messages), I'm attaching the current series for you to restart from
> (you can apply the patches using "git am 000*.patch"). If you prefer
> I've also pushed it as branch 20260116-dump-vars-3.
>
> Thanks,
> Willy
I'm also seeing an ASAN crash with some regtests, that I could reduce
to this reproducer:
$ cat crash-var-asan.cfg
frontend main-fe
bind :8001
mode http
timeout client 1s
http-request allow if { path,set-var(sess.foo) -m found }
$ ./haproxy -db -f crash-var-asan.cfg
$ curl 0:8001/
produces:
=================================================================
==547==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000001835
at pc 0x00000095d3f7 bp 0x7ffe96973de0 sp 0x7ffe96973dd8
READ of size 1 at 0x602000001835 thread T0
#0 0x95d3f6 in my_strndup src/tools.c:3030
#1 0x118766e in var_set src/vars.c:617
#2 0x1188a0d in smp_conv_store src/vars.c:798
#3 0x9c462c in sample_process_cnv src/sample.c:1337
#4 0x9c47ed in sample_process src/sample.c:1380
#5 0x16e8c67 in acl_exec_cond src/acl.c:1090
#6 0x8b9430 in acl_match_cond include/haproxy/acl.h:120
#7 0x8e2d70 in http_req_get_intercept_rule src/http_ana.c:2848
#8 0x8cb03a in http_process_req_common src/http_ana.c:413
#9 0x832bb7 in process_stream src/stream.c:2138
#10 0x16370aa in run_tasks_from_lists src/task.c:660
#11 0x1639a6f in process_runnable_tasks src/task.c:902
#12 0xc1cea1 in run_poll_loop src/haproxy.c:2893
#13 0xc1de07 in run_thread_poll_loop src/haproxy.c:3121
#14 0xc2053a in main src/haproxy.c:3747
#15 0x7fc5320dc03c in __libc_start_main (/lib64/libc.so.6+0x2403c)
#16 0x444249 in _start (/g/public/haproxy/haproxy+0x444249)
0x602000001835 is located 5 bytes inside of 9-byte region
[0x602000001830,0x602000001839)
freed by thread T0 here:
#0 0x7fc5328b14f7 in free (/usr/lib64/libasan.so.6+0xb14f7)
#1 0x117d343 in chunk_destroy include/haproxy/chunk.h:257
#2 0x1188ef8 in vars_check_arg src/vars.c:843
#3 0x118ac23 in conv_check_var src/vars.c:1151
#4 0x9c38d6 in sample_parse_expr_cnv src/sample.c:1191
#5 0x16e4238 in parse_acl_expr src/acl.c:219
#6 0x16e709f in parse_acl src/acl.c:671
#7 0x16e829d in parse_acl_cond src/acl.c:920
#8 0x16e88f5 in build_acl_cond src/acl.c:1021
#9 0x1711c53 in parse_http_req_cond src/http_rules.c:149
#10 0xc95a54 in cfg_parse_listen src/cfgparse-listen.c:1385
#11 0xa11cf9 in parse_cfg src/cfgparse.c:2760
#12 0xc136c6 in read_cfg src/haproxy.c:1078
#13 0xc1eed5 in main src/haproxy.c:3373
#14 0x7fc5320dc03c in __libc_start_main (/lib64/libc.so.6+0x2403c)
previously allocated by thread T0 here:
#0 0x7fc5328b17ef in __interceptor_malloc (/usr/lib64/libasan.so.6+0xb17ef)
#1 0x95d40d in my_strndup src/tools.c:3033
#2 0x1826c09 in make_arg_list src/arg.c:262
#3 0x9c369c in sample_parse_expr_cnv src/sample.c:1177
#4 0x16e4238 in parse_acl_expr src/acl.c:219
#5 0x16e709f in parse_acl src/acl.c:671
#6 0x16e829d in parse_acl_cond src/acl.c:920
#7 0x16e88f5 in build_acl_cond src/acl.c:1021
#8 0x1711c53 in parse_http_req_cond src/http_rules.c:149
#9 0xc95a54 in cfg_parse_listen src/cfgparse-listen.c:1385
#10 0xa11cf9 in parse_cfg src/cfgparse.c:2760
#11 0xc136c6 in read_cfg src/haproxy.c:1078
#12 0xc1eed5 in main src/haproxy.c:3373
#13 0x7fc5320dc03c in __libc_start_main (/lib64/libc.so.6+0x2403c)
SUMMARY: AddressSanitizer: heap-use-after-free src/tools.c:3030 in my_strndup
Shadow bytes around the buggy address:
0x0c047fff82b0: fa fa 00 04 fa fa 00 03 fa fa 00 03 fa fa 00 06
0x0c047fff82c0: fa fa 00 03 fa fa 00 fa fa fa 00 03 fa fa 07 fa
0x0c047fff82d0: fa fa 00 00 fa fa 00 05 fa fa 00 05 fa fa 00 04
0x0c047fff82e0: fa fa 00 04 fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff82f0: fa fa 00 fa fa fa 06 fa fa fa fd fa fa fa fd fa
=>0x0c047fff8300: fa fa fd fa fa fa[fd]fd fa fa 04 fa fa fa 01 fa
0x0c047fff8310: fa fa 00 05 fa fa 00 03 fa fa 00 05 fa fa 00 06
0x0c047fff8320: fa fa 00 05 fa fa 00 03 fa fa fd fd fa fa fd fd
0x0c047fff8330: fa fa fd fd fa fa fd fa fa fa 00 00 fa fa 02 fa
0x0c047fff8340: fa fa 00 01 fa fa 00 00 fa fa 00 02 fa fa 00 fa
0x0c047fff8350: fa fa 05 fa fa fa 03 fa fa fa fd fd fa fa fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==547==ABORTING
I could get a core dump this way:
$ ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1
./haproxy -db -f crash-var-asan.cfg
And the backtrace is:
(gdb) bt
#0 0x00007f4f77cf3868 in raise () from /lib64/libc.so.6
#1 0x00007f4f77cda546 in abort () from /lib64/libc.so.6
#2 0x00007f4f784cd4bf in ?? () from /usr/lib64/libasan.so.6
#3 0x00007f4f784d881c in ?? () from /usr/lib64/libasan.so.6
#4 0x00007f4f784b9c6c in ?? () from /usr/lib64/libasan.so.6
#5 0x00007f4f784b9536 in ?? () from /usr/lib64/libasan.so.6
#6 0x00007f4f784b9fdc in __asan_report_load1 () from /usr/lib64/libasan.so.6
#7 0x000000000095d3f7 in my_strndup (src=0x602000001835 "", n=3) at
src/tools.c:3030
#8 0x000000000118766f in var_set (desc=0x6120000013c8, smp=0x7ffc6c5decd0,
flags=0) at src/vars.c:617
#9 0x0000000001188a0e in smp_conv_store (args=0x6120000013c0,
smp=0x7ffc6c5decd0, private=0x0) at src/vars.c:798
#10 0x00000000009c462d in sample_process_cnv (expr=0x60400002f350,
p=0x7ffc6c5decd0) at src/sample.c:1337
#11 0x00000000009c47ee in sample_process (px=0x61e000002480,
sess=0x610000005540, strm=0x61900006ae80, opt=6,
expr=0x60400002f350, p=0x7ffc6c5decd0) at src/sample.c:1380
#12 0x00000000016e8c68 in acl_exec_cond (cond=0x606000007400,
px=0x61e000002480, sess=0x610000005540,
strm=0x61900006ae80, opt=6) at src/acl.c:1090
#13 0x00000000008b9431 in acl_match_cond (cond=0x606000007400,
px=0x61e000002480, sess=0x610000005540,
strm=0x61900006ae80, opt=2) at include/haproxy/acl.h:120
#14 0x00000000008e2d71 in http_req_get_intercept_rule (px=0x61e000002480,
def_rules=0x0, rules=0x61e0000024e8,
s=0x61900006ae80) at src/http_ana.c:2848
#15 0x00000000008cb03b in http_process_req_common (s=0x61900006ae80,
req=0x61900006aea8, an_bit=16,
px=0x61e000002480) at src/http_ana.c:413
#16 0x0000000000832bb8 in process_stream (t=0x61100006b840,
context=0x61900006ae80, state=260) at src/stream.c:2138
#17 0x00000000016370ab in run_tasks_from_lists (budgets=0x7ffc6c5df5d0) at
src/task.c:660
#18 0x0000000001639a70 in process_runnable_tasks () at src/task.c:902
#19 0x0000000000c1cea2 in run_poll_loop () at src/haproxy.c:2893
#20 0x0000000000c1de08 in run_thread_poll_loop (data=0x2547540
<ha_thread_info>) at src/haproxy.c:3121
#21 0x0000000000c2053b in main (argc=4, argv=0x7ffc6c5df9d8) at
src/haproxy.c:3747
There's apparently a use-after-free on a freshly released variable.
Maybe we're freeing too early in certain cases. If you want to
reproduce it, I'm enabling ASAN by appending
ARCH_FLAGS="-g -fsanitize=address" to the "make" command line.
Hoping this helps,
Willy