Repository: caddyserver/caddy (language: go)

Issue:
 fuzz-format: Timeout in fuzz-format
Detailed Report: https://oss-fuzz.com/testcase?key=5806400649363456

Project: caddy
Fuzzing Engine: libFuzzer
Fuzz Target: fuzz-format
Job Type: libfuzzer_asan_caddy
Platform Id: linux

Crash Type: Timeout (exceeds 60 secs)
Crash Address: 
Crash State:  fuzz-format
  
Sanitizer: address (ASAN)

Regressed: https://oss-fuzz.com/revisions?job=libfuzzer_asan_caddy&range=202401210622:202401220608

Reproducer Testcase: https://oss-fuzz.com/download?testcase_id=5806400649363456

Issue on oss-fuzz tracker: [Issue 66641](https://crbug.com/oss-fuzz/66641)

Minimized reproducer test case: [clusterfuzz-testcase-minimized-fuzz-format-5806400649363456.txt](https://github.com/caddyserver/caddy/files/14233558/clusterfuzz-testcase-minimized-fuzz-format-5806400649363456.txt)


Discussion / hints:

Fix patch:
diff --git a/caddyconfig/caddyfile/formatter.go b/caddyconfig/caddyfile/formatter.go
index 764f7911802..423de542a22 100644
--- a/caddyconfig/caddyfile/formatter.go
+++ b/caddyconfig/caddyfile/formatter.go
@@ -16,6 +16,7 @@ package caddyfile
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"unicode"
 
@@ -118,6 +119,10 @@ func Format(input []byte) []byte {
 				heredoc = heredocClosed
 			} else {
 				heredocMarker = append(heredocMarker, ch)
+				if len(heredocMarker) > 32 {
+					errorString := fmt.Sprintf("heredoc marker too long: <<%s", string(heredocMarker))
+					panic(errorString)
+				}
 				write(ch)
 				continue
 			}
diff --git a/caddyconfig/caddyfile/lexer.go b/caddyconfig/caddyfile/lexer.go
index 4db63749b5b..a59f0fc46de 100644
--- a/caddyconfig/caddyfile/lexer.go
+++ b/caddyconfig/caddyfile/lexer.go
@@ -149,6 +149,10 @@ func (l *lexer) next() (bool, error) {
 				continue
 			}
 
+			if len(val) > 32 {
+				return false, fmt.Errorf("heredoc marker too long on line #%d: %s", l.line, string(val))
+			}
+
 			// after hitting a newline, we know that the heredoc marker
 			// is the characters after the two << and the newline.
 			// we reset the val because the heredoc is syntax we don't


Test patch:
diff --git a/caddyconfig/caddyfile/formatter_test.go b/caddyconfig/caddyfile/formatter_test.go
index 6eec822fe59..5ea29c33576 100644
--- a/caddyconfig/caddyfile/formatter_test.go
+++ b/caddyconfig/caddyfile/formatter_test.go
@@ -15,6 +15,8 @@
 package caddyfile
 
 import (
+	"fmt"
+	"os"
 	"strings"
 	"testing"
 )
@@ -24,6 +26,7 @@ func TestFormatter(t *testing.T) {
 		description string
 		input       string
 		expect      string
+		panics      bool
 	}{
 		{
 			description: "very simple",
@@ -434,18 +437,36 @@ block2 {
 }
 `,
 		},
+		{
+			description: "very long heredoc from fuzzer",
+			input: func() string {
+				bs, _ := os.ReadFile("testdata/clusterfuzz-testcase-minimized-fuzz-format-5806400649363456")
+				return string(bs)
+			}(),
+			panics: true,
+		},
 	} {
-		// the formatter should output a trailing newline,
-		// even if the tests aren't written to expect that
-		if !strings.HasSuffix(tc.expect, "\n") {
-			tc.expect += "\n"
-		}
-
-		actual := Format([]byte(tc.input))
-
-		if string(actual) != tc.expect {
-			t.Errorf("\n[TEST %d: %s]\n====== EXPECTED ======\n%s\n====== ACTUAL ======\n%s^^^^^^^^^^^^^^^^^^^^^",
-				i, tc.description, string(tc.expect), string(actual))
-		}
+		t.Run(fmt.Sprintf("test case %d: %s", i, tc.description), func(t *testing.T) {
+			if tc.panics {
+				defer func() {
+					if r := recover(); r == nil {
+						t.Errorf("[TEST %d: %s] Expected panic, but got none", i, tc.description)
+					}
+				}()
+			}
+
+			// the formatter should output a trailing newline,
+			// even if the tests aren't written to expect that
+			if !strings.HasSuffix(tc.expect, "\n") {
+				tc.expect += "\n"
+			}
+
+			actual := Format([]byte(tc.input))
+
+			if !tc.panics && string(actual) != tc.expect {
+				t.Errorf("\n[TEST %d: %s]\n====== EXPECTED ======\n%s\n====== ACTUAL ======\n%s^^^^^^^^^^^^^^^^^^^^^",
+					i, tc.description, string(tc.expect), string(actual))
+			}
+		})
 	}
 }
diff --git a/caddyconfig/caddyfile/testdata/clusterfuzz-testcase-minimized-fuzz-format-5806400649363456 b/caddyconfig/caddyfile/testdata/clusterfuzz-testcase-minimized-fuzz-format-5806400649363456
new file mode 100644
index 00000000000..94b70919c4b
Binary files /dev/null and b/caddyconfig/caddyfile/testdata/clusterfuzz-testcase-minimized-fuzz-format-5806400649363456 differ