fix(core): reject video sizes whose frame does not fit an int32
NewVideoSource and Resize accepted 32768x16384 and larger frames, whose byte size wraps the SPA buffer size and the data maxsize.
- commit
- 648b1efadd05a6c40913c65f91230823fe2991c5
- author
- brice · · 2026-10-14 21:04 UTC
- parent
- f9ca745
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +12 -2
M internal/core/video.go +9 -2
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "errors" | |
| 5 | 5 | "fmt" | |
| 6 | 6 | "log/slog" | |
| 7 | + | "math" | |
| 7 | 8 | "strings" | |
| 8 | 9 | "sync" | |
| 9 | 10 | "sync/atomic" | |
| @@ -213,7 +214,7 @@ func (c *Core) NewVideoSource(cfg VideoConfig, h portin.VideoHandler) (*VideoSou | |||
| 213 | 214 | if c.ptr == nil || c.capi == nil { | |
| 214 | 215 | return nil, ErrVideoStreamCreate | |
| 215 | 216 | } | |
| 216 | - | if h == nil || cfg.Width <= 0 || cfg.Height <= 0 || cfg.Width > 1<<15 || cfg.Height > 1<<15 { | |
| 217 | + | if h == nil || !validVideoSize(cfg.Width, cfg.Height) { | |
| 217 | 218 | return nil, ErrVideoConfig | |
| 218 | 219 | } | |
| 219 | 220 | if cfg.MaxFPS <= 0 { | |
| @@ -387,6 +388,12 @@ func (s *VideoSource) Trigger() { | |||
| 387 | 388 | } | |
| 388 | 389 | } | |
| 389 | 390 | ||
| 391 | + | // validVideoSize reports whether a w×h frame of 4-byte pixels fits the | |
| 392 | + | // int32 sizes SPA buffers carry. | |
| 393 | + | func validVideoSize(w, h int) bool { | |
| 394 | + | return w > 0 && h > 0 && w <= 1<<15 && h <= 1<<15 && w*4*h <= math.MaxInt32 | |
| 395 | + | } | |
| 396 | + | ||
| 390 | 397 | // Resize renegotiates the stream with a new size. Buffers PipeWire replaces | |
| 391 | 398 | // stay valid while borrowed; hand them back with Queue or Release (ideally | |
| 392 | 399 | // resize when none is borrowed). | |
| @@ -394,7 +401,7 @@ func (s *VideoSource) Resize(w, h int) error { | |||
| 394 | 401 | if s.stream == nil { | |
| 395 | 402 | return ErrVideoResize | |
| 396 | 403 | } | |
| 397 | - | if w <= 0 || h <= 0 || w > 1<<15 || h > 1<<15 { | |
| 404 | + | if !validVideoSize(w, h) { | |
| 398 | 405 | return ErrVideoConfig | |
| 399 | 406 | } | |
| 400 | 407 | if _, err := spa.VideoEnumFormat(s.enumPod[:], w, h, s.cfg.MaxFPS, s.formats[:s.nformats]...); err != nil { | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "errors" | 4 | "errors" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | "log/slog" | 6 | "log/slog" | ||
| 7 | + | "math" | |||
| 7 | "strings" | 8 | "strings" | ||
| 8 | "sync" | 9 | "sync" | ||
| 9 | "sync/atomic" | 10 | "sync/atomic" | ||
| @@ -213,7 +214,7 @@ func (c *Core) NewVideoSource(cfg VideoConfig, h portin.VideoHandler) (*VideoSou | |||||
| 213 | if c.ptr == nil || c.capi == nil { | 214 | if c.ptr == nil || c.capi == nil { | ||
| 214 | return nil, ErrVideoStreamCreate | 215 | return nil, ErrVideoStreamCreate | ||
| 215 | } | 216 | } | ||
| 216 | - | if h == nil || cfg.Width <= 0 || cfg.Height <= 0 || cfg.Width > 1<<15 || cfg.Height > 1<<15 { | 217 | + | if h == nil || !validVideoSize(cfg.Width, cfg.Height) { |
| 217 | return nil, ErrVideoConfig | 218 | return nil, ErrVideoConfig | ||
| 218 | } | 219 | } | ||
| 219 | if cfg.MaxFPS <= 0 { | 220 | if cfg.MaxFPS <= 0 { | ||
| @@ -387,6 +388,12 @@ func (s *VideoSource) Trigger() { | |||||
| 387 | } | 388 | } | ||
| 388 | } | 389 | } | ||
| 389 | 390 | ||||
| 391 | + | // validVideoSize reports whether a w×h frame of 4-byte pixels fits the | |||
| 392 | + | // int32 sizes SPA buffers carry. | |||
| 393 | + | func validVideoSize(w, h int) bool { | |||
| 394 | + | return w > 0 && h > 0 && w <= 1<<15 && h <= 1<<15 && w*4*h <= math.MaxInt32 | |||
| 395 | + | } | |||
| 396 | + | ||||
| 390 | // Resize renegotiates the stream with a new size. Buffers PipeWire replaces | 397 | // Resize renegotiates the stream with a new size. Buffers PipeWire replaces | ||
| 391 | // stay valid while borrowed; hand them back with Queue or Release (ideally | 398 | // stay valid while borrowed; hand them back with Queue or Release (ideally | ||
| 392 | // resize when none is borrowed). | 399 | // resize when none is borrowed). | ||
| @@ -394,7 +401,7 @@ func (s *VideoSource) Resize(w, h int) error { | |||||
| 394 | if s.stream == nil { | 401 | if s.stream == nil { | ||
| 395 | return ErrVideoResize | 402 | return ErrVideoResize | ||
| 396 | } | 403 | } | ||
| 397 | - | if w <= 0 || h <= 0 || w > 1<<15 || h > 1<<15 { | 404 | + | if !validVideoSize(w, h) { |
| 398 | return ErrVideoConfig | 405 | return ErrVideoConfig | ||
| 399 | } | 406 | } | ||
| 400 | if _, err := spa.VideoEnumFormat(s.enumPod[:], w, h, s.cfg.MaxFPS, s.formats[:s.nformats]...); err != nil { | 407 | if _, err := spa.VideoEnumFormat(s.enumPod[:], w, h, s.cfg.MaxFPS, s.formats[:s.nformats]...); err != nil { | ||
M internal/core/video_test.go +3 -0
| @@ -447,6 +447,9 @@ func TestNewVideoSourceErrors(t *testing.T) { | |||
| 447 | 447 | if _, err := core.NewVideoSource(VideoConfig{Width: 0, Height: 10}, h); !errors.Is(err, ErrVideoConfig) { | |
| 448 | 448 | t.Errorf("zero width: %v", err) | |
| 449 | 449 | } | |
| 450 | + | if _, err := core.NewVideoSource(VideoConfig{Width: 1 << 15, Height: 1 << 14}, h); !errors.Is(err, ErrVideoConfig) { | |
| 451 | + | t.Errorf("frame size over int32: %v", err) | |
| 452 | + | } | |
| 450 | 453 | if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, nil); !errors.Is(err, ErrVideoConfig) { | |
| 451 | 454 | t.Errorf("nil handler: %v", err) | |
| 452 | 455 | } | |
| @@ -447,6 +447,9 @@ func TestNewVideoSourceErrors(t *testing.T) { | |||||
| 447 | if _, err := core.NewVideoSource(VideoConfig{Width: 0, Height: 10}, h); !errors.Is(err, ErrVideoConfig) { | 447 | if _, err := core.NewVideoSource(VideoConfig{Width: 0, Height: 10}, h); !errors.Is(err, ErrVideoConfig) { | ||
| 448 | t.Errorf("zero width: %v", err) | 448 | t.Errorf("zero width: %v", err) | ||
| 449 | } | 449 | } | ||
| 450 | + | if _, err := core.NewVideoSource(VideoConfig{Width: 1 << 15, Height: 1 << 14}, h); !errors.Is(err, ErrVideoConfig) { | |||
| 451 | + | t.Errorf("frame size over int32: %v", err) | |||
| 452 | + | } | |||
| 450 | if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, nil); !errors.Is(err, ErrVideoConfig) { | 453 | if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, nil); !errors.Is(err, ErrVideoConfig) { | ||
| 451 | t.Errorf("nil handler: %v", err) | 454 | t.Errorf("nil handler: %v", err) | ||
| 452 | } | 455 | } | ||