48
GraphicsFuzz Secure and Robust Graphics Rendering Hugues Evrard [email protected] .uk, Paul Thomson [email protected]

GraphicsFuzz - Khronos Group Secure and Robust Graphics Rendering ... semantics preserving diff/crash transformations. image image Generate bug-inducing shader graphics driver

  • Upload
    lehuong

  • View
    234

  • Download
    0

Embed Size (px)

Citation preview

GraphicsFuzzSecure and Robust Graphics Rendering

Hugues Evrard [email protected], Paul Thomson [email protected]

Problem: defective drivers

We are here to help!

How: automatic generation of minimal difference test cases

The context

graphics drivershader compiler

GPUoriginalshader

image

Challenge 1: is there a bug? (oracle problem)

graphics drivershader compiler

originalshader

1. Compiler crash

Crash

Challenge 1: is there a bug? (oracle problem)

graphics drivershader compiler

GPUoriginalshader

image

1. Compiler crash2. Miscompilation: much harder to detect

• bad image• security issues

????

Challenge 2: where is the bug?

graphics drivershader compiler

GPUoriginalshader

badimage

WebGL OpenGL IR GPU asm

Challenge 3: is the bug important?

graphics drivershader compiler

randomshader

high-valueshader

• Randomly searching may be ineffective• We start searching from high-value shaders

• Prioritizes issues that are likely to affect end-users

Our approach

Generate bug-inducing shader

graphics driver(shader compiler)

GPU

originalshader

image

imageimage

Generate bug-inducing shader

graphics driver(shader compiler)

GPU

variantshader

originalshader

image

x = y + 0 ;

z = x * 1 ;

semantics preservingtransformations

imageimage

Generate bug-inducing shader

graphics driver(shader compiler)

GPU

variantshader

originalshader

semantics preservingtransformations

image

imageimageimage

imageimage

Generate bug-inducing shader

graphics driver(shader compiler)

GPU

variantshader

originalshader

image

imageimageimage

comparisonsemantics preservingtransformations

GPU

imageimage

Generate bug-inducing shader

graphics driver(shader compiler)

variantshader

originalshader

image

imageimageimage

diff/crashsemantics preservingtransformations

imageimage

Generate bug-inducing shader

graphics driver(shader compiler)

GPU

variantshader

originalshader

image

imageimageimage

comparisonsemantics preservingtransformations

metamorphic testing

Transformations

Semantics-preserving transformations

• prelude: opaque expression [host] vec2 opaq = { 0.0, 1.0 };

[device] kernel main (…, opaq) {

( opaq.x < opaq.y ) opaq_True

( opaq.x > opaq.y ) opaq_False

opaq.x opaq_0

opaq.y opaq_1

}

Opaque value unknown at shader compilation time

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

int orig_y;

if (opaq_False) {

float donor_x;

// code from donor shader

orig_y = donor_x;

}

Donor code variables are either:• declared at start of block (donor_x)• replaced by existing variable with same type (orig_y)

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

• dead jump injection

if (opaq_False) {

jump;

}

Complicate the control flow graphjump: break / continue / return / discard

(special case of dead code injection)

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

• dead jump injection

• live code injection

// … original code

// donor code to be executed

// … original code

Add donor code at random position:• rename variables to avoid name clashes• declare variables• remove: break / continue / return / discard

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

• dead jump injection

• live code injection

• expression mutation

(opaq_True ?

[original expression]

: [random expression]

)

Random expression via “traditional” fuzzing

[original expression] + opaq_0;

For numeric expressions:

( [original expression] || opaq_False);

For boolean expressions:

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

• dead jump injection

• live code injection

• expression mutation

• vectorization

float a, c;

vec2 b;

b.x = a + b.y + c;

Pack variables into a merged variable:

vec4 abc; // abc == { a, b.x, b.y, c }

abc.yz.x = abc.x + abc.yz.y + abc.w;

• prelude: opaque expression

• dead code injection

• dead jump injection

• live code injection

• expression mutation

• vectorization

• control flow wrapping

if (opaq_True) { [original code] }

for (int tmp = opaq_0; tmp < opaq_1; tmp++) {

[original code]

}

do { [original code] } while (opaq_False);

Semantics-preserving transformations

Semantics-preserving transformations

• prelude: opaque expression

• dead code injection

• dead jump injection

• live code injection

• expression mutation

• vectorization

• control flow wrapping

if (opaq_True) { [original code] }

for (int tmp = opaq_0; tmp < opaq_1; tmp++) {

[original code]

}

do { [original code] } while (opaq_False);

Transformations can be applied recursively!

Reduction

