Skip to content

Commit 5fd67f9

Browse files
TerryHoweclaude
andauthored
fix(content): reject descriptor sizes exceeding 32 MiB in ReadAll (#1153)
## Summary - Adds a `maxDescriptorSize` constant (32 MiB) in `content/reader.go` - Rejects any descriptor whose `Size` exceeds this limit in `ReadAll()` with `ErrInvalidDescriptorSize`, the same error already returned for negative sizes - Adds `TestReadAll_OversizedDescriptor` to confirm a 2^62 size value is safely rejected without panic Fixes GHSA-f36w-mj3v-6jqv ### Root cause `ReadAll()` only checked for negative `Size` values. A crafted OCI layout `index.json` can supply a large positive `Size` (e.g. `4611686018427387904`, or 2^62), causing `make([]byte, desc.Size)` to trigger a Go runtime panic (`makeslice: len out of range`) before any allocation occurs. The `recover()` in `syncutil/once.go` unconditionally re-panics, so the ORAS process terminates unconditionally. This is reachable via any ORAS CLI command that accepts `--oci-layout`, `--from-oci-layout`, or `--input` flags (`cp`, `pull`, `restore`, `manifest fetch`, `repo tags`, `resolve`, `discover`). ### Fix ```go const maxDescriptorSize = 32 * 1024 * 1024 // 32 MiB func ReadAll(r io.Reader, desc ocispec.Descriptor) ([]byte, error) { if desc.Size < 0 || desc.Size > maxDescriptorSize { return nil, ErrInvalidDescriptorSize } ... } ``` ## Test plan - [x] `TestReadAll_OversizedDescriptor` — verifies `Size: 4611686018427387904` returns `ErrInvalidDescriptorSize` without panic - [x] All existing `TestReadAll_*` tests continue to pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Terry Howe <terrylhowe@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4683c46 commit 5fd67f9

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

content/reader.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ import (
2424
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2525
)
2626

27+
// maxDescriptorSize is the upper-bound for descriptor sizes accepted by
28+
// ReadAll. Descriptors sourced from attacker-supplied OCI layouts can carry
29+
// arbitrarily large Size values; without this cap, make([]byte, desc.Size)
30+
// triggers a runtime panic before any allocation occurs.
31+
const maxDescriptorSize = 32 * 1024 * 1024 // 32 MiB
32+
2733
var (
2834
// ErrInvalidDescriptorSize is returned by ReadAll() when
2935
// the descriptor has an invalid size.
@@ -119,7 +125,7 @@ func NewVerifyReader(r io.Reader, desc ocispec.Descriptor) *VerifyReader {
119125
// The read content is verified against the size and the digest
120126
// using a VerifyReader.
121127
func ReadAll(r io.Reader, desc ocispec.Descriptor) ([]byte, error) {
122-
if desc.Size < 0 {
128+
if desc.Size < 0 || desc.Size > maxDescriptorSize {
123129
return nil, ErrInvalidDescriptorSize
124130
}
125131
buf := make([]byte, desc.Size)

content/reader_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,17 @@ func TestReadAll_InvalidDescriptorSize(t *testing.T) {
260260
t.Errorf("ReadAll() error = %v, want %v", err, ErrInvalidDescriptorSize)
261261
}
262262
}
263+
264+
func TestReadAll_OversizedDescriptor(t *testing.T) {
265+
content := []byte("example content")
266+
desc := ocispec.Descriptor{
267+
MediaType: ocispec.MediaTypeImageLayer,
268+
Digest: digest.FromBytes(content),
269+
Size: 4611686018427387904, // 2^62, triggers makeslice panic without the cap
270+
}
271+
r := bytes.NewReader(content)
272+
_, err := ReadAll(r, desc)
273+
if err == nil || !errors.Is(err, ErrInvalidDescriptorSize) {
274+
t.Errorf("ReadAll() error = %v, want %v", err, ErrInvalidDescriptorSize)
275+
}
276+
}

0 commit comments

Comments
 (0)