When parsing a logformat expression using parse_logformat_string(), the caller passes the proxy under which the expression is found as argument. This information allows the logformat expression API to check if the expression is compatible with the proxy settings. Since 7a21c3a ("MAJOR: log: implement proper postparsing for logformat expressions"), the proxy compatibilty checks are postponed after the proxy is fully parsed to ensure proxy properties are fully resolved for checks consistency. The way it works, is that each time parse_logformat_string() is called for a given expression and proxy, it schedules the expression for postchecking by appending the expression to the list of pending expression checks on the proxy (lf_checks struct). Then, when the proxy is called with the REGISTER_POST_PROXY_CHECK() hook, it iterates over unchecked expressions and performs the check, then it removes the expression from its list. However, I overlooked a special case: if a logformat expression is used on a proxy that is disabled or a default proxy: REGISTER_POST_PROXY_CHECK() hook is never called. Because of that, lf expressions may still point to the proxy after the proxy is freed. For most logformat expressions, this isn't an issue because they are stored within the proxy itself, but this isn't the case with {tcp,http}checks logformat expressions: during deinit() sequence, all proxies are first cleaned up, and only then shared checks are freed. Because of that, the below config will trigger UAF since 7a21c3a: uaf.conf: listen dummy bind localhost:2222 backend testback disabled mode http option httpchk http-check send hdr test "test" http-check expect status 200 haproxy -f uaf.conf -c: ==152096== Invalid write of size 8 ==152096== at 0x21C317: lf_expr_deinit (log.c:3491) ==152096== by 0x2334A3: free_tcpcheck_http_hdr (tcpcheck.c:84) ==152096== by 0x2334A3: free_tcpcheck_http_hdr (tcpcheck.c:79) ==152096== by 0x2334A3: free_tcpcheck_http_hdrs (tcpcheck.c:98) ==152096== by 0x23365A: free_tcpcheck.part.0 (tcpcheck.c:130) ==152096== by 0x2338B1: free_tcpcheck (tcpcheck.c:108) ==152096== by 0x2338B1: deinit_tcpchecks (tcpcheck.c:3780) ==152096== by 0x2CF9A4: deinit (haproxy.c:2949) ==152096== by 0x2D0065: deinit_and_exit (haproxy.c:3052) ==152096== by 0x169BC0: main (haproxy.c:3996) ==152096== Address 0x52a8df8 is 6,968 bytes inside a block of size 7,168 free'd ==152096== at 0x484B27F: free (vg_replace_malloc.c:872) ==152096== by 0x2CF8AD: deinit (haproxy.c:2906) ==152096== by 0x2D0065: deinit_and_exit (haproxy.c:3052) ==152096== by 0x169BC0: main (haproxy.c:3996) To fix the issue, let's ensure in proxy_free_common() that no unchecked expressions may still point to the proxy after the proxy is freed by purging the list (DEL_INIT is used to reset list items). Special thanks to GH user @mhameed who filed a comprehensive issue with all the relevant information required to reproduce the bug (see GH #2597), after having first reported the issue on the alpine project bug tracker.
HAProxy
HAProxy is a free, very fast and reliable reverse-proxy offering high availability, load balancing, and proxying for TCP and HTTP-based applications.
Installation
The INSTALL file describes how to build HAProxy. A list of packages is also available on the wiki.
Getting help
The discourse and the mailing-list are available for questions or configuration assistance. You can also use the slack or IRC channel. Please don't use the issue tracker for these.
The issue tracker is only for bug reports or feature requests.
Documentation
The HAProxy documentation has been split into a number of different files for ease of use. It is available in text format as well as HTML. The wiki is also meant to replace the old architecture guide.
Please refer to the following files depending on what you're looking for:
- INSTALL for instructions on how to build and install HAProxy
- BRANCHES to understand the project's life cycle and what version to use
- LICENSE for the project's license
- CONTRIBUTING for the process to follow to submit contributions
The more detailed documentation is located into the doc/ directory:
- doc/intro.txt for a quick introduction on HAProxy
- doc/configuration.txt for the configuration's reference manual
- doc/lua.txt for the Lua's reference manual
- doc/SPOE.txt for how to use the SPOE engine
- doc/network-namespaces.txt for how to use network namespaces under Linux
- doc/management.txt for the management guide
- doc/regression-testing.txt for how to use the regression testing suite
- doc/peers.txt for the peers protocol reference
- doc/coding-style.txt for how to adopt HAProxy's coding style
- doc/internals for developer-specific documentation (not all up to date)
License
HAProxy is licensed under GPL 2 or any later version, the headers under LGPL 2.1. See the LICENSE file for a more detailed explanation.