Package: xscreensaver-gl
Version: 6.09+dfsg1-2
Severity: minor
Tags: patch

glsnake's lighting depends on uninitialised stack memory.
gl_init declares `float light_pos[][3]` and passes each row to 
glLightfv(GL_POSITION), which reads four floats.
Light 0's w happens to be light_pos[1][0], which is 0.0.
Light 1's w comes from past the array; valgrind reports "Conditional jump or 
move depends on uninitialised value(s)" from a stack allocation in gl_init.

The value is the low half of a pointer, so the picture changes between launches.
A large w puts light 1 at the viewer, a small one puts it far off along (0, 20, 
-1) or the opposite way.
With address randomisation on, 25 frames gave 9 different pictures; the light 
at the viewer was the most common, 17 of 25.
With it off (setarch -R), 6 of 6 runs drew the same picture, but preloading a 
library that draws nothing changed 5.07 % of the pixels.
The same code is in 6.17, the current upstream release.

The attached patch gives both rows a w: light 0 keeps w = 0, and light 1 sits 
at the viewer, the picture most launches showed.
Three pictures attached: light 1 as the sun (w = 0), at the viewer, and as a 
lamp at (0, 20, -1).
In our own build the user can also pick among the three; that part is our 
choice and not part of this report.

I found and measured this with the help of an AI assistant (Claude).

Daniel


These runs were in a Debian sid container; the kernel shown is the host's.

-- System Information:
Debian Release: forky/sid
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 7.3.0-rc6-stereo3d8 (SMP w/12 CPU threads; PREEMPT)
Kernel taint flags: TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=C, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: unable to detect

Versions of packages xscreensaver-gl depends on:
ii  libc6                   2.43-7
ii  libcap2-bin             1:2.78-1+b1
ii  libegl1                 1.7.0-3+b1
ii  libgdk-pixbuf-2.0-0     2.44.8+dfsg-1
ii  libgl1                  1.7.0-3+b1
ii  libglib2.0-0t64         2.90.1-2
ii  libglu1-mesa [libglu1]  9.0.2-1.1+b4
ii  libwww-perl             6.83-1
ii  libx11-6                2:1.8.13-1
ii  libxext6                2:1.3.4-1+b4
ii  libxft2                 2.3.6-1+b5
ii  libxt6t64               1:1.2.1-1.3+b2

xscreensaver-gl recommends no packages.

Versions of packages xscreensaver-gl suggests:
pn  xscreensaver  <none>
Description: glsnake: give both light positions a w component
 glLightfv(GL_POSITION) reads four floats, but light_pos held three per
 light. Light 0's w was light_pos[1][0], which is 0.0. Light 1's w came from
 past the array, from stack memory that gl_init() never sets: valgrind's
 memcheck reports "Conditional jump or move depends on uninitialised
 value(s)" in the OpenGL driver, with the value "created by a stack
 allocation" in gl_init().
 .
 That stray w is the low half of a pointer, so light 1 moved from run to run.
 With address randomisation on, 25 frames of earlier runs showed 9 different
 pictures. With it off (setarch -R), 6 of 6 stock runs drew the same picture,
 but preloading a library that draws nothing (an OpenGL layer with its output
 disabled) still changed 5.07% of the pixels, because it changes what was on
 the stack before gl_init().
 .
 A large |w| puts light 1 at the viewer. That is the picture in 17 of the 25
 frames and in all 6 runs with setarch -R. The authors' (0, 20, -1) only takes
 effect when w is small, and w = 0 would make light 1 a directional light
 that 1 of the 25 frames came near. Write light 0 out as it reads today, with
 w = 0, and put light 1 at the viewer, (0, 0, 0, 1): it draws that usual
 picture pixel for pixel, in every run.
 .
 XScreenSaver 6.17 has the same arrays, and memcheck reports the same read
 there (glsnake.c:1414).
Author: Daniel Ramos (Capitain Jack) <[email protected]>
Forwarded: no
Last-Update: 2026-10-17

--- a/hacks/glx/glsnake.c
+++ b/hacks/glx/glsnake.c
@@ -1368,7 +1368,7 @@ static void gl_init(
 #endif
 	     ) 
 {
-    float light_pos[][3] = {{0.0, 10.0, 20.0}, {0.0, 20.0, -1.0}};
+    float light_pos[][4] = {{0.0, 10.0, 20.0, 0.0}, {0.0, 0.0, 0.0, 1.0}};
     float light_dir[][3] = {{0.0, -10.0,-20.0}, {0.0,-20.0, 1.0}};
 
     glEnable(GL_DEPTH_TEST);

Reply via email to