void main(void){

vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;uv.x *= resolution.x / resolution.y;if(_GLF_DEAD(_GLF_FALSE(false, (injectionSwitch.x > injectionSwitch.y))))

return;vec3 finalColor = RenderScene(uv);if(_GLF_DEAD(_GLF_IDENTITY(false, (false) || false)))

{vec3 donor_replacementp = _GLF_FUZZED(faceforward(((++ finalColor) - faceforward(vec3(4.8, 7582.5251, -

3.4), vec3(-369.491, -9.0, 6172.7474), finalColor)), vec3(6108.1119, -181.078, 495.885), (finalColor).yzx));float donor_replacementtw = _GLF_FUZZED(sign(dot((EPS / vec3(53.44, 6.0, -752.725)), fract(finalColor))));float donor_replacementstrength = _GLF_FUZZED(38.04);float donor_replacementprev = _GLF_FUZZED(clamp((+ distance(time, -47.91)), (-- finalColor.g), (mouse /

EPS)[1]));if(_GLF_DEAD(_GLF_FALSE(false, (injectionSwitch.x > injectionSwitch.y))))

return;float donor_replacementaccum = _GLF_FUZZED(distance(vec2(-349.170, -4419.3875), (- vec4(-359.006, 69.29, -

96.95, -243.116)).wz));if(_GLF_DEAD(_GLF_IDENTITY(false, (false ? _GLF_FUZZED((-28449 < shadowType)) : false))))

return;for(

int i = 0;i < 16;++ i

){

float mag = dot(donor_replacementp, donor_replacementp);donor_replacementp = abs(donor_replacementp) / mag + vec3(- .5, - .8 + 0.1 * sin(time * 0.7 + 2.0),

- 1.1 + 0.3 * cos(time * 0.3));float w = exp(- float(i) / 7.);donor_replacementaccum += w * exp(- donor_replacementstrength * pow(abs(mag -

$ diff recipient_reduced_final.frag variant_27_reduced_final.frag6a7,8> uniform vec2 injectionSwitch;>168a171,174> if(injectionSwitch.x > injectionSwitch.y)> {> return vec3(1.0);> }194a201,204> if(injectionSwitch.x > injectionSwitch.y)> {> return;> }

GPU

Reduce to a minimal difference test case

graphics driver(shader compiler)

Bug-induc.shader

originalshader

image

image

diff/crash

while (diff/crash) {random_remove_transformation()

}

GPU

Minimal difference test case

graphics driver(shader compiler)

Bug-induc.shader

originalshader

image

image

diff/crashsame semanticssmall syntax diff

isolates the bug

Bug gallery

GraphicsFuzz: bug gallery

• AMD

if(injSwitch.x > injSwitch.y) {if(injSwitch.x > injSwitch.y) { return; }int f = 1;

}[..]if(injSwitch.x > injSwitch.y) { return; }

GraphicsFuzz: bug gallery

• AMD

• Apple

for(int c = 0; c < 1; c++) {if(j == 0) {return vec4(0.8, 0.5, 0.5, 1.0);

}}return vec4(0.8 * injSwitch.y, 0.7, 0.4, 1.0);

GraphicsFuzz: bug gallery

• AMD

• Apple

• ARM

if(injSwitch.x > injSwitch.y) {for(int i = 0; i < 1; i ++) {k = 0.0;

}}

GraphicsFuzz: bug gallery

• AMD

• Apple

• ARM

• Imagination

if(injSwitch.x > injSwitch.y) {for(int i = 0; i < 10; i ++) { continue; }

}[..]if(injSwitch.x > injSwitch.y) {if((p.z > 60.)) { break; }

}

GraphicsFuzz: bug gallery

• AMD

• Apple

• ARM

• Imagination

• Intel[..]vec2 uvs = [..];vec4 uvs_vec = vec4(0.0, uvs, 0.0);/* Replace uvs with uvs_vec.yz after */

GraphicsFuzz: bug gallery

• AMD

• Apple

• ARM

• Imagination

• Intel

• Nvidia vec3 hsbToRGB(float h, float s, float b) {return b * ((false ? (--s) : 1.0 ) - s)+ (b - (false ? (--s) : b * (1.0 - s) ))* clamp(abs(abs((false ? (--s) : 6.0 ) * [..];

}

GraphicsFuzz: bug gallery

• AMD

• Apple

• ARM

• Imagination

• Intel

• Nvidia

• Qualcomm

/* Multiple instances of the following,where v is a literal value */

if(injSwitch.x > injSwitch.y)return v;

Security bugs

Security bugs

• AMD: bluescreen

• Apple: iPhone rendering garbage

• Imagination: garbage rendering on Nexus Player

• Intel: bluescreen

• Nvidia: Ubuntu freeze

• Qualcomm: HTC One M7 restart via WebGL

Blog post series by Alastair on Medium : https://medium.com/@afd_icl/crashes-hangs-and-crazy-images-by-adding-zero-689d15ce922b

Team gallery

Alastair DonaldsonGroup Leader

Andrei LascuPhD student

Hugues EvrardPostDoc

Paul ThomsonPostDoc

MulticoreProgrammingGroup

http://multicore.doc.ic.ac.uk

Conclusion

Conclusion

• Our approach:• Automatically finds bugs• Produces a minimal difference test case

• C1: Is there a bug? Detect differences• metamorphic testing• semantics-preserving transformations

• C2: Where is the bug? Minimal difference test cases• Help to pinpoint bug location

• C3: Is the bug important/relevant? Start from high-value shaders• Prioritizes bugs that are close to being found in practice by developers

• Generic approach• beyond GLSL: HLSL, SPIR-V, Metal, …

• Can be used to amplify existing test suite

Future work

• More transformations

• Extend to other shader languages

• Focus search on security bugs

• Coverage-based fuzzing

• Fuzz for performance issues