Skip to content

[Proto] fix: negative signed integers corrupt varint encoding (#917) - #918

Open
Deliay wants to merge 1 commit into
LagrangeDev:masterfrom
Deliay:fix/proto-negative-varint
Open

[Proto] fix: negative signed integers corrupt varint encoding (#917)#918
Deliay wants to merge 1 commit into
LagrangeDev:masterfrom
Deliay:fix/proto-negative-varint

Conversation

@Deliay

@Deliay Deliay commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

关联 Issue

Fixes #917

问题现象

含有符号整型字段(如 int Type = -1)的对象经过 ProtoSerializer.SerializeDeserialize 往返后解析失败,抛出 ArgumentOutOfRangeExceptionCreateSpan/SkipField 越界)等异常。

调查结果

通过编写单元测试复现后,定位到两处问题:

根因:ProtoWriter.EncodeVarInt<T> 单字节快路径误判(Lagrange.Proto/Primitives/ProtoWriter.cs

if (value < T.CreateTruncating(0x80))  // 有符号负数恒为真,如 int -1 < 0x80

负数会错误进入单字节快路径,被 byte.CreateTruncating(value) 截断为 0xFF 写入——这是一个 continuation bit 置位的非法 varint 起始字节,导致整个流的后续解析全部错乱。

关联隐患:ProtoHelper.GetVarIntLength 长度计算数组越界(Lagrange.Proto/Utility/ProtoHelper.cs

长度计算使用 uint/ulong.CreateSaturating(value),负数被钳到 0,LeadingZeroCount(0) = 32(或 64),导致 VarIntLengths32[32] 数组越界抛 IndexOutOfRangeException。嵌套对象序列化的 Measure 路径(ProtoSerializableConverter.WriteMeasureHandler)会稳定触发,已用嵌套对象的测试用例验证。

修复方案

  • ProtoWriter.EncodeVarInt<T>:快路径条件改为 ulong.CreateTruncating(value) < 0x80。负数转为无符号位模式后必然 ≥ 0x80,走正常多字节编码路径(int -1 → 5 字节 varint FF FF FF FF 0Flong -1 → 10 字节),解码端按截断语义可正确还原。
  • ProtoHelper.GetVarIntLengthCreateSaturatingCreateTruncating,负数按位模式计算前导零,得到与实际编码一致的长度(32 位负数 → 5,64 位负数 → 10)。

注:本库对负 int32 编码为 5 字节 varint 而非 protobuf 规范的 10 字节符号扩展形式,但解码端为截断读取,两种形式均可正确还原,库内自洽,属于既有设计,本次未改动。

测试

新增 Lagrange.Proto.Test/NegativeVarIntTest.cs,共 4 个用例:

  • TestNegativeInt_Roundtrip_Reflection:issue 原始复现场景(反射路径 Serialize/Deserialize
  • TestNegativeInt_Roundtrip_SourceGenerated:同场景走源生成路径(SerializeProtoPackable
  • TestNegativeInt_NestedObject_Roundtrip:嵌套对象含负数字段(覆盖 Measure/GetVarIntLength 路径)
  • TestNegativeValues_Boundarysbyte/short/int/long-1MinValue 边界值往返

修复前 4 个用例全部失败(复现成功),修复后全部通过;Lagrange.Proto.Test 完整套件 221 个测试全部通过,无回归。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug?]: Lagrange.Proto 序列化失败

1 participant