From: 胡玮文 Date: Sun, 23 Nov 2025 18:29:24 +0000 (+0800) Subject: internal/runtime/cgroup: lineReader fuzz test X-Git-Tag: go1.26rc1~29 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=67851547d8;p=gostls13.git internal/runtime/cgroup: lineReader fuzz test The original unit test is converted to a fuzz test, to be more confident on future refactors. All sub-tests are converted to seed corpus. Change-Id: Id0c167ed47729a00ea0614d17746ddcc284697d3 Reviewed-on: https://go-review.googlesource.com/c/go/+/723581 Auto-Submit: Michael Pratt LUCI-TryBot-Result: Go LUCI Reviewed-by: Dmitri Shuralyov Reviewed-by: Michael Pratt --- diff --git a/src/internal/runtime/cgroup/line_reader_test.go b/src/internal/runtime/cgroup/line_reader_test.go index ceef1b5b4c..6095dd0481 100644 --- a/src/internal/runtime/cgroup/line_reader_test.go +++ b/src/internal/runtime/cgroup/line_reader_test.go @@ -11,131 +11,134 @@ import ( "testing" ) -func TestLineReader(t *testing.T) { - type nextLine struct { - line string - incomplete bool // next call before this line should return incomplete - } - complete := func(s string) nextLine { - return nextLine{line: s} - } - incomplete := func(s string) nextLine { - return nextLine{line: s, incomplete: true} - } +type nextLine struct { + line string + incomplete bool // next call before this line should return incomplete +} - const scratchSize = 8 +func complete(s string) nextLine { + return nextLine{line: s} +} +func incomplete(s string) nextLine { + return nextLine{line: s, incomplete: true} +} - tests := []struct { - name string - contents string - want []nextLine - }{ - { - name: "empty", - contents: "", - }, - { - name: "single", - contents: "1234\n", - want: []nextLine{ - complete("1234"), - }, +const scratchSize = 8 + +var readerTests = []struct { + name string + contents string + want []nextLine +}{ + { + name: "empty", + contents: "", + }, + { + name: "single", + contents: "1234\n", + want: []nextLine{ + complete("1234"), }, - { - name: "single-incomplete", - contents: "1234", - want: []nextLine{ - incomplete("1234"), - }, + }, + { + name: "single-incomplete", + contents: "1234", + want: []nextLine{ + incomplete("1234"), }, - { - name: "single-exact", - contents: "1234567\n", - want: []nextLine{ - complete("1234567"), - }, + }, + { + name: "single-exact", + contents: "1234567\n", + want: []nextLine{ + complete("1234567"), }, - { - name: "single-exact-incomplete", - contents: "12345678", - want: []nextLine{ - incomplete("12345678"), - }, + }, + { + name: "single-exact-incomplete", + contents: "12345678", + want: []nextLine{ + incomplete("12345678"), }, - { - name: "multi", - contents: `1234 + }, + { + name: "multi", + contents: `1234 5678 `, - want: []nextLine{ - complete("1234"), - complete("5678"), - }, + want: []nextLine{ + complete("1234"), + complete("5678"), }, - { - name: "multi-short", - contents: `12 + }, + { + name: "multi-short", + contents: `12 34 56 78 `, - want: []nextLine{ - complete("12"), - complete("34"), - complete("56"), - complete("78"), - }, + want: []nextLine{ + complete("12"), + complete("34"), + complete("56"), + complete("78"), }, - { - name: "multi-notrailingnewline", - contents: `1234 + }, + { + name: "multi-notrailingnewline", + contents: `1234 5678`, - want: []nextLine{ - complete("1234"), - incomplete("5678"), - }, + want: []nextLine{ + complete("1234"), + incomplete("5678"), }, - { - name: "middle-too-long", - contents: `1234 + }, + { + name: "middle-too-long", + contents: `1234 1234567890 5678 `, - want: []nextLine{ - complete("1234"), - incomplete("12345678"), - complete("5678"), - }, + want: []nextLine{ + complete("1234"), + incomplete("12345678"), + complete("5678"), }, - { - // Multiple reads required to find newline. - name: "middle-way-too-long", - contents: `1234 + }, + { + // Multiple reads required to find newline. + name: "middle-way-too-long", + contents: `1234 12345678900000000000000000000000000000000000000000000000000 5678 `, - want: []nextLine{ - complete("1234"), - incomplete("12345678"), - complete("5678"), - }, + want: []nextLine{ + complete("1234"), + incomplete("12345678"), + complete("5678"), }, + }, +} + +func readString(contents string) func(fd int, b []byte) (int, uintptr) { + r := strings.NewReader(contents) + return func(fd int, b []byte) (int, uintptr) { + n, err := r.Read(b) + if err != nil && err != io.EOF { + const dummyErrno = 42 + return n, dummyErrno + } + return n, 0 } +} - for _, tc := range tests { +func TestLineReader(t *testing.T) { + for _, tc := range readerTests { t.Run(tc.name, func(t *testing.T) { - r := strings.NewReader(tc.contents) - read := func(fd int, b []byte) (int, uintptr) { - n, err := r.Read(b) - if err != nil && err != io.EOF { - const dummyErrno = 42 - return n, dummyErrno - } - return n, 0 - } - var scratch [scratchSize]byte - l := cgroup.NewLineReader(0, scratch[:], read) + l := cgroup.NewLineReader(0, scratch[:], readString(tc.contents)) var got []nextLine for { @@ -168,3 +171,39 @@ func TestLineReader(t *testing.T) { }) } } + +func FuzzLineReader(f *testing.F) { + for _, tc := range readerTests { + f.Add(tc.contents) + } + f.Fuzz(func(t *testing.T, input string) { + scratch := make([]byte, scratchSize) + reader := cgroup.NewLineReader(0, scratch, readString(input)) + for expected := range strings.Lines(input) { + err := reader.Next() + line := reader.Line() + + var expectedErr error + if len(expected) > scratchSize { + expected = expected[:scratchSize] + expectedErr = cgroup.ErrIncompleteLine + } else if expected[len(expected)-1] == '\n' { + expected = expected[:len(expected)-1] + } else { + expectedErr = cgroup.ErrIncompleteLine + } + + if err != expectedErr { + t.Fatalf("got err %v, want %v", err, expectedErr) + } + + if string(line) != expected { + t.Fatalf("got %q, want %q", string(line), expected) + } + } + err := reader.Next() + if err != cgroup.ErrEOF { + t.Fatalf("got %v, want EOF", err) + } + }) +}