fix: reset skippedNodes in HandleMethodNotAllowed loop to prevent panic

Fixes #4818

When HandleMethodNotAllowed is enabled, the handleHTTPRequest method
loops through all method trees calling getValue() with the same
skippedNodes slice. Since getValue() appends to skippedNodes without
clearing it, and the slice cannot grow beyond its initial capacity
(engine.maxSections), accumulated entries from previous iterations
can cause a slice bounds panic when the reslice operation exceeds
capacity.

This fix resets skippedNodes to length 0 before each getValue() call
in the HandleMethodNotAllowed loop, preventing accumulation across
multiple method trees.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
liuyuyan2717 2026-08-29 17:54:34 +08:00
parent dcaa4296d1
commit c7a67900ae
2 changed files with 40 additions and 0 deletions

3
gin.go
View File

@ -743,6 +743,9 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
if tree.method == httpMethod {
continue
}
// Reset skippedNodes before each getValue call to prevent accumulation
// across multiple method trees, which could cause slice bounds panic
*c.skippedNodes = (*c.skippedNodes)[:0]
if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
allowed = append(allowed, tree.method)
}

37
gin_test_issue_4818.go Normal file
View File

@ -0,0 +1,37 @@
package gin
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestHandleMethodNotAllowedSkippedNodesPanic(t *testing.T) {
SetMode(ReleaseMode)
router := New()
router.HandleMethodNotAllowed = true
h := func(c *Context) {}
router.OPTIONS("/:p0/:p1/a/:p2", h)
router.GET("/:p0/:p1/a/:p2", h)
router.PATCH("/b/:p0/:p1/c", h)
router.DELETE("/b/:p0/:p1/d/:p3", h)
router.GET("/b/:p0/:p1/e/f", h)
router.POST("/b/:p0/:p1/g/:p4/h", h)
router.OPTIONS("/b/:p0/:p1/g/:p4/h", h)
router.DELETE("/b/cache", h)
router.GET("/b/clients/:p1/g", h)
router.POST("/b/clients/:p1/g", h)
router.PATCH("/b/clients/:p1/g/:p4", h)
router.OPTIONS("/b/clients/:p1/g/:p4", h)
req := httptest.NewRequest(http.MethodPost, "/b/clients/42", nil)
w := httptest.NewRecorder()
// This should return 405 Method Not Allowed, not panic
router.ServeHTTP(w, req)
if w.Code != http.StatusMethodNotAllowed {
t.Errorf("Expected status 405, got %d", w.Code)
}
}