* [meta-virtualization][scarthgap][PATCH] grpc-go: Fix CVE-2026-33186
@ 2026-09-03 6:11 Hetvi Thakar -X (hthakar - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-09-16 13:59 ` Bruce Ashfield
0 siblings, 1 reply; 2+ messages in thread
From: Hetvi Thakar -X (hthakar - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-09-03 6:11 UTC (permalink / raw)
To: meta-virtualization; +Cc: xe-linux-external, Hetvi Thakar
From: Hetvi Thakar <hthakar@cisco.com>
This patch applies the equivalent master fix from [1].
The nearest fixed-tag commit is [2], which is included in v1.79.3.
The advisory in [3] documents the CVE and fixed version but does not
cite either commit. Commit [1] is used because it contains the same
CVE-relevant strict path checking change without the unrelated
test-data changes from [2].
Backport Changes:
- Updated do_install to exclude Quilt patch-management (.pc) files from the
installed Go source files.
[1] https://github.com/grpc/grpc-go/commit/58bbf8ab36838ce1454da092ac0dad350b0162ea
[2] https://github.com/grpc/grpc-go/commit/72186f163e75a065c39e6f7df9b6dea07fbdeff5
[3] https://github.com/grpc/grpc-go/security/advisories/GHSA-p77j-4mvh-x3m3
Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
---
.../go/grpc-go/CVE-2026-33186.patch | 346 ++++++++++++++++++
recipes-devtools/go/grpc-go_git.bb | 6 +-
2 files changed, 350 insertions(+), 2 deletions(-)
create mode 100644 recipes-devtools/go/grpc-go/CVE-2026-33186.patch
diff --git a/recipes-devtools/go/grpc-go/CVE-2026-33186.patch b/recipes-devtools/go/grpc-go/CVE-2026-33186.patch
new file mode 100644
index 0000000..4ee3d83
--- /dev/null
+++ b/recipes-devtools/go/grpc-go/CVE-2026-33186.patch
@@ -0,0 +1,346 @@
+From cc6152a20c72326c091e73fde27628be1324e3e7 Mon Sep 17 00:00:00 2001
+From: Easwar Swaminathan <easwars@google.com>
+Date: Mon, 22 Jun 2026 22:36:59 -0700
+Subject: [PATCH] grpc: enforce strict path checking for incoming requests on
+ the server (#8985)
+
+RELEASE NOTES:
+* server: fix an authorization bypass where malformed :path headers
+(missing the leading slash) could bypass path-based restricted "deny"
+rules in interceptors like `grpc/authz`. Any request with a
+non-canonical path is now immediately rejected with an `Unimplemented`
+error.
+
+CVE: CVE-2026-33186
+Upstream-Status: Backport [https://github.com/grpc/grpc-go/commit/58bbf8ab36838ce1454da092ac0dad350b0162ea]
+
+Backport Changes:
+- Adapted handleMalformedMethodName for grpc-go v1.59.0 transport APIs.
+ Upstream uses transport.ServerStream and calls stream.WriteStatus(...),
+ while v1.59.0 handleStream receives *transport.Stream and writes status
+ through transport.ServerTransport. The backport passes the server transport
+ into handleMalformedMethodName and uses t.WriteStatus(stream, ...).
+- Adapted channelz logging for grpc-go v1.59.0 Server fields.
+ Upstream logs with s.channelz, but v1.59.0 stores the server channelz
+ identifier as s.channelzID. The backport uses s.channelzID for the new
+ warning logs and malformed-method status-write failure log.
+- Replaced the newer testutils.SetEnvConfig helper used by upstream.
+ grpc-go v1.59.0 does not provide that helper, so the backport omits the
+ upstream google.golang.org/grpc/internal/testutils import and uses local
+ save/restore for envconfig.DisableStrictPathChecking in the regression test.
+
+(cherry picked from commit 58bbf8ab36838ce1454da092ac0dad350b0162ea)
+Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
+---
+ internal/envconfig/envconfig.go | 16 +++
+ server.go | 57 ++++++++---
+ test/malformed_method_test.go | 178 ++++++++++++++++++++++++++++++++
+ 3 files changed, 235 insertions(+), 16 deletions(-)
+ create mode 100644 test/malformed_method_test.go
+
+diff --git a/internal/envconfig/envconfig.go b/internal/envconfig/envconfig.go
+index 3cf10ddf..6d579e55 100644
+--- a/internal/envconfig/envconfig.go
++++ b/internal/envconfig/envconfig.go
+@@ -46,6 +46,22 @@ var (
+ // ALTSMaxConcurrentHandshakes is the maximum number of concurrent ALTS
+ // handshakes that can be performed.
+ ALTSMaxConcurrentHandshakes = uint64FromEnv("GRPC_ALTS_MAX_CONCURRENT_HANDSHAKES", 100, 1, 100)
++
++ // DisableStrictPathChecking indicates whether strict path checking is
++ // disabled. This feature can be disabled by setting the environment
++ // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true".
++ //
++ // When strict path checking is enabled, gRPC will reject requests with
++ // paths that do not conform to the gRPC over HTTP/2 specification found at
++ // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.
++ //
++ // When disabled, gRPC will allow paths that do not contain a leading slash.
++ // Enabling strict path checking is recommended for security reasons, as it
++ // prevents potential path traversal vulnerabilities.
++ //
++ // A future release will remove this environment variable, enabling strict
++ // path checking behavior unconditionally.
++ DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false)
+ )
+
+ func boolFromEnv(envVar string, def bool) bool {
+diff --git a/server.go b/server.go
+index 8f60d421..75e58eb4 100644
+--- a/server.go
++++ b/server.go
+@@ -43,6 +43,7 @@ import (
+ "google.golang.org/grpc/internal"
+ "google.golang.org/grpc/internal/binarylog"
+ "google.golang.org/grpc/internal/channelz"
++ "google.golang.org/grpc/internal/envconfig"
+ "google.golang.org/grpc/internal/grpcsync"
+ "google.golang.org/grpc/internal/grpcutil"
+ "google.golang.org/grpc/internal/transport"
+@@ -140,6 +141,8 @@ type Server struct {
+ czData *channelzData
+
+ serverWorkerChannel chan func()
++
++ strictPathCheckingLogEmitted atomic.Bool
+ }
+
+ type serverOptions struct {
+@@ -1687,6 +1690,24 @@ func (s *Server) processStreamingRPC(ctx context.Context, t transport.ServerTran
+ return t.WriteStatus(ss.s, statusOK)
+ }
+
++func (s *Server) handleMalformedMethodName(t transport.ServerTransport, stream *transport.Stream, ti *traceInfo) {
++ if ti != nil {
++ ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{stream.Method()}}, true)
++ ti.tr.SetError()
++ }
++ errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
++ if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
++ if ti != nil {
++ ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true)
++ ti.tr.SetError()
++ }
++ channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
++ }
++ if ti != nil {
++ ti.tr.Finish()
++ }
++}
++
+ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
+ ctx := stream.Context()
+ var ti *traceInfo
+@@ -1706,26 +1727,30 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
+ }
+
+ sm := stream.Method()
+- if sm != "" && sm[0] == '/' {
++ if sm == "" {
++ s.handleMalformedMethodName(t, stream, ti)
++ return
++ }
++ if sm[0] != '/' {
++ // TODO(easwars): Add a link to the CVE in the below log messages once
++ // published.
++ if envconfig.DisableStrictPathChecking {
++ if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
++ channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm)
++ }
++ } else {
++ if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
++ channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm)
++ }
++ s.handleMalformedMethodName(t, stream, ti)
++ return
++ }
++ } else {
+ sm = sm[1:]
+ }
+ pos := strings.LastIndex(sm, "/")
+ if pos == -1 {
+- if ti != nil {
+- ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{sm}}, true)
+- ti.tr.SetError()
+- }
+- errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
+- if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
+- if ti != nil {
+- ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true)
+- ti.tr.SetError()
+- }
+- channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
+- }
+- if ti != nil {
+- ti.tr.Finish()
+- }
++ s.handleMalformedMethodName(t, stream, ti)
+ return
+ }
+ service := sm[:pos]
+diff --git a/test/malformed_method_test.go b/test/malformed_method_test.go
+new file mode 100644
+index 00000000..25a09ccb
+--- /dev/null
++++ b/test/malformed_method_test.go
+@@ -0,0 +1,178 @@
++/*
++ *
++ * Copyright 2026 gRPC authors.
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ */
++
++package test
++
++import (
++ "bytes"
++ "context"
++ "net"
++ "testing"
++
++ "golang.org/x/net/http2"
++ "golang.org/x/net/http2/hpack"
++ "google.golang.org/grpc/internal/envconfig"
++ "google.golang.org/grpc/internal/stubserver"
++
++ testpb "google.golang.org/grpc/interop/grpc_testing"
++)
++
++// TestMalformedMethodPath tests that the server responds with Unimplemented
++// when the method path is malformed. This verifies that the server does not
++// route requests with a malformed method path to the application handler.
++func (s) TestMalformedMethodPath(t *testing.T) {
++ tests := []struct {
++ name string
++ path string
++ envVar bool
++ wantStatus string // string representation of codes.Code
++ }{
++ {
++ name: "missing_leading_slash_disableStrictPathChecking_false",
++ path: "grpc.testing.TestService/UnaryCall",
++ wantStatus: "12", // Unimplemented
++ },
++ {
++ name: "empty_path_disableStrictPathChecking_false",
++ path: "",
++ wantStatus: "12", // Unimplemented
++ },
++ {
++ name: "just_slash_disableStrictPathChecking_false",
++ path: "/",
++ wantStatus: "12", // Unimplemented
++ },
++ {
++ name: "missing_leading_slash_disableStrictPathChecking_true",
++ path: "grpc.testing.TestService/UnaryCall",
++ envVar: true,
++ wantStatus: "0", // OK
++ },
++ {
++ name: "empty_path_disableStrictPathChecking_true",
++ path: "",
++ envVar: true,
++ wantStatus: "12", // Unimplemented
++ },
++ {
++ name: "just_slash_disableStrictPathChecking_true",
++ path: "/",
++ envVar: true,
++ wantStatus: "12", // Unimplemented
++ },
++ }
++
++ for _, tc := range tests {
++ t.Run(tc.name, func(t *testing.T) {
++ ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
++ defer cancel()
++
++ origDisableStrictPathChecking := envconfig.DisableStrictPathChecking
++ envconfig.DisableStrictPathChecking = tc.envVar
++ defer func() { envconfig.DisableStrictPathChecking = origDisableStrictPathChecking }()
++
++ ss := &stubserver.StubServer{
++ UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
++ return &testpb.SimpleResponse{Payload: &testpb.Payload{Body: []byte("pwned")}}, nil
++ },
++ }
++ if err := ss.Start(nil); err != nil {
++ t.Fatalf("Error starting endpoint server: %v", err)
++ }
++ defer ss.Stop()
++
++ // Open a raw TCP connection to the server and speak HTTP/2 directly.
++ tcpConn, err := net.Dial("tcp", ss.Address)
++ if err != nil {
++ t.Fatalf("Failed to dial tcp: %v", err)
++ }
++ defer tcpConn.Close()
++
++ // Write the HTTP/2 connection preface and the initial settings frame.
++ if _, err := tcpConn.Write([]byte("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")); err != nil {
++ t.Fatalf("Failed to write preface: %v", err)
++ }
++ framer := http2.NewFramer(tcpConn, tcpConn)
++ if err := framer.WriteSettings(); err != nil {
++ t.Fatalf("Failed to write settings: %v", err)
++ }
++
++ // Encode and write the HEADERS frame.
++ var headerBuf bytes.Buffer
++ enc := hpack.NewEncoder(&headerBuf)
++ writeHeader := func(name, value string) {
++ enc.WriteField(hpack.HeaderField{Name: name, Value: value})
++ }
++ writeHeader(":method", "POST")
++ writeHeader(":scheme", "http")
++ writeHeader(":authority", ss.Address)
++ writeHeader(":path", tc.path)
++ writeHeader("content-type", "application/grpc")
++ writeHeader("te", "trailers")
++ if err := framer.WriteHeaders(http2.HeadersFrameParam{
++ StreamID: 1,
++ BlockFragment: headerBuf.Bytes(),
++ EndStream: false,
++ EndHeaders: true,
++ }); err != nil {
++ t.Fatalf("Failed to write headers: %v", err)
++ }
++
++ // Send a small gRPC-encoded data frame (0 length).
++ if err := framer.WriteData(1, true, []byte{0, 0, 0, 0, 0}); err != nil {
++ t.Fatalf("Failed to write data: %v", err)
++ }
++
++ // Read responses and look for grpc-status.
++ gotStatus := ""
++ dec := hpack.NewDecoder(4096, func(f hpack.HeaderField) {
++ if f.Name == "grpc-status" {
++ gotStatus = f.Value
++ }
++ })
++ done := make(chan struct{})
++ go func() {
++ defer close(done)
++ for {
++ frame, err := framer.ReadFrame()
++ if err != nil {
++ return
++ }
++ if headers, ok := frame.(*http2.HeadersFrame); ok {
++ if _, err := dec.Write(headers.HeaderBlockFragment()); err != nil {
++ return
++ }
++ if headers.StreamEnded() {
++ return
++ }
++ }
++ }
++ }()
++
++ select {
++ case <-done:
++ case <-ctx.Done():
++ t.Fatalf("Timed out waiting for response")
++ }
++
++ if gotStatus != tc.wantStatus {
++ t.Errorf("Got grpc-status %v, want %v", gotStatus, tc.wantStatus)
++ }
++ })
++ }
++}
+--
+2.35.6
diff --git a/recipes-devtools/go/grpc-go_git.bb b/recipes-devtools/go/grpc-go_git.bb
index fdfc230..ec37890 100644
--- a/recipes-devtools/go/grpc-go_git.bb
+++ b/recipes-devtools/go/grpc-go_git.bb
@@ -7,7 +7,9 @@ LIC_FILES_CHKSUM = "file://src/${PKG_NAME}/LICENSE;md5=3b83ef96387f14655fc854ddc
SRCNAME = "grpc-go"
PKG_NAME = "google.golang.org/grpc"
-SRC_URI = "git://github.com/grpc/${SRCNAME}.git;destsuffix=git/src/${PKG_NAME};branch=v1.59.x;protocol=https"
+SRC_URI = "git://github.com/grpc/${SRCNAME}.git;destsuffix=git/src/${PKG_NAME};branch=v1.59.x;protocol=https \
+ file://CVE-2026-33186.patch;patchdir=src/${PKG_NAME} \
+ "
SRCREV = "7765221f4bf6104973db7946d56936cf838cad46"
PV = "1.59.0+git"
@@ -20,7 +22,7 @@ do_compile() {
do_install() {
install -d ${D}${prefix}/local/go/src/${PKG_NAME}
- for j in $(cd ${S} && find src/${PKG_NAME} -name "*.go"); do
+ for j in $(cd ${S} && find src/${PKG_NAME} -name "*.go" ! -path "*/.pc/*"); do
if [ ! -d ${D}${prefix}/local/go/$(dirname $j) ]; then
mkdir -p ${D}${prefix}/local/go/$(dirname $j)
fi
--
2.35.6
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [meta-virtualization][scarthgap][PATCH] grpc-go: Fix CVE-2026-33186
2026-09-03 6:11 [meta-virtualization][scarthgap][PATCH] grpc-go: Fix CVE-2026-33186 Hetvi Thakar -X (hthakar - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-09-16 13:59 ` Bruce Ashfield
0 siblings, 0 replies; 2+ messages in thread
From: Bruce Ashfield @ 2026-09-16 13:59 UTC (permalink / raw)
To: hthakar; +Cc: meta-virtualization
merged
Bruce
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-16 13:59 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 6:11 [meta-virtualization][scarthgap][PATCH] grpc-go: Fix CVE-2026-33186 Hetvi Thakar -X (hthakar - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-09-16 13:59 ` Bruce Ashfield
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.