plugin/hosts: don't drop entries after an over-long line (#8496)

bufio.Scanner stops at the first line longer than its 64KiB default
buffer and reports bufio.ErrTooLong from Err(). parse() never checked
Err(), so that line and every entry after it were dropped silently: the
hosts file simply looked shorter than it is, with nothing in the log.

Raise the scanner's limit to 1MiB (the scanner still grows its buffer
lazily, so nothing is preallocated up front) and log an error if the
scan does stop early, so the truncation is at least visible.

Signed-off-by: Paco Cartones <pacocartones@users.noreply.github.com>
Co-authored-by: Paco Cartones <pacocartones@users.noreply.github.com>
This commit is contained in:
Paco Cartones
2026-09-01 09:01:04 +02:00
committed by GitHub
parent ac796cd723
commit 85aa27cd9c
2 changed files with 29 additions and 0 deletions

View File

@@ -164,11 +164,20 @@ func (h *Hostsfile) initInline(inline []string) {
h.inline = h.parse(strings.NewReader(strings.Join(inline, "\n")))
}
// maxLineSize is the largest hosts file line we are willing to parse. A line
// can legitimately be long when many names share a single address, so this is
// well above bufio.Scanner's 64KiB default, but still bounded.
const maxLineSize = 1024 * 1024
// Parse reads the hostsfile and populates the byName and addr maps.
func (h *Hostsfile) parse(r io.Reader) *Map {
hmap := newMap()
scanner := bufio.NewScanner(r)
// The scanner grows its buffer as needed; only raise the limit at which it
// gives up, otherwise a single long line aborts the scan and every entry
// after it is dropped.
scanner.Buffer(nil, maxLineSize)
for scanner.Scan() {
line := scanner.Bytes()
if i := bytes.Index(line, []byte{'#'}); i >= 0 {
@@ -220,6 +229,10 @@ func (h *Hostsfile) parse(r io.Reader) *Map {
hmap.addr[addr.String()] = append(hmap.addr[addr.String()], name)
}
}
if err := scanner.Err(); err != nil {
// Entries after the failing line have not been read.
log.Errorf("Failed to parse hosts file %q: %v", h.path, err)
}
return hmap
}

View File

@@ -290,3 +290,19 @@ func TestLookupStaticHostReloadRace(t *testing.T) {
wg.Wait()
}
func TestParseLineLongerThanDefaultScanBuffer(t *testing.T) {
// A line longer than bufio.Scanner's default 64KiB buffer must not stop
// the scan: the entries that follow it still have to be parsed.
long := strings.Repeat("a", 70*1024)
h := testHostsfile("127.0.0.1 before.example.org\n" +
"127.0.0.2 " + long + ".example.org\n" +
"127.0.0.3 after.example.org\n")
if addrs := h.LookupStaticHostV4("before.example.org."); len(addrs) != 1 || addrs[0].String() != "127.0.0.1" {
t.Errorf("LookupStaticHostV4(before.example.org.) = %v, want [127.0.0.1]", addrs)
}
if addrs := h.LookupStaticHostV4("after.example.org."); len(addrs) != 1 || addrs[0].String() != "127.0.0.3" {
t.Errorf("LookupStaticHostV4(after.example.org.) = %v, want [127.0.0.3]", addrs)
}
}