In the health-check context, smp_prefetch_htx() unconditionally treats
<check->bi> as an HTX message:
if (!s || !chn) {
if (check) {
htx = htxbuf(&check->bi);
This is only true for an HTTP check, where the h1 mux fills the buffer with
HTX blocks. For any other check ruleset (plain "tcp-check", or the default TCP
connect check), the buffer holds the raw bytes received from the server.
All the response-side HTTP sample fetches (res.body, res.hdr, res.hdrs,
res.ver, status, res.cook...) are declared with SMP_SRC_HRSHV/HRSHP/HRSBO and
the validity table in sample.c makes them usable at the SMP_CKP_BE_CHK_RUL
checkpoint, so referencing one of them from a "tcp-check" rule (typically in an
"on-error" log-format string, or in a "status-code" expression) is accepted at
boot without any warning. When the check then runs, the first bytes of the
server's answer are used as "struct htx" fields: ->size, ->head, ->tail and
->first are entirely provided by the peer. htx_get_first_blk() computes
"htx->blocks + htx->size - (first + 1) * sizeof(struct htx_blk)" and the
resulting block is dereferenced, which is a wild read that crashes the worker,
and which may otherwise return arbitrary process memory as the fetched value.
This can be reproduced with a backend using:
option tcp-check
tcp-check connect
tcp-check expect string ZZZZ on-error "body=%[res.body]"
and a server answering with 24 bytes crafted as a "struct htx" header with a
large ->size (e.g. 0x40000000), ->head = 0, ->tail = 1 and ->first = 0,
followed by any padding: the worker segfaults at the first check.
Let's simply refuse to look at the buffer when the check is not relying on
HTX. HTTP checks are unaffected, the fetches now simply return no sample on
other checks.
Note that this requires a hostile server, which is a trusted component for a
reverse proxy, so this is not a security issue, but the parser must not be fed
a buffer whose format it cannot assume.
This should be backported to all supported versions.
BUG_ON(check && (s || chn));
if (!s || !chn) {
if (check) {
+ /* The check input buffer only contains an HTX message for
+ * an HTTP check.
+ */
+ if (!IS_HTX_SC(check->sc))
+ return NULL;
+
htx = htxbuf(&check->bi);
/* Analyse not yet started */