From: Owais Lone Date: Tue, 3 Nov 2026 10:29:01 +0000 (+0000) Subject: REGTESTS: protobuf: cover the ungrpc message realignment X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=c923b3b4e407b0a587d1e926ecb8a1659ba38ed5;p=haproxy.git REGTESTS: protobuf: cover the ungrpc message realignment Regression test for 3a319e14c ("BUG/MINOR: sample: realign the ungrpc converter on message boundaries"). grpc.vtc already walks a body of two gRPC messages, but the lookup succeeds in its first message, so the cursor is left at the message end and the bug does not show: that file passes both before and after the fix. Reaching the defect needs a lookup that fails part-way through the first message, which a length-delimited field whose wire type does not match the requested one produces. The body is 23 bytes and holds field 1 twice: length-delimited in the first message, with a payload that reads as a gRPC header once the cursor is misaligned, and as a varint 9999 in the second one. ungrpc(1,int64) must return 9999. Before the fix it returned 1337, a field of no message of the body. Checked against 3a319e14c (passes) and its parent 1f114e4c4 (fails, returning 1337). --- diff --git a/reg-tests/protobuf/ungrpc_realign.vtc b/reg-tests/protobuf/ungrpc_realign.vtc new file mode 100644 index 000000000..d010a7e24 --- /dev/null +++ b/reg-tests/protobuf/ungrpc_realign.vtc @@ -0,0 +1,87 @@ +varnishtest "Test that ungrpc() realigns on gRPC message boundaries" + +# Regression test for: +# 3a319e14c BUG/MINOR: sample: realign the ungrpc converter on message boundaries +# +# sample_conv_ungrpc() walks the gRPC messages of the body with a read cursor +# and a counter . When protobuf_field_lookup() fails it does +# not always leave at the end of the message: for a length-delimited +# field it stops on the value, after the key and the length. The loop still +# subtracted the whole message length from without moving , +# so the next iteration decoded a "gRPC header" out of the previous message's +# payload. +# +# grpc.vtc already walks a body of two messages, but its first message is +# found by the lookup, so the cursor is left at the message end and the bug +# does not show. Reaching it needs a lookup that FAILS part-way through the +# first message, which is what the body below does. +# +# The body is 23 bytes: +# +# 00 0000000a gRPC header, message of 10 bytes +# 0a 08 field 1, length-delimited, 8 bytes +# 00 00 00 00 03 <- looks like a gRPC header once misaligned +# 08 b9 0a <- field 1, varint 1337 +# 00 00000003 gRPC header, message of 3 bytes +# 08 8f 4e field 1, varint 9999 +# +# ungrpc(1,int64) asks for field 1 as a varint. In the first message field 1 +# is length-delimited, so the wire types do not match and the lookup fails +# after the key and the length, leaving the cursor on the value. Before the +# fix the converter resumed there, read "00 00000003" out of the payload as a +# header and returned 1337, the value of a field of no message of the body. +# After the fix it resumes at the real message end and returns 9999, the +# field 1 of the second message. +# +# The 1337 payload is deliberately a valid encoding, so a failure here reports +# a concrete wrong value rather than an empty sample. + +feature ignore_unknown_macro + +haproxy h1 -conf { + global + .if feature(THREAD) + thread-groups 1 + .endif + chroot / + + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe + bind "fd@${fe}" + option http-buffer-request + http-request wait-for-body time "${HAPROXY_TEST_TIMEOUT-5s}" + + # field 1 of the second message, reached only by resuming on a real + # message boundary after the failed lookup in the first one + http-request set-var(txn.v) req.body,ungrpc(1,int64) + + # the same body, asking for the field with its real wire type: the + # lookup succeeds in the first message and never walks further + http-request set-var(txn.s) req.body,ungrpc(1),hex + + # a field present in neither message still yields nothing + http-request set-var(txn.m) req.body,ungrpc(4,int64) + + http-request return status 200 + + http-after-response set-header x-v "%[var(txn.v)]" + http-after-response set-header x-s "%[var(txn.s)]" + http-after-response set-header x-m "%[var(txn.m)]" +} -start + +client c1 -connect ${h1_fe_sock} { + txreq -req POST -url "/" -hdr "content-length: 23" -nolen + sendhex "00 0000000a 0a08 0000000003 08b90a" + sendhex "00 00000003 088f4e" + rxresp + expect resp.status == 200 + # 1337 here means the converter resumed inside the first message + expect resp.http.x-v == "9999" + expect resp.http.x-s == "000000000308B90A" + expect resp.http.x-m == "" +} -run