@@ -45,6 +45,8 @@ SRC_URI += "\
file://CVE-2026-27143.patch \
file://CVE-2026-27144.patch \
file://CVE-2026-32280.patch \
+ file://CVE-2026-32282.patch \
+ file://CVE-2026-32283.patch \
"
SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
new file mode 100644
@@ -0,0 +1,240 @@
+From 4c79c4223e42b4727d1600e71ad6983cfb21f534 Mon Sep 17 00:00:00 2001
+From: Damien Neil <dneil@google.com>
+Date: Mon, 23 Mar 2026 10:34:50 -0700
+Subject: [PATCH] [release-branch.go1.25] internal/syscall/unix: properly
+ support AT_SYMLINK_NOFOLLOW on Linux
+
+On Linux, the fchmodat syscall silently ignores the AT_SYMLINK_NOFOLLOW flag.
+
+Change the Linux Fchmodat function to use the fstatat2 syscall
+(added in Linux 6.6) when available.
+
+When fstatat2 is not available, use the same workaround as
+GNU libc and musl, which is to open the target file
+with O_PATH and then chmod it via /proc/self/fd.
+
+This change fixes an os.Root escape, where Root.Chmod could follow
+a symlink and act on a file outside of the root. Root.Chmod checks
+to see if its target is a symlink before calling fchmodat, so this
+escape requires the target to be replaced with a symlink in between
+the initial check and the fchmodat.
+
+Thanks to Uuganbayar Lkhamsuren (https://github.com/uug4na)
+for reporting this issue.
+
+Fixes CVE-2026-32282
+Fixes #78293
+
+Change-Id: Ie487be1a853b341a77b42ae0c59301d46a6a6964
+Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/3900
+Reviewed-by: Damien Neil <dneil@google.com>
+Reviewed-by: Neal Patel <nealpatel@google.com>
+Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/3982
+Commit-Queue: Damien Neil <dneil@google.com>
+Reviewed-on: https://go-review.googlesource.com/c/go/+/763550
+Reviewed-by: David Chase <drchase@google.com>
+Auto-Submit: Gopher Robot <gobot@golang.org>
+TryBot-Bypass: Gopher Robot <gobot@golang.org>
+Reviewed-by: Junyang Shao <shaojunyang@google.com>
+
+
+CVE: CVE-2026-32282
+Upstream-Status: Backport [https://github.com/golang/go/commit/4c79c4223e42b4727d1600e71ad6983cfb21f534]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ src/cmd/dist/buildtool.go | 1 +
+ src/internal/syscall/unix/at_sysnum_linux.go | 2 +
+ src/internal/syscall/unix/fchmodat_linux.go | 51 ++++++++++++++++
+ src/internal/syscall/unix/fchmodat_other.go | 29 +++++++++
+ src/internal/syscall/unix/fchmodat_test.go | 62 ++++++++++++++++++++
+ 5 files changed, 145 insertions(+)
+ create mode 100644 src/internal/syscall/unix/fchmodat_linux.go
+ create mode 100644 src/internal/syscall/unix/fchmodat_other.go
+ create mode 100644 src/internal/syscall/unix/fchmodat_test.go
+
+diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
+index 3232896..9cc7075 100644
+--- a/src/cmd/dist/buildtool.go
++++ b/src/cmd/dist/buildtool.go
+@@ -74,6 +74,7 @@ var bootstrapDirs = []string{
+ "internal/goroot",
+ "internal/gover",
+ "internal/goversion",
++ "internal/itoa",
+ // internal/lazyregexp is provided by Go 1.17, which permits it to
+ // be imported by other packages in this list, but is not provided
+ // by the Go 1.17 version of gccgo. It's on this list only to
+diff --git a/src/internal/syscall/unix/at_sysnum_linux.go b/src/internal/syscall/unix/at_sysnum_linux.go
+index 7c3b15c..12ee1d4 100644
+--- a/src/internal/syscall/unix/at_sysnum_linux.go
++++ b/src/internal/syscall/unix/at_sysnum_linux.go
+@@ -16,4 +16,6 @@ const (
+ AT_SYMLINK_NOFOLLOW = 0x100
+
+ UTIME_OMIT = 0x3ffffffe
++
++ O_PATH = 0x200000
+ )
+diff --git a/src/internal/syscall/unix/fchmodat_linux.go b/src/internal/syscall/unix/fchmodat_linux.go
+new file mode 100644
+index 0000000..a9fa42b
+--- /dev/null
++++ b/src/internal/syscall/unix/fchmodat_linux.go
+@@ -0,0 +1,51 @@
++// Copyright 2026 The Go Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style
++// license that can be found in the LICENSE file.
++
++//go:build linux
++
++package unix
++
++import (
++ "internal/itoa"
++ "syscall"
++)
++
++func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
++ // On Linux, the fchmodat syscall silently ignores the AT_SYMLINK_NOFOLLOW flag.
++ // We need to use fchmodat2 instead.
++ // syscall.Fchmodat handles this.
++ if err := syscall.Fchmodat(dirfd, path, mode, flags); err != syscall.EOPNOTSUPP {
++ return err
++ }
++
++ // This kernel doesn't appear to support fchmodat2 (added in Linux 6.6).
++ // We can't fall back to Fchmod, because it requires write permissions on the file.
++ // Instead, use the same workaround as GNU libc and musl, which is to open the file
++ // and then fchmodat the FD in /proc/self/fd.
++ // See: https://lwn.net/Articles/939217/
++ fd, err := Openat(dirfd, path, O_PATH|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
++ if err != nil {
++ return err
++ }
++ defer syscall.Close(fd)
++ procPath := "/proc/self/fd/" + itoa.Itoa(fd)
++
++ // Check to see if this file is a symlink.
++ // (We passed O_NOFOLLOW above, but O_PATH|O_NOFOLLOW will open a symlink.)
++ var st syscall.Stat_t
++ if err := syscall.Stat(procPath, &st); err != nil {
++ if err == syscall.ENOENT {
++ // /proc has probably not been mounted. Give up.
++ return syscall.EOPNOTSUPP
++ }
++ return err
++ }
++ if st.Mode&syscall.S_IFMT == syscall.S_IFLNK {
++ // fchmodat on the proc FD for a symlink apparently gives inconsistent
++ // results, so just refuse to try.
++ return syscall.EOPNOTSUPP
++ }
++
++ return syscall.Fchmodat(AT_FDCWD, procPath, mode, flags&^AT_SYMLINK_NOFOLLOW)
++}
+diff --git a/src/internal/syscall/unix/fchmodat_other.go b/src/internal/syscall/unix/fchmodat_other.go
+new file mode 100644
+index 0000000..76f478c
+--- /dev/null
++++ b/src/internal/syscall/unix/fchmodat_other.go
+@@ -0,0 +1,29 @@
++// Copyright 2026 The Go Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style
++// license that can be found in the LICENSE file.
++
++//go:build dragonfly || freebsd || netbsd || (openbsd && mips64)
++
++package unix
++
++import (
++ "syscall"
++ "unsafe"
++)
++
++func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
++ p, err := syscall.BytePtrFromString(path)
++ if err != nil {
++ return err
++ }
++ _, _, errno := syscall.Syscall6(fchmodatTrap,
++ uintptr(dirfd),
++ uintptr(unsafe.Pointer(p)),
++ uintptr(mode),
++ uintptr(flags),
++ 0, 0)
++ if errno != 0 {
++ return errno
++ }
++ return nil
++}
+diff --git a/src/internal/syscall/unix/fchmodat_test.go b/src/internal/syscall/unix/fchmodat_test.go
+new file mode 100644
+index 0000000..49a0985
+--- /dev/null
++++ b/src/internal/syscall/unix/fchmodat_test.go
+@@ -0,0 +1,62 @@
++// Copyright 2026 The Go Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style
++// license that can be found in the LICENSE file.
++
++//go:build unix || wasip1
++
++package unix_test
++
++import (
++ "internal/syscall/unix"
++ "os"
++ "runtime"
++ "testing"
++)
++
++// TestFchmodAtSymlinkNofollow verifies that Fchmodat honors the AT_SYMLINK_NOFOLLOW flag.
++func TestFchmodatSymlinkNofollow(t *testing.T) {
++ if runtime.GOOS == "wasip1" {
++ t.Skip("wasip1 doesn't support chmod")
++ }
++
++ dir := t.TempDir()
++ filename := dir + "/file"
++ linkname := dir + "/symlink"
++ if err := os.WriteFile(filename, nil, 0o100); err != nil {
++ t.Fatal(err)
++ }
++ if err := os.Symlink(filename, linkname); err != nil {
++ t.Fatal(err)
++ }
++
++ parent, err := os.Open(dir)
++ if err != nil {
++ t.Fatal(err)
++ }
++ defer parent.Close()
++
++ lstatMode := func(path string) os.FileMode {
++ st, err := os.Lstat(path)
++ if err != nil {
++ t.Fatal(err)
++ }
++ return st.Mode()
++ }
++
++ // Fchmodat with no flags follows symlinks.
++ const mode1 = 0o200
++ if err := unix.Fchmodat(int(parent.Fd()), "symlink", mode1, 0); err != nil {
++ t.Fatal(err)
++ }
++ if got, want := lstatMode(filename), os.FileMode(mode1); got != want {
++ t.Errorf("after Fchmodat(parent, symlink, %v, 0); mode = %v, want %v", mode1, got, want)
++ }
++
++ // Fchmodat with AT_SYMLINK_NOFOLLOW does not follow symlinks.
++ // The Fchmodat call may fail or chmod the symlink itself, depending on the kernel version.
++ const mode2 = 0o400
++ unix.Fchmodat(int(parent.Fd()), "symlink", mode2, unix.AT_SYMLINK_NOFOLLOW)
++ if got, want := lstatMode(filename), os.FileMode(mode1); got != want {
++ t.Errorf("after Fchmodat(parent, symlink, %v, AT_SYMLINK_NOFOLLOW); mode = %v, want %v", mode1, got, want)
++ }
++}
+--
+2.50.1
+
new file mode 100644
@@ -0,0 +1,178 @@
+From 02f574a8303560a4a79a42834f3092ce7c9a57cc Mon Sep 17 00:00:00 2001
+From: Roland Shoemaker <bracewell@google.com>
+Date: Mon, 23 Mar 2026 11:54:41 -0700
+Subject: [PATCH] [release-branch.go1.25] crypto/tls: prevent deadlock when
+ client sends multiple key update messages
+
+When we made setReadTrafficSecret send an alert when there are pending
+handshake messages, we introduced a deadlock when the client sends
+multiple key update messages that request a response, as handleKeyUpdate
+will lock the mutex, and defer the unlocking until the end of the
+function, but setReadTrafficSecret called sendAlert in the failure case,
+which also tries to lock the mutex.
+
+Add an argument to setReadTrafficSecret which lets the caller indicate
+if the mutex is already locked, and if so, call sendAlertLocked instead
+of sendAlert.
+
+Thanks to Jakub Ciolek for reporting this issue.
+
+Fixes #78334
+Fixes CVE-2026-32283
+
+Change-Id: Id8e56974233c910e0d66ba96eafbd2ea57832610
+Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/3881
+Reviewed-by: Damien Neil <dneil@google.com>
+Reviewed-by: Nicholas Husin <husin@google.com>
+Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/3988
+Reviewed-on: https://go-review.googlesource.com/c/go/+/763555
+Auto-Submit: Gopher Robot <gobot@golang.org>
+Reviewed-by: David Chase <drchase@google.com>
+TryBot-Bypass: Gopher Robot <gobot@golang.org>
+Reviewed-by: Junyang Shao <shaojunyang@google.com>
+
+CVE: CVE-2026-32283
+Upstream-Status: Backport [https://github.com/golang/go/commit/02f574a8303560a4a79a42834f3092ce7c9a57cc]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ src/crypto/tls/conn.go | 10 +++--
+ src/crypto/tls/handshake_client_tls13.go | 4 +-
+ src/crypto/tls/handshake_server_tls13.go | 4 +-
+ src/crypto/tls/handshake_test.go | 48 ++++++++++++++++++++++++
+ 4 files changed, 59 insertions(+), 7 deletions(-)
+
+diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
+index 08609ce..770d456 100644
+--- a/src/crypto/tls/conn.go
++++ b/src/crypto/tls/conn.go
+@@ -1345,7 +1345,7 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
+- if err := c.setReadTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret); err != nil {
++ if err := c.setReadTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret, keyUpdate.updateRequested); err != nil {
+ return err
+ }
+
+@@ -1675,12 +1675,16 @@ func (c *Conn) VerifyHostname(host string) error {
+ // setReadTrafficSecret sets the read traffic secret for the given encryption level. If
+ // being called at the same time as setWriteTrafficSecret, the caller must ensure the call
+ // to setWriteTrafficSecret happens first so any alerts are sent at the write level.
+-func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) error {
++func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte, locked bool) error {
+ // Ensure that there are no buffered handshake messages before changing the
+ // read keys, since that can cause messages to be parsed that were encrypted
+ // using old keys which are no longer appropriate.
+ if c.hand.Len() != 0 {
+- c.sendAlert(alertUnexpectedMessage)
++ if locked {
++ c.sendAlertLocked(alertUnexpectedMessage)
++ } else {
++ c.sendAlert(alertUnexpectedMessage)
++ }
+ return errors.New("tls: handshake buffer not empty before setting read traffic secret")
+ }
+ c.in.setTrafficSecret(suite, level, secret)
+diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go
+index 68ff92b..2d58b21 100644
+--- a/src/crypto/tls/handshake_client_tls13.go
++++ b/src/crypto/tls/handshake_client_tls13.go
+@@ -396,7 +396,7 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
+ c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
+ serverSecret := hs.suite.deriveSecret(handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+- if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret); err != nil {
++ if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret, false); err != nil {
+ return err
+ }
+
+@@ -607,7 +607,7 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error {
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+- if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret); err != nil {
++ if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret, false); err != nil {
+ return err
+ }
+
+diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
+index 1ecee3a..f73b536 100644
+--- a/src/crypto/tls/handshake_server_tls13.go
++++ b/src/crypto/tls/handshake_server_tls13.go
+@@ -636,7 +636,7 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
+ c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
+ clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+- if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret); err != nil {
++ if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret, false); err != nil {
+ return err
+ }
+
+@@ -1005,7 +1005,7 @@ func (hs *serverHandshakeStateTLS13) readClientFinished() error {
+ return errors.New("tls: invalid client finished hash")
+ }
+
+- if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret); err != nil {
++ if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret, false); err != nil {
+ return err
+ }
+
+diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go
+index 4991a0e..a95d751 100644
+--- a/src/crypto/tls/handshake_test.go
++++ b/src/crypto/tls/handshake_test.go
+@@ -673,3 +673,51 @@ func concatHandshakeMessages(msgs ...handshakeMessage) ([]byte, error) {
+ outBuf = append(outBuf, marshalled...)
+ return outBuf, nil
+ }
++
++func TestMultipleKeyUpdate(t *testing.T) {
++ for _, requestUpdate := range []bool{true, false} {
++ t.Run(fmt.Sprintf("requestUpdate=%t", requestUpdate), func(t *testing.T) {
++
++ c, s := localPipe(t)
++ cfg := testConfig.Clone()
++ cfg.MinVersion = VersionTLS13
++ cfg.MaxVersion = VersionTLS13
++ client := Client(c, testConfig)
++ server := Server(s, testConfig)
++
++ clientHandshakeDone := make(chan struct{})
++ go func() {
++ if err := client.Handshake(); err != nil {
++ }
++ close(clientHandshakeDone)
++ io.Copy(io.Discard, server)
++ }()
++
++ if err := server.Handshake(); err != nil {
++ t.Fatalf("server handshake failed: %v\n", err)
++ }
++ <-clientHandshakeDone
++
++ c.SetReadDeadline(time.Now().Add(1 * time.Second))
++ s.SetReadDeadline(time.Now().Add(1 * time.Second))
++
++ kuMsg, err := (&keyUpdateMsg{updateRequested: requestUpdate}).marshal()
++ if err != nil {
++ t.Fatalf("failed to marshal key update message: %v", err)
++ }
++
++ client.out.Lock()
++ if _, err := client.writeRecordLocked(recordTypeHandshake, append(kuMsg, kuMsg...)); err != nil {
++ t.Fatalf("failed to write key update messages: %v", err)
++ }
++ client.out.Unlock()
++
++ _, err = io.Copy(io.Discard, client)
++ if err == nil {
++ t.Fatal("expected multiple key update messages to cause an error, got nil")
++ } else if !strings.HasSuffix(err.Error(), "tls: unexpected message") {
++ t.Fatalf("unexpected error: %v", err)
++ }
++ })
++ }
++}
+--
+2.50.1
+
Pick patch from [1] & [2] also mentioned at Debian report in [3] & [4] [1] https://github.com/golang/go/commit/4c79c4223e42b4727d1600e71ad6983cfb21f534 [2] https://github.com/golang/go/commit/02f574a8303560a4a79a42834f3092ce7c9a57cc [3] https://security-tracker.debian.org/tracker/CVE-2026-32282 [4] https://security-tracker.debian.org/tracker/CVE-2026-32283 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> --- meta/recipes-devtools/go/go-1.22.12.inc | 2 + .../go/go/CVE-2026-32282.patch | 240 ++++++++++++++++++ .../go/go/CVE-2026-32283.patch | 178 +++++++++++++ 3 files changed, 420 insertions(+) create mode 100644 meta/recipes-devtools/go/go/CVE-2026-32282.patch create mode 100644 meta/recipes-devtools/go/go/CVE-2026-32283.patch