diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-15 18:11:12 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-15 18:11:12 +0200 |
| commit | d5812ffa717cad99e12752b719a024f06b72b6af (patch) | |
| tree | 3bd332da4708d3b5eb97f6e6c43a63d917ba1495 /internal | |
| parent | 2a3916ce192d73bdd6f621cc3842782814351227 (diff) | |
| download | notifyd-d5812ffa717cad99e12752b719a024f06b72b6af.tar.gz notifyd-d5812ffa717cad99e12752b719a024f06b72b6af.zip | |
fix(notify): bound RowStride before the int64 guard
The dimension cap closed the named overflow, but stride was still
r.RowStride whenever RowStride >= Width*Channels, and asInt accepts an
int64 wire value. With Width=2, Height=2, RowStride=MaxInt the int64
need product wraps negative, the guard passes, and r.Data[y*stride:]
panics at y=1. Reject a negative stride and any stride above
Width*Channels+4096, which is the real packing plus alignment slack.
After the cap and this bound neither need nor y*stride can overflow, so
the comment now says so.
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/notify/image.go | 18 | ||||
| -rw-r--r-- | internal/notify/image_test.go | 17 |
2 files changed, 31 insertions, 4 deletions
diff --git a/internal/notify/image.go b/internal/notify/image.go index 9dd750e..575f52b 100644 --- a/internal/notify/image.go +++ b/internal/notify/image.go @@ -109,10 +109,14 @@ func asInt(v any) (int, bool) { } // maxImageDimension caps a client-supplied width or height. A notification -// icon is small, and the cap keeps every later width*channels and y*stride -// product far from overflow. +// icon is small. const maxImageDimension = 1 << 16 +// maxRowStrideSlack bounds how far RowStride may exceed the tight packing of +// Width*Channels. Real strides are the tight width or that plus a small +// alignment pad. +const maxRowStrideSlack = 4096 + // PNG encodes the raw pixels as a PNG the renderer can load. func (r *RawImage) PNG() ([]byte, error) { if r.Width <= 0 || r.Height <= 0 { @@ -124,12 +128,18 @@ func (r *RawImage) PNG() ([]byte, error) { if r.BitsPerSample != 8 || (r.Channels != 3 && r.Channels != 4) { return nil, fmt.Errorf("notifyd: image bits=%d channels=%d", r.BitsPerSample, r.Channels) } + // RowStride is client-supplied and may arrive as int64. A negative or + // absurd stride would wrap both the length guard and the y*stride slice + // index, so bound it before use. stride := r.RowStride + if stride < 0 || stride > r.Width*r.Channels+maxRowStrideSlack { + return nil, fmt.Errorf("notifyd: image rowstride %d for %dx%d channels=%d", stride, r.Width, r.Height, r.Channels) + } if stride < r.Width*r.Channels { stride = r.Width * r.Channels } - // int64 keeps a hostile RowStride from wrapping the length comparison - // negative and passing the guard. + // Width/Height are capped and stride is bounded, so this product and the + // y*stride indexing below cannot overflow. need := int64(stride)*int64(r.Height-1) + int64(r.Width)*int64(r.Channels) if int64(len(r.Data)) < need { return nil, fmt.Errorf("notifyd: image data short: %d bytes", len(r.Data)) diff --git a/internal/notify/image_test.go b/internal/notify/image_test.go index 00dfb33..4f8c7ce 100644 --- a/internal/notify/image_test.go +++ b/internal/notify/image_test.go @@ -16,6 +16,7 @@ import ( "image" "image/color" "image/png" + "math" "testing" "github.com/godbus/dbus/v5" @@ -161,4 +162,20 @@ func TestRawImagePNGRejectsBadData(t *testing.T) { }).PNG(); err == nil { t.Fatal("oversized dimensions must error") } + // Small dimensions but RowStride at MaxInt: need wraps negative and the + // y*stride slice at y=1 panics. Must error, not panic. + if _, err := (&RawImage{ + Width: 2, Height: 2, RowStride: math.MaxInt, + BitsPerSample: 8, Channels: 4, + Data: []byte{0, 0, 0, 255, 0, 0, 0, 255}, + }).PNG(); err == nil { + t.Fatal("oversized rowstride must error") + } + if _, err := (&RawImage{ + Width: 2, Height: 1, RowStride: -1, + BitsPerSample: 8, Channels: 4, + Data: []byte{0, 0, 0, 255, 0, 0, 0, 255}, + }).PNG(); err == nil { + t.Fatal("negative rowstride must error") + } } |
