]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MAJOR: cfgparse: properly check userlist section when scanning keywords
authorWilly Tarreau <w@1wt.eu>
Fri, 9 Oct 2026 06:52:44 +0000 (08:52 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 Oct 2026 07:22:33 +0000 (09:22 +0200)
In cfg_parse_users(), we scan all config keywords and only want to
evaluate those whose section is CFG_USERLIST. However, the section
is checked as a bit field instead of an equality, and CFG_USERLIST
is 3, which is CFG_GLOBAL|CFG_LISTEN, meaning that we can call a
wrong parser for keywords "user" or "group" when they are registered
in one of these sections. This is what randomly happens when the
global "group" keyword is refactored to have its own parser, depending
on build options the linker, it may randomly be found before or after
the userlist's and cause startup failures if the global section's
keyword parser is called from the userlist parser, such as in this
regtest that only fails with ASAN on the CI when the config only has
"group" set in a userlist section:

  config : parsing [cfg:18] : cannot find group id for 'admins' (0:Success)

This was introduced in 3.4 with commit c17ed69bf3 ("MINOR: cfgparse:
Refactor "userlist" parser to print it in -dKall operation") so it must
be backported there.

If a config works it's OK, but this was still marked major because
there's simply no workaround for a failing binary where it only
depends on build options. It could happen for example if other
keywords are added via Lua or patches.

src/cfgparse.c

index bf5d46e39a6eb0a44c20c1a76446988dd3c5eacc..b5cae5af52a16437b41beee786917c7d0596455f 100644 (file)
@@ -791,8 +791,8 @@ cfg_parse_netns(const char *file, int linenum, char **args, int kwm)
 #endif
 }
 
-int
-cfg_parse_users(const char *file, int linenum, char **args, int kwm)
+/* parser for a "userlist" section */
+int cfg_parse_users(const char *file, int linenum, char **args, int kwm)
 {
 
        int err_code = 0;
@@ -851,7 +851,7 @@ cfg_parse_users(const char *file, int linenum, char **args, int kwm)
 
                list_for_each_entry(kwl, &cfg_keywords.list, list) {
                        for (index = 0; kwl->kw[index].kw; index++) {
-                               if ((kwl->kw[index].section & CFG_USERLIST) &&
+                               if ((kwl->kw[index].section == CFG_USERLIST) &&
                                        (strcmp(kwl->kw[index].kw, args[0]) == 0)) {
                                                err_code |= kwl->kw[index].parse(args, CFG_USERLIST, NULL, NULL, file, linenum, &errmsg);
                                                if (errmsg) {