Skip to content

Fix StubReader.ReadByte() discarding valid byte on EOF#98

Open
hdrover wants to merge 1 commit intoSagerNet:devfrom
hdrover:fix/stubreader-readbyte
Open

Fix StubReader.ReadByte() discarding valid byte on EOF#98
hdrover wants to merge 1 commit intoSagerNet:devfrom
hdrover:fix/stubreader-readbyte

Conversation

@hdrover
Copy link
Copy Markdown

@hdrover hdrover commented Mar 23, 2026

StubReader.ReadByte() ignores the n return value from Read().

When Read() returns (n=1, err=io.EOF), which is valid per the io.Reader contract, ReadByte() returns (byte, EOF) instead of (byte, nil). Callers see err != nil and discard the successfully read byte.

This causes readString() in libbox/profile_import.go to fail with unexpected EOF when decoding .bpf profile files if the gzip stream delivers the last byte together with EOF.

Fix: replace r.Read() with io.ReadFull(), which returns nil when the requested number of bytes has been fully read, even if the underlying Read() returned EOF alongside the data.

This became a visible regression in sing-box v1.13.0 after commit SagerNet/sing-box@1af14a0 ("Remove varbin usages"), which changed string reading from io.ReadFull() (tolerant to data+EOF) to a byte-by-byte ReadByte() loop, exposing this bug. In v1.12.x profile import worked correctly.

Screenshot: sing-box Android app failing to import a valid .bpf file generated with standard gzip (without sync flush).

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.

1 participant