Hi,
Fred suggested I send this here. It is the regression test for 3a319e14c
("BUG/MINOR: sample: realign the ungrpc converter on message boundaries"),
which he merged this morning.
First, a correction to something I told him off-list: I said
reg-tests/protobuf
only contained nested_and_flat.vtc and did not exercise ungrpc at all. That
was
wrong. grpc.vtc has been there since 2026-09-22 and covers the converter
thoroughly, including a body of two gRPC messages. I should have looked
before
saying so.
Having now built both revisions and run them, the useful part is that
grpc.vtc
does not catch this particular bug:
reg-tests/protobuf/grpc.vtc on 3a319e14c PASS
on 1f114e4c4 PASS <- bug not caught
reg-tests/protobuf/ungrpc_realign.vtc (new)
on 3a319e14c PASS
on 1f114e4c4 FAIL <- returns 1337
The reason is that grpc.vtc's two-message body (frontend fe3) asks for a
field
the lookup finds in the first message, so the cursor is left at the message
end
and the misalignment never happens. Reaching it needs a lookup that fails
part-way through a message, which a length-delimited field whose wire type
does
not match the requested one produces.
The attached test uses the 23-byte body from your commit message, so a
failure
reports the concrete forged value rather than an empty sample. On the parent
commit it reports:
x-v: 1337
x-s: 000000000308B90A
x-m:
and 9999 on the fix.
Built with TARGET=linux-glibc and run with VTest2 at its "last" tag.
Regards,
Owais Lone
---
>From dfe2ee4a5b0dd41b4589670acd0ba988af9627d6 Mon Sep 17 00:00:00 2001
From: Owais Lone <[email protected]>
Date: Tue, 3 Nov 2026 10:29:01 +0000
Subject: [PATCH] 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).
---
reg-tests/protobuf/ungrpc_realign.vtc | 87 +++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 reg-tests/protobuf/ungrpc_realign.vtc
diff --git a/reg-tests/protobuf/ungrpc_realign.vtc
b/reg-tests/protobuf/ungrpc_realign.vtc
new file mode 100644
index 000000000..5e319e5b7
--- /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
+# <pos> and a counter <grpc_left>. When protobuf_field_lookup() fails it
does
+# not always leave <pos> 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 <grpc_left> without moving
<pos>,
+# 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
--
2.43.0