diff --git a/SPECS/containerd2/CVE-2026-34986.patch b/SPECS/containerd2/CVE-2026-34986.patch new file mode 100644 index 00000000000..2f95feb5808 --- /dev/null +++ b/SPECS/containerd2/CVE-2026-34986.patch @@ -0,0 +1,1046 @@ +From a2f81b47dd4147411d6edd51ff41bb66bb78f733 Mon Sep 17 00:00:00 2001 +From: Kanishk Bansal +Date: Sun, 26 Apr 2026 05:03:08 +0000 +Subject: [PATCH 1/2] go-jose upgrade + +Signed-off-by: Kanishk Bansal +--- + go.mod | 2 +- + go.sum | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/go.mod b/go.mod +index 1e96a3d..4a9c04f 100644 +--- a/go.mod ++++ b/go.mod +@@ -99,7 +99,7 @@ require ( + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect +- github.com/go-jose/go-jose/v4 v4.0.5 // indirect ++ github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect +diff --git a/go.sum b/go.sum +index 7342f89..a401efb 100644 +--- a/go.sum ++++ b/go.sum +@@ -106,8 +106,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S + github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= + github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= + github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= ++github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= ++github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= + github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= + github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= + github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +-- +2.45.4 + + +From 34a0ec6c76be0cf3c0617f7bde864beb696a7bbf Mon Sep 17 00:00:00 2001 +From: Kanishk Bansal +Date: Sun, 26 Apr 2026 05:14:30 +0000 +Subject: [PATCH 2/2] Code Upgrade for go-jose v4.1.4 + +--- + .../go-jose/go-jose/v4/CHANGELOG.md | 96 ------------------- + .../github.com/go-jose/go-jose/v4/README.md | 76 ++++++++------- + .../go-jose/go-jose/v4/asymmetric.go | 10 +- + .../go-jose/go-jose/v4/cipher/key_wrap.go | 10 +- + .../github.com/go-jose/go-jose/v4/crypter.go | 20 ++-- + vendor/github.com/go-jose/go-jose/v4/jwe.go | 19 +++- + vendor/github.com/go-jose/go-jose/v4/jwk.go | 59 ++++++++---- + vendor/github.com/go-jose/go-jose/v4/jws.go | 74 +++++++++++--- + .../github.com/go-jose/go-jose/v4/shared.go | 33 ++++++- + .../github.com/go-jose/go-jose/v4/signing.go | 44 ++++++--- + .../go-jose/go-jose/v4/symmetric.go | 39 +++++--- + vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 77 --------------- + vendor/modules.txt | 5 +- + 13 files changed, 273 insertions(+), 289 deletions(-) + delete mode 100644 vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md + delete mode 100644 vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go + +diff --git a/vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md b/vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md +deleted file mode 100644 +index 6f717db..0000000 +--- a/vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md ++++ /dev/null +@@ -1,96 +0,0 @@ +-# v4.0.4 +- +-## Fixed +- +- - Reverted "Allow unmarshalling JSONWebKeySets with unsupported key types" as a +- breaking change. See #136 / #137. +- +-# v4.0.3 +- +-## Changed +- +- - Allow unmarshalling JSONWebKeySets with unsupported key types (#130) +- - Document that OpaqueKeyEncrypter can't be implemented (for now) (#129) +- - Dependency updates +- +-# v4.0.2 +- +-## Changed +- +- - Improved documentation of Verify() to note that JSONWebKeySet is a supported +- argument type (#104) +- - Defined exported error values for missing x5c header and unsupported elliptic +- curves error cases (#117) +- +-# v4.0.1 +- +-## Fixed +- +- - An attacker could send a JWE containing compressed data that used large +- amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`. +- Those functions now return an error if the decompressed data would exceed +- 250kB or 10x the compressed size (whichever is larger). Thanks to +- Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj) +- for reporting. +- +-# v4.0.0 +- +-This release makes some breaking changes in order to more thoroughly +-address the vulnerabilities discussed in [Three New Attacks Against JSON Web +-Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot +-token". +- +-## Changed +- +- - Limit JWT encryption types (exclude password or public key types) (#78) +- - Enforce minimum length for HMAC keys (#85) +- - jwt: match any audience in a list, rather than requiring all audiences (#81) +- - jwt: accept only Compact Serialization (#75) +- - jws: Add expected algorithms for signatures (#74) +- - Require specifying expected algorithms for ParseEncrypted, +- ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned, +- jwt.ParseSignedAndEncrypted (#69, #74) +- - Usually there is a small, known set of appropriate algorithms for a program +- to use and it's a mistake to allow unexpected algorithms. For instance the +- "billion hash attack" relies in part on programs accepting the PBES2 +- encryption algorithm and doing the necessary work even if they weren't +- specifically configured to allow PBES2. +- - Revert "Strip padding off base64 strings" (#82) +- - The specs require base64url encoding without padding. +- - Minimum supported Go version is now 1.21 +- +-## Added +- +- - ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON. +- - These allow parsing a specific serialization, as opposed to ParseSigned and +- ParseEncrypted, which try to automatically detect which serialization was +- provided. It's common to require a specific serialization for a specific +- protocol - for instance JWT requires Compact serialization. +- +-[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf +- +-# v3.0.2 +- +-## Fixed +- +- - DecryptMulti: handle decompression error (#19) +- +-## Changed +- +- - jwe/CompactSerialize: improve performance (#67) +- - Increase the default number of PBKDF2 iterations to 600k (#48) +- - Return the proper algorithm for ECDSA keys (#45) +- +-## Added +- +- - Add Thumbprint support for opaque signers (#38) +- +-# v3.0.1 +- +-## Fixed +- +- - Security issue: an attacker specifying a large "p2c" value can cause +- JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large +- amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the +- disclosure and to Tom Tervoort for originally publishing the category of attack. +- https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf +diff --git a/vendor/github.com/go-jose/go-jose/v4/README.md b/vendor/github.com/go-jose/go-jose/v4/README.md +index 02b5749..55c5509 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/README.md ++++ b/vendor/github.com/go-jose/go-jose/v4/README.md +@@ -3,7 +3,6 @@ + [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4) + [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt) + [![license](https://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE) +-[![test](https://img.shields.io/github/checks-status/go-jose/go-jose/v4)](https://github.com/go-jose/go-jose/actions) + + Package jose aims to provide an implementation of the Javascript Object Signing + and Encryption set of standards. This includes support for JSON Web Encryption, +@@ -29,17 +28,20 @@ libraries in other languages. + + ### Versions + +-[Version 4](https://github.com/go-jose/go-jose) +-([branch](https://github.com/go-jose/go-jose/tree/main), +-[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version: ++The forthcoming Version 5 will be released with several breaking API changes, ++and will require Golang's `encoding/json/v2`, which is currently requires ++Go 1.25 built with GOEXPERIMENT=jsonv2. ++ ++Version 4 is the current stable version: + + import "github.com/go-jose/go-jose/v4" + +-The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which +-are still useable but not actively developed anymore. ++It supports at least the current and previous Golang release. Currently it ++requires Golang 1.24. ++ ++Version 3 is only receiving critical security updates. Migration to Version 4 is recommended. + +-Version 3, in this repo, is still receiving security fixes but not functionality +-updates. ++Versions 1 and 2 are obsolete, but can be found in the old repository, [square/go-jose](https://github.com/square/go-jose). + + ### Supported algorithms + +@@ -47,36 +49,36 @@ See below for a table of supported algorithms. Algorithm identifiers match + the names in the [JSON Web Algorithms](https://dx.doi.org/10.17487/RFC7518) + standard where possible. The Godoc reference has a list of constants. + +- Key encryption | Algorithm identifier(s) +- :------------------------- | :------------------------------ +- RSA-PKCS#1v1.5 | RSA1_5 +- RSA-OAEP | RSA-OAEP, RSA-OAEP-256 +- AES key wrap | A128KW, A192KW, A256KW +- AES-GCM key wrap | A128GCMKW, A192GCMKW, A256GCMKW +- ECDH-ES + AES key wrap | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW +- ECDH-ES (direct) | ECDH-ES1 +- Direct encryption | dir1 ++| Key encryption | Algorithm identifier(s) | ++|:-----------------------|:-----------------------------------------------| ++| RSA-PKCS#1v1.5 | RSA1_5 | ++| RSA-OAEP | RSA-OAEP, RSA-OAEP-256 | ++| AES key wrap | A128KW, A192KW, A256KW | ++| AES-GCM key wrap | A128GCMKW, A192GCMKW, A256GCMKW | ++| ECDH-ES + AES key wrap | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW | ++| ECDH-ES (direct) | ECDH-ES1 | ++| Direct encryption | dir1 | + + 1. Not supported in multi-recipient mode + +- Signing / MAC | Algorithm identifier(s) +- :------------------------- | :------------------------------ +- RSASSA-PKCS#1v1.5 | RS256, RS384, RS512 +- RSASSA-PSS | PS256, PS384, PS512 +- HMAC | HS256, HS384, HS512 +- ECDSA | ES256, ES384, ES512 +- Ed25519 | EdDSA2 ++| Signing / MAC | Algorithm identifier(s) | ++|:------------------|:------------------------| ++| RSASSA-PKCS#1v1.5 | RS256, RS384, RS512 | ++| RSASSA-PSS | PS256, PS384, PS512 | ++| HMAC | HS256, HS384, HS512 | ++| ECDSA | ES256, ES384, ES512 | ++| Ed25519 | EdDSA2 | + + 2. Only available in version 2 of the package + +- Content encryption | Algorithm identifier(s) +- :------------------------- | :------------------------------ +- AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 +- AES-GCM | A128GCM, A192GCM, A256GCM ++| Content encryption | Algorithm identifier(s) | ++|:-------------------|:--------------------------------------------| ++| AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 | ++| AES-GCM | A128GCM, A192GCM, A256GCM | + +- Compression | Algorithm identifiers(s) +- :------------------------- | ------------------------------- +- DEFLATE (RFC 1951) | DEF ++| Compression | Algorithm identifiers(s) | ++|:-------------------|--------------------------| ++| DEFLATE (RFC 1951) | DEF | + + ### Supported key types + +@@ -85,12 +87,12 @@ library, and can be passed to corresponding functions such as `NewEncrypter` or + `NewSigner`. Each of these keys can also be wrapped in a JWK if desired, which + allows attaching a key id. + +- Algorithm(s) | Corresponding types +- :------------------------- | ------------------------------- +- RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey) +- ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey) +- EdDSA1 | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey) +- AES, HMAC | []byte ++| Algorithm(s) | Corresponding types | ++|:------------------|--------------------------------------------------------------------------------------------------------------------------------------| ++| RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey) | ++| ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey) | ++| EdDSA1 | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey) | ++| AES, HMAC | []byte | + + 1. Only available in version 2 or later of the package + +diff --git a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go +index f8d5774..7784cd4 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go ++++ b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go +@@ -414,6 +414,9 @@ func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) { + + // Decrypt the given payload and return the content encryption key. + func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { ++ if recipient == nil { ++ return nil, errors.New("go-jose/go-jose: missing recipient") ++ } + epk, err := headers.getEPK() + if err != nil { + return nil, errors.New("go-jose/go-jose: invalid epk header") +@@ -461,13 +464,18 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI + return nil, ErrUnsupportedAlgorithm + } + ++ encryptedKey := recipient.encryptedKey ++ if len(encryptedKey) == 0 { ++ return nil, errors.New("go-jose/go-jose: missing JWE Encrypted Key") ++ } ++ + key := deriveKey(string(algorithm), keySize) + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + +- return josecipher.KeyUnwrap(block, recipient.encryptedKey) ++ return josecipher.KeyUnwrap(block, encryptedKey) + } + + func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { +diff --git a/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go b/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go +index b9effbc..a2f86e3 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go ++++ b/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go +@@ -66,12 +66,20 @@ func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) { + } + + // KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher. ++// ++// https://datatracker.ietf.org/doc/html/rfc7518#section-4.4 ++// https://datatracker.ietf.org/doc/html/rfc7518#section-4.6 ++// https://datatracker.ietf.org/doc/html/rfc7518#section-4.8 + func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) { ++ n := (len(ciphertext) / 8) - 1 ++ if n <= 0 { ++ return nil, errors.New("go-jose/go-jose: JWE Encrypted Key too short") ++ } ++ + if len(ciphertext)%8 != 0 { + return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks") + } + +- n := (len(ciphertext) / 8) - 1 + r := make([][]byte, n) + + for i := range r { +diff --git a/vendor/github.com/go-jose/go-jose/v4/crypter.go b/vendor/github.com/go-jose/go-jose/v4/crypter.go +index d81b03b..31290fc 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/crypter.go ++++ b/vendor/github.com/go-jose/go-jose/v4/crypter.go +@@ -286,6 +286,10 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey + return newSymmetricRecipient(alg, encryptionKey) + case string: + return newSymmetricRecipient(alg, []byte(encryptionKey)) ++ case JSONWebKey: ++ recipient, err := makeJWERecipient(alg, encryptionKey.Key) ++ recipient.keyID = encryptionKey.KeyID ++ return recipient, err + case *JSONWebKey: + recipient, err := makeJWERecipient(alg, encryptionKey.Key) + recipient.keyID = encryptionKey.KeyID +@@ -450,13 +454,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) + return nil, errors.New("go-jose/go-jose: too many recipients in payload; expecting only one") + } + +- critical, err := headers.getCritical() ++ err := headers.checkNoCritical() + if err != nil { +- return nil, fmt.Errorf("go-jose/go-jose: invalid crit header") +- } +- +- if len(critical) > 0 { +- return nil, fmt.Errorf("go-jose/go-jose: unsupported crit header") ++ return nil, err + } + + key, err := tryJWKS(decryptionKey, obj.Header) +@@ -523,13 +523,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) + func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) { + globalHeaders := obj.mergedHeaders(nil) + +- critical, err := globalHeaders.getCritical() ++ err := globalHeaders.checkNoCritical() + if err != nil { +- return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: invalid crit header") +- } +- +- if len(critical) > 0 { +- return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported crit header") ++ return -1, Header{}, nil, err + } + + key, err := tryJWKS(decryptionKey, obj.Header) +diff --git a/vendor/github.com/go-jose/go-jose/v4/jwe.go b/vendor/github.com/go-jose/go-jose/v4/jwe.go +index 9f1322d..6102f91 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/jwe.go ++++ b/vendor/github.com/go-jose/go-jose/v4/jwe.go +@@ -274,7 +274,7 @@ func validateAlgEnc(headers rawHeader, keyAlgorithms []KeyAlgorithm, contentEncr + if alg != "" && !containsKeyAlgorithm(keyAlgorithms, alg) { + return fmt.Errorf("unexpected key algorithm %q; expected %q", alg, keyAlgorithms) + } +- if alg != "" && !containsContentEncryption(contentEncryption, enc) { ++ if enc != "" && !containsContentEncryption(contentEncryption, enc) { + return fmt.Errorf("unexpected content encryption algorithm %q; expected %q", enc, contentEncryption) + } + return nil +@@ -288,11 +288,20 @@ func ParseEncryptedCompact( + keyAlgorithms []KeyAlgorithm, + contentEncryption []ContentEncryption, + ) (*JSONWebEncryption, error) { +- // Five parts is four separators +- if strings.Count(input, ".") != 4 { +- return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts") ++ var parts [5]string ++ var ok bool ++ ++ for i := range 4 { ++ parts[i], input, ok = strings.Cut(input, ".") ++ if !ok { ++ return nil, errors.New("go-jose/go-jose: compact JWE format must have five parts") ++ } ++ } ++ // Validate that the last part does not contain more dots ++ if strings.ContainsRune(input, '.') { ++ return nil, errors.New("go-jose/go-jose: compact JWE format must have five parts") + } +- parts := strings.SplitN(input, ".", 5) ++ parts[4] = input + + rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0]) + if err != nil { +diff --git a/vendor/github.com/go-jose/go-jose/v4/jwk.go b/vendor/github.com/go-jose/go-jose/v4/jwk.go +index 9e57e93..164d6a1 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/jwk.go ++++ b/vendor/github.com/go-jose/go-jose/v4/jwk.go +@@ -175,6 +175,8 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { + } + + // UnmarshalJSON reads a key from its JSON representation. ++// ++// Returns ErrUnsupportedKeyType for unrecognized or unsupported "kty" header values. + func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { + var raw rawJSONWebKey + err = json.Unmarshal(data, &raw) +@@ -228,7 +230,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { + } + key, err = raw.symmetricKey() + case "OKP": +- if raw.Crv == "Ed25519" && raw.X != nil { ++ if raw.Crv == "Ed25519" { + if raw.D != nil { + key, err = raw.edPrivateKey() + if err == nil { +@@ -238,17 +240,27 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { + key, err = raw.edPublicKey() + keyPub = key + } +- } else { +- return fmt.Errorf("go-jose/go-jose: unknown curve %s'", raw.Crv) + } +- default: +- return fmt.Errorf("go-jose/go-jose: unknown json web key type '%s'", raw.Kty) ++ case "": ++ // kty MUST be present ++ err = fmt.Errorf("go-jose/go-jose: missing json web key type") + } + + if err != nil { + return + } + ++ if key == nil { ++ // RFC 7517: ++ // 5. JWK Set Format ++ // ... ++ // Implementations SHOULD ignore JWKs within a JWK Set that use "kty" ++ // (key type) values that are not understood by them, that are missing ++ // required members, or for which values are out of the supported ++ // ranges. ++ return ErrUnsupportedKeyType ++ } ++ + if certPub != nil && keyPub != nil { + if !reflect.DeepEqual(certPub, keyPub) { + return errors.New("go-jose/go-jose: invalid JWK, public keys in key and x5c fields do not match") +@@ -581,10 +593,10 @@ func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) { + + func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) { + var missing []string +- switch { +- case key.D == nil: ++ if key.D == nil { + missing = append(missing, "D") +- case key.X == nil: ++ } ++ if key.X == nil { + missing = append(missing, "X") + } + +@@ -611,19 +623,21 @@ func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) { + + func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { + var missing []string +- switch { +- case key.N == nil: ++ if key.N == nil { + missing = append(missing, "N") +- case key.E == nil: ++ } ++ if key.E == nil { + missing = append(missing, "E") +- case key.D == nil: ++ } ++ if key.D == nil { + missing = append(missing, "D") +- case key.P == nil: ++ } ++ if key.P == nil { + missing = append(missing, "P") +- case key.Q == nil: ++ } ++ if key.Q == nil { + missing = append(missing, "Q") + } +- + if len(missing) > 0 { + return nil, fmt.Errorf("go-jose/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", ")) + } +@@ -698,8 +712,19 @@ func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) { + return nil, fmt.Errorf("go-jose/go-jose: unsupported elliptic curve '%s'", key.Crv) + } + +- if key.X == nil || key.Y == nil || key.D == nil { +- return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, missing x/y/d values") ++ var missing []string ++ if key.X == nil { ++ missing = append(missing, "X") ++ } ++ if key.Y == nil { ++ missing = append(missing, "Y") ++ } ++ if key.D == nil { ++ missing = append(missing, "D") ++ } ++ ++ if len(missing) > 0 { ++ return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, missing %s value(s)", strings.Join(missing, ", ")) + } + + // The length of this octet string MUST be the full size of a coordinate for +diff --git a/vendor/github.com/go-jose/go-jose/v4/jws.go b/vendor/github.com/go-jose/go-jose/v4/jws.go +index d09d8ba..c40bd3e 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/jws.go ++++ b/vendor/github.com/go-jose/go-jose/v4/jws.go +@@ -75,7 +75,14 @@ type Signature struct { + original *rawSignatureInfo + } + +-// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization. ++// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization. Validation fails if ++// the JWS is signed with an algorithm that isn't in the provided list of signature algorithms. ++// Applications should decide for themselves which signature algorithms are acceptable. If you're ++// not sure which signature algorithms your application might receive, consult the documentation of ++// the program which provides them or the protocol that you are implementing. You can also try ++// getting an example JWS and decoding it with a tool like https://jwt.io to see what its "alg" ++// header parameter indicates. The signature on the JWS does not get validated during parsing. Call ++// Verify() after parsing to validate the signature and obtain the payload. + // + // https://datatracker.ietf.org/doc/html/rfc7515#section-7 + func ParseSigned( +@@ -90,7 +97,14 @@ func ParseSigned( + return parseSignedCompact(signature, nil, signatureAlgorithms) + } + +-// ParseSignedCompact parses a message in JWS Compact Serialization. ++// ParseSignedCompact parses a message in JWS Compact Serialization. Validation fails if the JWS is ++// signed with an algorithm that isn't in the provided list of signature algorithms. Applications ++// should decide for themselves which signature algorithms are acceptable.If you're not sure which ++// signature algorithms your application might receive, consult the documentation of the program ++// which provides them or the protocol that you are implementing. You can also try getting an ++// example JWS and decoding it with a tool like https://jwt.io to see what its "alg" header ++// parameter indicates. The signature on the JWS does not get validated during parsing. Call ++// Verify() after parsing to validate the signature and obtain the payload. + // + // https://datatracker.ietf.org/doc/html/rfc7515#section-7.1 + func ParseSignedCompact( +@@ -101,6 +115,15 @@ func ParseSignedCompact( + } + + // ParseDetached parses a signed message in compact serialization format with detached payload. ++// Validation fails if the JWS is signed with an algorithm that isn't in the provided list of ++// signature algorithms. Applications should decide for themselves which signature algorithms are ++// acceptable. If you're not sure which signature algorithms your application might receive, consult ++// the documentation of the program which provides them or the protocol that you are implementing. ++// You can also try getting an example JWS and decoding it with a tool like https://jwt.io to see ++// what its "alg" header parameter indicates. The signature on the JWS does not get validated during ++// parsing. Call Verify() after parsing to validate the signature and obtain the payload. ++// ++// https://datatracker.ietf.org/doc/html/rfc7515#appendix-F + func ParseDetached( + signature string, + payload []byte, +@@ -181,6 +204,25 @@ func containsSignatureAlgorithm(haystack []SignatureAlgorithm, needle SignatureA + return false + } + ++// ErrUnexpectedSignatureAlgorithm is returned when the signature algorithm in ++// the JWS header does not match one of the expected algorithms. ++type ErrUnexpectedSignatureAlgorithm struct { ++ // Got is the signature algorithm found in the JWS header. ++ Got SignatureAlgorithm ++ expected []SignatureAlgorithm ++} ++ ++func (e *ErrUnexpectedSignatureAlgorithm) Error() string { ++ return fmt.Sprintf("unexpected signature algorithm %q; expected %q", e.Got, e.expected) ++} ++ ++func newErrUnexpectedSignatureAlgorithm(got SignatureAlgorithm, expected []SignatureAlgorithm) error { ++ return &ErrUnexpectedSignatureAlgorithm{ ++ Got: got, ++ expected: expected, ++ } ++} ++ + // sanitized produces a cleaned-up JWS object from the raw JSON. + func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgorithm) (*JSONWebSignature, error) { + if len(signatureAlgorithms) == 0 { +@@ -236,8 +278,7 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo + + alg := SignatureAlgorithm(signature.Header.Algorithm) + if !containsSignatureAlgorithm(signatureAlgorithms, alg) { +- return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q", +- alg, signatureAlgorithms) ++ return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms) + } + + if signature.header != nil { +@@ -285,8 +326,7 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo + + alg := SignatureAlgorithm(obj.Signatures[i].Header.Algorithm) + if !containsSignatureAlgorithm(signatureAlgorithms, alg) { +- return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q", +- alg, signatureAlgorithms) ++ return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms) + } + + if obj.Signatures[i].header != nil { +@@ -321,35 +361,43 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo + return obj, nil + } + ++const tokenDelim = "." ++ + // parseSignedCompact parses a message in compact format. + func parseSignedCompact( + input string, + payload []byte, + signatureAlgorithms []SignatureAlgorithm, + ) (*JSONWebSignature, error) { +- // Three parts is two separators +- if strings.Count(input, ".") != 2 { ++ protected, s, ok := strings.Cut(input, tokenDelim) ++ if !ok { // no period found ++ return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts") ++ } ++ claims, sig, ok := strings.Cut(s, tokenDelim) ++ if !ok { // only one period found ++ return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts") ++ } ++ if strings.ContainsRune(sig, '.') { // too many periods found + return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts") + } +- parts := strings.SplitN(input, ".", 3) + +- if parts[1] != "" && payload != nil { ++ if claims != "" && payload != nil { + return nil, fmt.Errorf("go-jose/go-jose: payload is not detached") + } + +- rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0]) ++ rawProtected, err := base64.RawURLEncoding.DecodeString(protected) + if err != nil { + return nil, err + } + + if payload == nil { +- payload, err = base64.RawURLEncoding.DecodeString(parts[1]) ++ payload, err = base64.RawURLEncoding.DecodeString(claims) + if err != nil { + return nil, err + } + } + +- signature, err := base64.RawURLEncoding.DecodeString(parts[2]) ++ signature, err := base64.RawURLEncoding.DecodeString(sig) + if err != nil { + return nil, err + } +diff --git a/vendor/github.com/go-jose/go-jose/v4/shared.go b/vendor/github.com/go-jose/go-jose/v4/shared.go +index 1ec3396..35130b3 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/shared.go ++++ b/vendor/github.com/go-jose/go-jose/v4/shared.go +@@ -77,6 +77,9 @@ var ( + + // ErrUnsupportedEllipticCurve indicates unsupported or unknown elliptic curve has been found. + ErrUnsupportedEllipticCurve = errors.New("go-jose/go-jose: unsupported/unknown elliptic curve") ++ ++ // ErrUnsupportedCriticalHeader is returned when a header is marked critical but not supported by go-jose. ++ ErrUnsupportedCriticalHeader = errors.New("go-jose/go-jose: unsupported critical header") + ) + + // Key management algorithms +@@ -167,8 +170,8 @@ const ( + ) + + // supportedCritical is the set of supported extensions that are understood and processed. +-var supportedCritical = map[string]bool{ +- headerB64: true, ++var supportedCritical = map[string]struct{}{ ++ headerB64: {}, + } + + // rawHeader represents the JOSE header for JWE/JWS objects (used for parsing). +@@ -346,6 +349,32 @@ func (parsed rawHeader) getCritical() ([]string, error) { + return q, nil + } + ++// checkNoCritical verifies there are no critical headers present. ++func (parsed rawHeader) checkNoCritical() error { ++ if _, ok := parsed[headerCritical]; ok { ++ return ErrUnsupportedCriticalHeader ++ } ++ ++ return nil ++} ++ ++// checkSupportedCritical verifies there are no unsupported critical headers. ++// Supported headers are passed in as a set: map of names to empty structs ++func (parsed rawHeader) checkSupportedCritical(supported map[string]struct{}) error { ++ crit, err := parsed.getCritical() ++ if err != nil { ++ return err ++ } ++ ++ for _, name := range crit { ++ if _, ok := supported[name]; !ok { ++ return ErrUnsupportedCriticalHeader ++ } ++ } ++ ++ return nil ++} ++ + // getS2C extracts parsed "p2c" from the raw JSON. + func (parsed rawHeader) getP2C() (int, error) { + v := parsed[headerP2C] +diff --git a/vendor/github.com/go-jose/go-jose/v4/signing.go b/vendor/github.com/go-jose/go-jose/v4/signing.go +index 3dec011..5dbd04c 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/signing.go ++++ b/vendor/github.com/go-jose/go-jose/v4/signing.go +@@ -404,15 +404,23 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter + } + + signature := obj.Signatures[0] +- headers := signature.mergedHeaders() +- critical, err := headers.getCritical() +- if err != nil { +- return err ++ ++ if signature.header != nil { ++ // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, ++ // 4.1.11. "crit" (Critical) Header Parameter ++ // "When used, this Header Parameter MUST be integrity ++ // protected; therefore, it MUST occur only within the JWS ++ // Protected Header." ++ err = signature.header.checkNoCritical() ++ if err != nil { ++ return err ++ } + } + +- for _, name := range critical { +- if !supportedCritical[name] { +- return ErrCryptoFailure ++ if signature.protected != nil { ++ err = signature.protected.checkSupportedCritical(supportedCritical) ++ if err != nil { ++ return err + } + } + +@@ -421,6 +429,7 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter + return ErrCryptoFailure + } + ++ headers := signature.mergedHeaders() + alg := headers.getSignatureAlgorithm() + err = verifier.verifyPayload(input, signature.Signature, alg) + if err == nil { +@@ -469,14 +478,22 @@ func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey + + outer: + for i, signature := range obj.Signatures { +- headers := signature.mergedHeaders() +- critical, err := headers.getCritical() +- if err != nil { +- continue ++ if signature.header != nil { ++ // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, ++ // 4.1.11. "crit" (Critical) Header Parameter ++ // "When used, this Header Parameter MUST be integrity ++ // protected; therefore, it MUST occur only within the JWS ++ // Protected Header." ++ err = signature.header.checkNoCritical() ++ if err != nil { ++ continue outer ++ } + } + +- for _, name := range critical { +- if !supportedCritical[name] { ++ if signature.protected != nil { ++ // Check for only supported critical headers ++ err = signature.protected.checkSupportedCritical(supportedCritical) ++ if err != nil { + continue outer + } + } +@@ -486,6 +503,7 @@ outer: + continue + } + ++ headers := signature.mergedHeaders() + alg := headers.getSignatureAlgorithm() + err = verifier.verifyPayload(input, signature.Signature, alg) + if err == nil { +diff --git a/vendor/github.com/go-jose/go-jose/v4/symmetric.go b/vendor/github.com/go-jose/go-jose/v4/symmetric.go +index a69103b..f2ff29e 100644 +--- a/vendor/github.com/go-jose/go-jose/v4/symmetric.go ++++ b/vendor/github.com/go-jose/go-jose/v4/symmetric.go +@@ -21,6 +21,7 @@ import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" ++ "crypto/pbkdf2" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" +@@ -30,8 +31,6 @@ import ( + "hash" + "io" + +- "golang.org/x/crypto/pbkdf2" +- + josecipher "github.com/go-jose/go-jose/v4/cipher" + ) + +@@ -330,7 +329,10 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie + + // derive key + keyLen, h := getPbkdf2Params(alg) +- key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h) ++ key, err := pbkdf2.Key(h, string(ctx.key), salt, ctx.p2c, keyLen) ++ if err != nil { ++ return recipientInfo{}, nil ++ } + + // use AES cipher with derived key + block, err := aes.NewCipher(key) +@@ -364,11 +366,21 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie + + // Decrypt the content encryption key. + func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { +- switch headers.getAlgorithm() { +- case DIRECT: +- cek := make([]byte, len(ctx.key)) +- copy(cek, ctx.key) +- return cek, nil ++ if recipient == nil { ++ return nil, fmt.Errorf("go-jose/go-jose: missing recipient") ++ } ++ ++ alg := headers.getAlgorithm() ++ if alg == DIRECT { ++ return bytes.Clone(ctx.key), nil ++ } ++ ++ encryptedKey := recipient.encryptedKey ++ if len(encryptedKey) == 0 { ++ return nil, fmt.Errorf("go-jose/go-jose: missing JWE Encrypted Key") ++ } ++ ++ switch alg { + case A128GCMKW, A192GCMKW, A256GCMKW: + aead := newAESGCM(len(ctx.key)) + +@@ -383,7 +395,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien + + parts := &aeadParts{ + iv: iv.bytes(), +- ciphertext: recipient.encryptedKey, ++ ciphertext: encryptedKey, + tag: tag.bytes(), + } + +@@ -399,7 +411,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien + return nil, err + } + +- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) ++ cek, err := josecipher.KeyUnwrap(block, encryptedKey) + if err != nil { + return nil, err + } +@@ -432,7 +444,10 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien + + // derive key + keyLen, h := getPbkdf2Params(alg) +- key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h) ++ key, err := pbkdf2.Key(h, string(ctx.key), salt, p2c, keyLen) ++ if err != nil { ++ return nil, err ++ } + + // use AES cipher with derived key + block, err := aes.NewCipher(key) +@@ -440,7 +455,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien + return nil, err + } + +- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) ++ cek, err := josecipher.KeyUnwrap(block, encryptedKey) + if err != nil { + return nil, err + } +diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go +deleted file mode 100644 +index 28cd99c..0000000 +--- a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go ++++ /dev/null +@@ -1,77 +0,0 @@ +-// Copyright 2012 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. +- +-/* +-Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC +-2898 / PKCS #5 v2.0. +- +-A key derivation function is useful when encrypting data based on a password +-or any other not-fully-random data. It uses a pseudorandom function to derive +-a secure encryption key based on the password. +- +-While v2.0 of the standard defines only one pseudorandom function to use, +-HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved +-Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To +-choose, you can pass the `New` functions from the different SHA packages to +-pbkdf2.Key. +-*/ +-package pbkdf2 +- +-import ( +- "crypto/hmac" +- "hash" +-) +- +-// Key derives a key from the password, salt and iteration count, returning a +-// []byte of length keylen that can be used as cryptographic key. The key is +-// derived based on the method described as PBKDF2 with the HMAC variant using +-// the supplied hash function. +-// +-// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you +-// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by +-// doing: +-// +-// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) +-// +-// Remember to get a good random salt. At least 8 bytes is recommended by the +-// RFC. +-// +-// Using a higher iteration count will increase the cost of an exhaustive +-// search but will also make derivation proportionally slower. +-func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { +- prf := hmac.New(h, password) +- hashLen := prf.Size() +- numBlocks := (keyLen + hashLen - 1) / hashLen +- +- var buf [4]byte +- dk := make([]byte, 0, numBlocks*hashLen) +- U := make([]byte, hashLen) +- for block := 1; block <= numBlocks; block++ { +- // N.B.: || means concatenation, ^ means XOR +- // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter +- // U_1 = PRF(password, salt || uint(i)) +- prf.Reset() +- prf.Write(salt) +- buf[0] = byte(block >> 24) +- buf[1] = byte(block >> 16) +- buf[2] = byte(block >> 8) +- buf[3] = byte(block) +- prf.Write(buf[:4]) +- dk = prf.Sum(dk) +- T := dk[len(dk)-hashLen:] +- copy(U, T) +- +- // U_n = PRF(password, U_(n-1)) +- for n := 2; n <= iter; n++ { +- prf.Reset() +- prf.Write(U) +- U = U[:0] +- U = prf.Sum(U) +- for x := range U { +- T[x] ^= U[x] +- } +- } +- } +- return dk[:keyLen] +-} +diff --git a/vendor/modules.txt b/vendor/modules.txt +index 1f5fab5..785271d 100644 +--- a/vendor/modules.txt ++++ b/vendor/modules.txt +@@ -281,8 +281,8 @@ github.com/fsnotify/fsnotify/internal + # github.com/fxamacker/cbor/v2 v2.7.0 + ## explicit; go 1.17 + github.com/fxamacker/cbor/v2 +-# github.com/go-jose/go-jose/v4 v4.0.5 +-## explicit; go 1.21 ++# github.com/go-jose/go-jose/v4 v4.1.4 ++## explicit; go 1.24.0 + github.com/go-jose/go-jose/v4 + github.com/go-jose/go-jose/v4/cipher + github.com/go-jose/go-jose/v4/json +@@ -605,7 +605,6 @@ golang.org/x/crypto/openpgp/elgamal + golang.org/x/crypto/openpgp/errors + golang.org/x/crypto/openpgp/packet + golang.org/x/crypto/openpgp/s2k +-golang.org/x/crypto/pbkdf2 + # golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + ## explicit; go 1.22.0 + golang.org/x/exp/constraints +-- +2.45.4 + diff --git a/SPECS/containerd2/CVE-2026-35469.patch b/SPECS/containerd2/CVE-2026-35469.patch new file mode 100644 index 00000000000..ab659cb6c93 --- /dev/null +++ b/SPECS/containerd2/CVE-2026-35469.patch @@ -0,0 +1,658 @@ +From 2d3ef6113ced4dd767d281944d176cb4bc92cc50 Mon Sep 17 00:00:00 2001 +From: jykanase +Date: Thu, 23 Apr 2026 07:02:38 +0000 +Subject: [PATCH] CVE-2026-35469 + +Upstream Patch Reference: https://github.com/moby/spdystream/compare/v0.5.0...v0.5.1.patch + +--- + go.mod | 2 +- + go.sum | 4 +- + vendor/github.com/moby/spdystream/NOTICE | 12 +++ + .../github.com/moby/spdystream/connection.go | 11 ++- + .../github.com/moby/spdystream/spdy/LICENSE | 27 +++++++ + .../github.com/moby/spdystream/spdy/PATENTS | 22 +++++ + .../moby/spdystream/spdy/dictionary.go | 16 ---- + .../moby/spdystream/spdy/options.go | 25 ++++++ + .../moby/spdystream/spdy/options_test.go | 33 ++++++++ + .../github.com/moby/spdystream/spdy/read.go | 58 ++++++++----- + .../github.com/moby/spdystream/spdy/types.go | 49 +++++++---- + .../github.com/moby/spdystream/spdy/write.go | 81 ++++++++++++------- + vendor/modules.txt | 2 +- + 13 files changed, 255 insertions(+), 87 deletions(-) + create mode 100644 vendor/github.com/moby/spdystream/spdy/LICENSE + create mode 100644 vendor/github.com/moby/spdystream/spdy/PATENTS + create mode 100644 vendor/github.com/moby/spdystream/spdy/options.go + create mode 100644 vendor/github.com/moby/spdystream/spdy/options_test.go + +diff --git a/go.mod b/go.mod +index 1e96a3d..96dccee 100644 +--- a/go.mod ++++ b/go.mod +@@ -114,7 +114,7 @@ require ( + github.com/mdlayher/socket v0.5.1 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect + github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect +- github.com/moby/spdystream v0.5.0 // indirect ++ github.com/moby/spdystream v0.5.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect +diff --git a/go.sum b/go.sum +index 7342f89..4806ddf 100644 +--- a/go.sum ++++ b/go.sum +@@ -224,8 +224,8 @@ github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkO + github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= + github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= + github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +-github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +-github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= ++github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= ++github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= + github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= + github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= + github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +diff --git a/vendor/github.com/moby/spdystream/NOTICE b/vendor/github.com/moby/spdystream/NOTICE +index b9b11c9..24e2e2a 100644 +--- a/vendor/github.com/moby/spdystream/NOTICE ++++ b/vendor/github.com/moby/spdystream/NOTICE +@@ -3,3 +3,15 @@ Copyright 2014-2021 Docker Inc. + + This product includes software developed at + Docker Inc. (https://www.docker.com/). ++ ++SPDY implementation (spdy/) ++ ++The spdy directory contains code derived from the Go project (golang.org/x/net). ++ ++Copyright 2009-2013 The Go Authors. ++Licensed under the BSD 3-Clause License. ++ ++Modifications Copyright 2014-2021 Docker Inc. ++ ++The BSD license text and Go patent grant are included in ++spdy/LICENSE and spdy/PATENTS. +diff --git a/vendor/github.com/moby/spdystream/connection.go b/vendor/github.com/moby/spdystream/connection.go +index 1394d0a..69ce477 100644 +--- a/vendor/github.com/moby/spdystream/connection.go ++++ b/vendor/github.com/moby/spdystream/connection.go +@@ -224,7 +224,13 @@ type Connection struct { + // NewConnection creates a new spdy connection from an existing + // network connection. + func NewConnection(conn net.Conn, server bool) (*Connection, error) { +- framer, framerErr := spdy.NewFramer(conn, conn) ++ return NewConnectionWithOptions(conn, server) ++} ++ ++// NewConnectionWithOptions creates a new spdy connection and applies frame ++// parsing limits via options. ++func NewConnectionWithOptions(conn net.Conn, server bool, opts ...spdy.FramerOption) (*Connection, error) { ++ framer, framerErr := spdy.NewFramerWithOptions(conn, conn, opts...) + if framerErr != nil { + return nil, framerErr + } +@@ -350,6 +356,9 @@ Loop: + } else { + debugMessage("(%p) EOF received", s) + } ++ if spdyErr, ok := err.(*spdy.Error); ok && spdyErr.Err == spdy.InvalidControlFrame { ++ _ = s.conn.Close() ++ } + break + } + var priority uint8 +diff --git a/vendor/github.com/moby/spdystream/spdy/LICENSE b/vendor/github.com/moby/spdystream/spdy/LICENSE +new file mode 100644 +index 0000000..6a66aea +--- /dev/null ++++ b/vendor/github.com/moby/spdystream/spdy/LICENSE +@@ -0,0 +1,27 @@ ++Copyright (c) 2009 The Go Authors. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are ++met: ++ ++ * Redistributions of source code must retain the above copyright ++notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above ++copyright notice, this list of conditions and the following disclaimer ++in the documentation and/or other materials provided with the ++distribution. ++ * Neither the name of Google Inc. nor the names of its ++contributors may be used to endorse or promote products derived from ++this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +diff --git a/vendor/github.com/moby/spdystream/spdy/PATENTS b/vendor/github.com/moby/spdystream/spdy/PATENTS +new file mode 100644 +index 0000000..7330990 +--- /dev/null ++++ b/vendor/github.com/moby/spdystream/spdy/PATENTS +@@ -0,0 +1,22 @@ ++Additional IP Rights Grant (Patents) ++ ++"This implementation" means the copyrightable works distributed by ++Google as part of the Go project. ++ ++Google hereby grants to You a perpetual, worldwide, non-exclusive, ++no-charge, royalty-free, irrevocable (except as stated in this section) ++patent license to make, have made, use, offer to sell, sell, import, ++transfer and otherwise run, modify and propagate the contents of this ++implementation of Go, where such license applies only to those patent ++claims, both currently owned or controlled by Google and acquired in ++the future, licensable by Google that are necessarily infringed by this ++implementation of Go. This grant does not include claims that would be ++infringed only as a consequence of further modification of this ++implementation. If you or your agent or exclusive licensee institute or ++order or agree to the institution of patent litigation against any ++entity (including a cross-claim or counterclaim in a lawsuit) alleging ++that this implementation of Go or any code incorporated within this ++implementation of Go constitutes direct or contributory patent ++infringement, or inducement of patent infringement, then any patent ++rights granted to you under this License for this implementation of Go ++shall terminate as of the date such litigation is filed. +diff --git a/vendor/github.com/moby/spdystream/spdy/dictionary.go b/vendor/github.com/moby/spdystream/spdy/dictionary.go +index 392232f..5a5ff0e 100644 +--- a/vendor/github.com/moby/spdystream/spdy/dictionary.go ++++ b/vendor/github.com/moby/spdystream/spdy/dictionary.go +@@ -1,19 +1,3 @@ +-/* +- Copyright 2014-2021 Docker Inc. +- +- 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. +-*/ +- + // Copyright 2013 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. +diff --git a/vendor/github.com/moby/spdystream/spdy/options.go b/vendor/github.com/moby/spdystream/spdy/options.go +new file mode 100644 +index 0000000..ec03e0b +--- /dev/null ++++ b/vendor/github.com/moby/spdystream/spdy/options.go +@@ -0,0 +1,25 @@ ++package spdy ++ ++// FramerOption allows callers to customize frame parsing limits. ++type FramerOption func(*Framer) ++ ++// WithMaxControlFramePayloadSize sets the control-frame payload limit. ++func WithMaxControlFramePayloadSize(size uint32) FramerOption { ++ return func(f *Framer) { ++ f.maxFrameLength = size ++ } ++} ++ ++// WithMaxHeaderFieldSize sets the per-header name/value size limit. ++func WithMaxHeaderFieldSize(size uint32) FramerOption { ++ return func(f *Framer) { ++ f.maxHeaderFieldSize = size ++ } ++} ++ ++// WithMaxHeaderCount sets the maximum number of headers in a frame. ++func WithMaxHeaderCount(count uint32) FramerOption { ++ return func(f *Framer) { ++ f.maxHeaderCount = count ++ } ++} +diff --git a/vendor/github.com/moby/spdystream/spdy/options_test.go b/vendor/github.com/moby/spdystream/spdy/options_test.go +new file mode 100644 +index 0000000..b0b98f2 +--- /dev/null ++++ b/vendor/github.com/moby/spdystream/spdy/options_test.go +@@ -0,0 +1,33 @@ ++package spdy ++ ++import ( ++ "net" ++ "testing" ++) ++ ++func TestNewFramerWithOptions(t *testing.T) { ++ serverConn, clientConn := net.Pipe() ++ defer func() { ++ _ = clientConn.Close() ++ _ = serverConn.Close() ++ }() ++ ++ framer, err := NewFramerWithOptions(clientConn, clientConn, ++ WithMaxControlFramePayloadSize(1024), ++ WithMaxHeaderFieldSize(128), ++ WithMaxHeaderCount(16), ++ ) ++ if err != nil { ++ t.Fatalf("Error creating spdy connection with options: %s", err) ++ } ++ ++ if got := framer.maxFrameLength; got != 1024 { ++ t.Fatalf("Unexpected MaxControlFramePayloadSize: %d", got) ++ } ++ if got := framer.maxHeaderFieldSize; got != 128 { ++ t.Fatalf("Unexpected MaxHeaderFieldSize: %d", got) ++ } ++ if got := framer.maxHeaderCount; got != 16 { ++ t.Fatalf("Unexpected MaxHeaderCount: %d", got) ++ } ++} +diff --git a/vendor/github.com/moby/spdystream/spdy/read.go b/vendor/github.com/moby/spdystream/spdy/read.go +index 75ea045..2abb694 100644 +--- a/vendor/github.com/moby/spdystream/spdy/read.go ++++ b/vendor/github.com/moby/spdystream/spdy/read.go +@@ -1,19 +1,3 @@ +-/* +- Copyright 2014-2021 Docker Inc. +- +- 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. +-*/ +- + // Copyright 2011 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. +@@ -24,6 +8,7 @@ import ( + "compress/zlib" + "encoding/binary" + "io" ++ "io/ioutil" + "net/http" + "strings" + ) +@@ -59,6 +44,11 @@ func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } ++ // Each setting is 8 bytes (4-byte id + 4-byte value). ++ // Payload is 4 bytes for numSettings + numSettings*8. ++ if h.length < 4 || numSettings > (h.length-4)/8 { ++ return &Error{InvalidControlFrame, 0} ++ } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { +@@ -177,8 +167,19 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) ( + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } ++ maxControlFramePayload := uint32(MaxDataLength) ++ if f.maxFrameLength > 0 { ++ maxControlFramePayload = f.maxFrameLength ++ } ++ + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff ++ if length > maxControlFramePayload { ++ if _, err := io.CopyN(ioutil.Discard, f.r, int64(length)); err != nil { ++ return nil, err ++ } ++ return nil, &Error{InvalidControlFrame, 0} ++ } + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { +@@ -190,11 +191,22 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) ( + return cframe, nil + } + +-func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { ++func (f *Framer) parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { + var numHeaders uint32 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } ++ maxHeaders := defaultMaxHeaderCount ++ if f.maxHeaderCount > 0 { ++ maxHeaders = f.maxHeaderCount ++ } ++ if numHeaders > maxHeaders { ++ return nil, &Error{InvalidControlFrame, streamId} ++ } ++ maxFieldSize := defaultMaxHeaderFieldSize ++ if f.maxHeaderFieldSize > 0 { ++ maxFieldSize = f.maxHeaderFieldSize ++ } + var e error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { +@@ -202,6 +214,9 @@ func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } ++ if length > maxFieldSize { ++ return nil, &Error{InvalidControlFrame, streamId} ++ } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err +@@ -217,6 +232,9 @@ func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } ++ if length > maxFieldSize { ++ return nil, &Error{InvalidControlFrame, streamId} ++ } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err +@@ -256,7 +274,7 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) + } + reader = f.headerDecompressor + } +- frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) ++ frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } +@@ -288,7 +306,7 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) e + } + reader = f.headerDecompressor + } +- frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) ++ frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } +@@ -320,7 +338,7 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) err + } + reader = f.headerDecompressor + } +- frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) ++ frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } +diff --git a/vendor/github.com/moby/spdystream/spdy/types.go b/vendor/github.com/moby/spdystream/spdy/types.go +index a254a43..a552861 100644 +--- a/vendor/github.com/moby/spdystream/spdy/types.go ++++ b/vendor/github.com/moby/spdystream/spdy/types.go +@@ -1,23 +1,9 @@ +-/* +- Copyright 2014-2021 Docker Inc. +- +- 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. +-*/ +- + // Copyright 2011 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. + ++// Modifications Copyright 2014-2021 Docker Inc. ++ + // Package spdy implements the SPDY protocol (currently SPDY/3), described in + // http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. + package spdy +@@ -63,8 +49,20 @@ const ( + ) + + // MaxDataLength is the maximum number of bytes that can be stored in one frame. ++// ++// SPDY frame headers encode the payload length using a 24-bit field, ++// so the maximum representable size for both data and control frames ++// is 2^24-1 bytes. ++// ++// See the SPDY/3 specification, "Frame Format": ++// https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1/ + const MaxDataLength = 1<<24 - 1 + ++const ( ++ defaultMaxHeaderFieldSize uint32 = 1 << 20 ++ defaultMaxHeaderCount uint32 = 1000 ++) ++ + // headerValueSepator separates multiple header values. + const headerValueSeparator = "\x00" + +@@ -269,6 +267,10 @@ type Framer struct { + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser ++ ++ maxFrameLength uint32 // overrides the default frame payload length limit. ++ maxHeaderFieldSize uint32 // overrides the default per-header name/value length limit. ++ maxHeaderCount uint32 // overrides the default header count limit. + } + + // NewFramer allocates a new Framer for a given SPDY connection, represented by +@@ -276,6 +278,16 @@ type Framer struct { + // from/to the Reader and Writer, so the caller should pass in an appropriately + // buffered implementation to optimize performance. + func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { ++ return newFramer(w, r) ++} ++ ++// NewFramerWithOptions allocates a new Framer for a given SPDY connection and ++// applies frame parsing limits via options. ++func NewFramerWithOptions(w io.Writer, r io.Reader, opts ...FramerOption) (*Framer, error) { ++ return newFramer(w, r, opts...) ++} ++ ++func newFramer(w io.Writer, r io.Reader, opts ...FramerOption) (*Framer, error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) + if err != nil { +@@ -287,5 +299,10 @@ func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { + headerCompressor: compressor, + r: r, + } ++ for _, opt := range opts { ++ if opt != nil { ++ opt(framer) ++ } ++ } + return framer, nil + } +diff --git a/vendor/github.com/moby/spdystream/spdy/write.go b/vendor/github.com/moby/spdystream/spdy/write.go +index ab6d91f..75084d3 100644 +--- a/vendor/github.com/moby/spdystream/spdy/write.go ++++ b/vendor/github.com/moby/spdystream/spdy/write.go +@@ -1,19 +1,3 @@ +-/* +- Copyright 2014-2021 Docker Inc. +- +- 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. +-*/ +- + // Copyright 2011 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. +@@ -23,6 +7,7 @@ package spdy + import ( + "encoding/binary" + "io" ++ "math" + "net/http" + "strings" + ) +@@ -63,13 +48,21 @@ func (frame *RstStreamFrame) write(f *Framer) (err error) { + func (frame *SettingsFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings +- frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) ++ payloadLen := len(frame.FlagIdValues)*8 + 4 ++ if payloadLen > MaxDataLength { ++ return &Error{InvalidControlFrame, 0} ++ } ++ frame.CFHeader.length = uint32(payloadLen) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } +- if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { ++ n := len(frame.FlagIdValues) ++ if uint64(n) > math.MaxUint32 { ++ return &Error{InvalidControlFrame, 0} ++ } ++ if err = binary.Write(f.w, binary.BigEndian, uint32(n)); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { +@@ -170,29 +163,41 @@ func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { + + func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { + n = 0 +- if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { ++ numHeaders := len(h) ++ if numHeaders > math.MaxInt32 { ++ return n, &Error{InvalidControlFrame, 0} ++ } ++ if err = binary.Write(w, binary.BigEndian, uint32(numHeaders)); err != nil { + return + } +- n += 2 ++ n += 4 + for name, values := range h { +- if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { ++ nameLen := len(name) ++ if nameLen > math.MaxInt32 { ++ return n, &Error{InvalidControlFrame, 0} ++ } ++ if err = binary.Write(w, binary.BigEndian, uint32(nameLen)); err != nil { + return + } +- n += 2 ++ n += 4 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } +- n += len(name) ++ n += nameLen + v := strings.Join(values, headerValueSeparator) +- if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { ++ vLen := len(v) ++ if vLen > math.MaxInt32 { ++ return n, &Error{InvalidControlFrame, 0} ++ } ++ if err = binary.Write(w, binary.BigEndian, uint32(vLen)); err != nil { + return + } +- n += 2 ++ n += 4 + if _, err = io.WriteString(w, v); err != nil { + return + } +- n += len(v) ++ n += vLen + } + return + } +@@ -216,7 +221,11 @@ func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream +- frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) ++ hLen := len(f.headerBuf.Bytes()) + 10 ++ if hLen > MaxDataLength { ++ return &Error{InvalidControlFrame, 0} ++ } ++ frame.CFHeader.length = uint32(hLen) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { +@@ -260,7 +269,11 @@ func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply +- frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) ++ hLen := len(f.headerBuf.Bytes()) + 4 ++ if hLen > MaxDataLength { ++ return &Error{InvalidControlFrame, 0} ++ } ++ frame.CFHeader.length = uint32(hLen) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { +@@ -295,7 +308,11 @@ func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders +- frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) ++ hLen := len(f.headerBuf.Bytes()) + 4 ++ if hLen > MaxDataLength { ++ return &Error{InvalidControlFrame, 0} ++ } ++ frame.CFHeader.length = uint32(hLen) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { +@@ -323,7 +340,11 @@ func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } +- flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) ++ dLen := len(frame.Data) ++ if dLen > MaxDataLength { ++ return &Error{InvalidDataFrame, frame.StreamId} ++ } ++ flagsAndLength := uint32(frame.Flags)<<24 | uint32(dLen) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } +diff --git a/vendor/modules.txt b/vendor/modules.txt +index 1f5fab5..b30f288 100644 +--- a/vendor/modules.txt ++++ b/vendor/modules.txt +@@ -372,7 +372,7 @@ github.com/mistifyio/go-zfs/v3 + # github.com/moby/locker v1.0.1 + ## explicit; go 1.13 + github.com/moby/locker +-# github.com/moby/spdystream v0.5.0 ++# github.com/moby/spdystream v0.5.1 + ## explicit; go 1.13 + github.com/moby/spdystream + github.com/moby/spdystream/spdy +-- +2.45.4 + diff --git a/SPECS/containerd2/containerd2.spec b/SPECS/containerd2/containerd2.spec index a645457fb43..742ba7049dc 100644 --- a/SPECS/containerd2/containerd2.spec +++ b/SPECS/containerd2/containerd2.spec @@ -5,7 +5,7 @@ Summary: Industry-standard container runtime Name: %{upstream_name}2 Version: 2.1.6 -Release: 1%{?dist} +Release: 2%{?dist} License: ASL 2.0 Group: Tools/Container URL: https://www.containerd.io @@ -19,6 +19,8 @@ Source2: containerd.toml Patch0: multi-snapshotters-support.patch Patch1: tardev-support.patch Patch2: fix-credential-leak-in-cri-errors.patch +Patch3: CVE-2026-35469.patch +Patch4: CVE-2026-34986.patch %{?systemd_requires} @@ -95,6 +97,10 @@ fi %dir /opt/containerd/lib %changelog +* Thu Apr 24 2026 Jyoti Kanase - 2.1.6-2 +- Modify CVE-2026-35469 patch for 2.1.6 +- Patch for CVE-2026-34986 + * Fri Apr 17 2026 Jyoti Kanase - 2.1.6-1 - Upgrade to 2.1.6 - Remove CVE patches fixed in upstream: CVE-2024-25621, CVE-2024-40635,