From 1712192b2ee7256d0e4ddd85052a16373280a163 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sat, 16 May 2026 00:19:29 +0300 Subject: [PATCH] wire: bounds-check skip_field for I32/I64/LEN truncation skip_field advanced pos by a fixed or length-prefixed amount with no check against #buf, so a truncated unknown field was silently consumed: the outer decode loop's `while pos <= len` exited without raising, making the parser accept payloads it should have rejected. Validates that the new position never exceeds #buf+1 for WIRE_I64, WIRE_I32, and both WIRE_LEN fast and slow paths. WIRE_VARINT already errored correctly via decode_varint's per-byte check. Drops 15 entries from test/conformance/known_failures.txt (PrematureEofBeforeUnknownValue.*, PrematureEofInsideUnknownValue.*, PrematureEofInDelimitedDataForUnknownValue.*). --- runtime/pb/wire.lua | 18 ++++++++++++++---- test/conformance/known_failures.txt | 15 --------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/runtime/pb/wire.lua b/runtime/pb/wire.lua index a6150a66aa4ecc5b0c98d604106b497b0c73ceda..7214f32381765b5cd4862059a06dec3dc62a2a2e 100644 --- a/runtime/pb/wire.lua +++ b/runtime/pb/wire.lua @@ -446,15 +446,25 @@ local function skip_field(buf, pos, wire_type) local _, npos = decode_varint(buf, pos) return npos elseif wire_type == M.WIRE_I64 then - return pos + 8 + local np = pos + 8 + if np > #buf + 1 then error("truncated I64 at offset " .. pos, 0) end + return np elseif wire_type == M.WIRE_LEN then local b = buf:byte(pos) if b == nil then error("truncated varint at offset " .. pos, 0) end - if b < 0x80 then return pos + 1 + b end + if b < 0x80 then + local np = pos + 1 + b + if np > #buf + 1 then error("truncated LEN at offset " .. pos, 0) end + return np + end local len, npos = decode_varint(buf, pos) - return npos + tonumber(len) + local np = npos + tonumber(len) + if np > #buf + 1 then error("truncated LEN at offset " .. pos, 0) end + return np elseif wire_type == M.WIRE_I32 then - return pos + 4 + local np = pos + 4 + if np > #buf + 1 then error("truncated I32 at offset " .. pos, 0) end + return np end error("unknown wire type " .. tostring(wire_type), 0) end diff --git a/test/conformance/known_failures.txt b/test/conformance/known_failures.txt index 288c8fc1a3e49a6f6e7582977c260e9af71232da..81377d55fb2bbe3be55c10d6378407101e1034e4 100644 --- a/test/conformance/known_failures.txt +++ b/test/conformance/known_failures.txt @@ -54,21 +54,6 @@ Required.Proto3.ProtobufInput.BadTag_OverlongVarint Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_0 Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_1 Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_3 -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.DOUBLE -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.FIXED32 -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.FIXED64 -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.FLOAT -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED32 -Required.Proto3.ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED64 -Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.BYTES -Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.MESSAGE -Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.STRING -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.DOUBLE -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.FIXED32 -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.FIXED64 -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.FLOAT -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.SFIXED32 -Required.Proto3.ProtobufInput.PrematureEofInsideUnknownValue.SFIXED64 Required.Proto3.ProtobufInput.RepeatedScalarMessageMerge.ProtobufOutput Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.Merge.ProtobufOutput Required.Proto3.TimestampProtoNegativeNanos.JsonOutput