Skip to content

Commit 4ca64e8

Browse files
authored
Fix invalid HTTP2 parsing (#932)
1 parent a3ce1ee commit 4ca64e8

File tree

3 files changed

+338
-53
lines changed

3 files changed

+338
-53
lines changed

pkg/ebpf/common/http2grpc_transform.go

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"bytes"
88
"encoding/binary"
99
"errors"
10+
"regexp"
1011
"strconv"
1112
"strings"
1213
"unsafe"
@@ -34,6 +35,11 @@ const (
3435

3536
const initialHeaderTableSize = 4096
3637

38+
var (
39+
validPath = regexp.MustCompile(`^[A-Za-z0-9\-/._~]+$`)
40+
validContentType = regexp.MustCompile(`^[A-Za-z\-/\+]+$`)
41+
)
42+
3743
type h2Connection struct {
3844
hdec *bhpack.Decoder
3945
hdecRet *bhpack.Decoder
@@ -84,13 +90,50 @@ func protocolIsGRPC(activeGRPCConnections *lru.Cache[uint64, h2Connection], conn
8490

8591
var commonHDec = bhpack.NewDecoder(0, nil)
8692

93+
func isHTTPOp(op string) bool {
94+
return op == "GET" || op == "POST" || op == "PATCH" || op == "DELETE" || op == "OPTIONS" || op == "HEAD"
95+
}
96+
97+
func handleHeaderField(hf *bhpack.HeaderField) bool {
98+
hfKey := strings.ToLower(hf.Name)
99+
switch hfKey {
100+
case ":method":
101+
val := strings.ToUpper(hf.Value)
102+
if isHTTPOp(val) {
103+
return true
104+
}
105+
case ":scheme":
106+
val := strings.ToUpper(hf.Value)
107+
if val == "HTTP" {
108+
return true
109+
}
110+
case "traceparent":
111+
return true
112+
case ":path":
113+
val := hf.Value
114+
if pos := strings.Index(val, "?"); pos >= 0 {
115+
val = val[:pos]
116+
}
117+
if validPath.MatchString(val) {
118+
return true
119+
}
120+
case "content-type":
121+
val := hf.Value
122+
if validContentType.MatchString(val) {
123+
return true
124+
}
125+
case "grpc-status":
126+
return true
127+
}
128+
129+
return false
130+
}
131+
87132
func knownFrameKeys(fr *http2.Framer, hf *http2.HeadersFrame) bool {
88-
known := false
133+
knownCount := 0
89134
commonHDec.SetEmitFunc(func(hf bhpack.HeaderField) {
90-
hfKey := strings.ToLower(hf.Name)
91-
switch hfKey {
92-
case ":method", ":path", "content-type", ":status", "grpc-status":
93-
known = true
135+
if handleHeaderField(&hf) {
136+
knownCount++
94137
}
95138
})
96139
// Lose reference to MetaHeadersFrame:
@@ -116,7 +159,7 @@ func knownFrameKeys(fr *http2.Framer, hf *http2.HeadersFrame) bool {
116159
frag = cf.HeaderBlockFragment()
117160
}
118161

119-
return known
162+
return knownCount > 1
120163
}
121164

122165
func readMetaFrame(parseContext *EBPFParseContext, connID uint64, fr *http2.Framer, hf *http2.HeadersFrame) (string, string, string, bool) {
@@ -358,8 +401,10 @@ func http2FromBuffers(parseContext *EBPFParseContext, event *BPFHTTP2Info) (requ
358401
if ff, ok := f.(*http2.HeadersFrame); ok {
359402
rok := false
360403
method, path, contentType, ok := readMetaFrame(parseContext, connID, framer, ff)
361-
362-
if path == "" {
404+
if pos := strings.Index(path, "?"); pos >= 0 {
405+
path = path[:pos]
406+
}
407+
if path == "" || !validPath.MatchString(path) {
363408
path = "*"
364409
}
365410

0 commit comments

Comments
 (0)