Skip to content

hardening: GLSL background — latent raw-source sink lacks loop bound + uniform-tweak rebuilds the whole WebGL context (post-#11083 follow-ups) #11088

Description

@lalalune

Surfaced by a post-merge review of the programmable GLSL shader background shipped in #11083. The shipped paths are safe (the BACKGROUND agent action emits only a presetId/uniform patch — never GLSL text; the settings UI has no GLSL editor; presets use bounded ≤5-iteration loops; and there's real defense-in-depth: a static gate, an authoritative GL compile-validate before display, a frame-time watchdog, WebGL context-loss handling, and a CSS-color fallback). These are latent / hygiene follow-ups, not shipped-path exploits.

1. Latent raw-GLSL sink not fully bounded (defense-in-depth)

packages/ui/src/backgrounds/useBackgroundApplyChannel.ts accepts a raw source field from any background:apply broadcaster (the explicitSource branch). The static gate isPlausibleFragmentSource (packages/ui/src/backgrounds/shader-schema.ts) bans while/do and caps size at 16KB, but does not bound for-loop iteration counts — a crafted for(int i=0;i<200000;i++){...} passes the gate and the GL compile-validate, and can stall the GPU for one very long frame. The frame-time watchdog (ProgrammableShaderBackground.tsx:213-217) is best-effort: it only evaluates between rAF callbacks and needs 5 consecutive >120ms frames, so a single pathological frame can freeze the tab before the next callback runs.

No shipped caller sends GLSL text today, so this is only reachable if a future caller ships raw source through background:apply. Fix options: (a) drop the explicitSource branch entirely (presets are the only intended source), or (b) add a for-loop iteration-bound heuristic to isPlausibleFragmentSource.

2. Uniform-only tweaks rebuild the entire WebGL context (perf)

packages/ui/src/backgrounds/ProgrammableShaderBackground.tsx:227 — the single useEffect has deps [source, uniforms, color], so a uniform-only tweak (e.g. a "make it slower" glsl-tweak that keeps the same source) changes the uniforms object identity and tears down + rebuilds the whole THREE.WebGLRenderer + WebGL context + recompiles the shader instead of mutating uniformDefs.u_*.value. Rapid tweaks churn WebGL contexts (browsers cap ~16 live contexts → oldest force-killed) and add GC pressure. Fix: mutate live uniform values (and renderer size/color) in a lightweight effect keyed on the changed values; reserve the heavy build/compile path for actual source changes.

Minor (nits, from the same review)

  • rAF render loop has no explicit Page Visibility / IntersectionObserver pause (relies on the browser suspending rAF for hidden tabs; matters for an always-on occluded desktop/Electron window). ProgrammableShaderBackground.tsx:225.
  • On WebGL context loss, onContextRestored calls onFallback → permanent swap to the CSS color field until the background config changes; a user may expect the animated shader to resume after a transient GPU reset.

Evidence: multi-dimension review of #11083 at merge commit a9be4f4.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions