Hey, a web page just restarted my phone!
Qualcomm: HTC One M7 device restart via WebGL, and flickering rectangles
[Part of a series of stories on GPU shader compiler bugs.]
We have used GLFuzz to test a device with a Qualcomm GPU: the HTC One M7 phone that runs Android, with an Adreno 320 GPU. GLFuzz found many issues, including device restarts. We have reported the issues below to Qualcomm, but have not yet received any response.
Do you see the flickering rectangle?
See if your phone suffers from the “flickering rectangle” issue by visiting this web page. Our HTC One M7 does.
Phone restart
Similar to our previous posts on the blue screen of death on an AMD desktop and the UI freeze on an NVIDIA desktop, we are able to cause the HTC phone to crash and restart via a web page that uses via WebGL. GLFuzz produced several shaders that cause device restarts. This works in both the stock browser and in Chrome. This video shows the issue:
As shown, the UI will typically become unresponsive for a few seconds before the phone crashes. The phone does not always crash; sometimes just the browser process crashes, although upon reopening the browser, the page is then immediately reloaded, increasing the chance of a device restart.
Just as it should not be possible to blue-screen your machine by visiting a web page, visiting a web page should not cause your phone to restart!
In the interest of responsible disclosure, we will not reveal the fragment shaders that cause device crashes.
Where did that flickering yellow rectangle come from!?
For those who have not read our introduction, our test application renders a flat, rectangle that fills the screen and “shades” it using a provided fragment shader. For example, we tested a fragment shader from GLSLSandbox.com that produces this image on our HTC device with a Qualcomm GPU:
GLFuzz produces a variant of the fragment shader that is identical, except for a few small semantics-preserving changes. On our HTC device, this causes the Qualcomm GPU to produce the following unexpected image:
A strange yellow rectangle has appeared and thus GLFuzz appears to have exposed a compiler bug.
In this case, GLFuzz added seven unreachable return statements in the variant shader of the form:
if(injectionSwitch.x > injectionSwitch.y) { return X;}
and one with the form:
if(false || (injectionSwitch.x > injectionSwitch.y)) { return X;}
where X is some literal (e.g. vec2(1.0)). Recall that “injectionSwitch.x > injectionSwitch.y” will be false at runtime, because injectionSwitch gets the value (0.0, 1.0), and thus these injections should make no difference.
This is a surprisingly large number of injections compared to other examples we have seen. The “false ||” is also interesting, as you would expect the compiler to optimise this away. Removing any of the injections (including “false ||”) causes the yellow rectangle (and thus the issue) to disappear.
Notably, the yellow rectangle also flickers, despite the fact that the image should be static! Check it out:
We have encountered such nondeterministic shaders before (e.g. an iPhone rendering garbage), and this is yet another indication that this is a bug.
The shaders are compatible with WebGL so you can easily view them in your browser. Let us know if you see any issues on your devices:
https://github.com/mc-imperial/shader-compiler-bugs/issues/9
The infamous red space scene
One of the shaders we tested produces the image of a space scene on most desktop platforms:
Yet, we found that many Android devices, including the HTC One M7, render this shader as a red image:
As noted in our ARM post, the variant shaders produced by GLFuzz sometimes “fix” the shader, causing the intended image to be rendered. When this happens, it is not always clear whether the original and variant shader pair demonstrate a shader compiler bug or in fact indicate a poorly written original shader (although it is often the former).
In the case of the red space scene, though, GLFuzz produced a variant that renders a black image:
Thus, the image is different from both the intended image and the original image on the HTC One M7. GLFuzz changed the following statement from the original shader:
vec4 newCol = (forCol2 + vec4(backCol2, 1.0)) * 1.0;
to the following (in the variant shader):
vec4 newCol = (forCol2 + vec4(backCol2, 1.0)) * (1.0 + injectionSwitch.x);
Recall that injectionSwitch.x will always be 0.0 at runtime, so this injection should make no difference to the rendered image.
The shaders are compatible with WebGL and so you can easily view them in your browser. Let us know if you see any issues on your devices:
https://github.com/mc-imperial/shader-compiler-bugs/issues/27
OK, that concludes our tour of shader compiler bugs in drivers from the seven main GPU designers — AMD, Apple, ARM, Imagination, Intel, NVIDIA and Qualcomm!
We hope you enjoyed the posts, and by popular request we will post some more stories about fuzzing open source Mesa drivers soon.