Bug 1217290 - Refactor WebGL max & min attribute constants for WebGL fingerprinting r=arthuredelstein,daoshengmu,jgilbert
authorChung-Sheng Fu <cfu@mozilla.com>
Thu, 27 Jul 2017 15:49:46 +0800
changeset 376601 61d074cfdeafd13395c089dcd4b2c47fb5d80204
parent 376600 3fc40b0c5e0ae244af61b1335c363f3c085e2e0f
child 376602 49462cfe24ecf1ee026a6fca2a9821c201488304
push id49532
push userryanvm@gmail.com
push dateThu, 24 Aug 2017 12:53:08 +0000
treeherderautoland@49462cfe24ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarthuredelstein, daoshengmu, jgilbert
bugs1217290
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1217290 - Refactor WebGL max & min attribute constants for WebGL fingerprinting r=arthuredelstein,daoshengmu,jgilbert MozReview-Commit-ID: 5fxOdV8euJ0
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextValidate.cpp
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -50,40 +50,16 @@
 #include "nsWrapperCache.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
 class nsIDocShell;
 
 /*
- * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
- *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
- *
- * Exceptions: some of the following values are set to higher values than in the spec because
- * the values in the spec are ridiculously low. They are explicitly marked below
- */
-#define MINVALUE_GL_MAX_TEXTURE_SIZE                  1024  // Different from the spec, which sets it to 64 on page 162
-#define MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE         512   // Different from the spec, which sets it to 16 on page 162
-#define MINVALUE_GL_MAX_VERTEX_ATTRIBS                8     // Page 164
-#define MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS      16    // Page 164
-#define MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS        128   // Page 164
-#define MINVALUE_GL_MAX_VARYING_VECTORS               8     // Page 164
-#define MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS           8     // Page 164
-#define MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS    0     // Page 164
-#define MINVALUE_GL_MAX_RENDERBUFFER_SIZE             1024  // Different from the spec, which sets it to 1 on page 164
-#define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS  8     // Page 164
-
-/*
- * Minimum value constants define in 6.2 State Tables of OpenGL ES - 3.0.4
- */
-#define MINVALUE_GL_MAX_3D_TEXTURE_SIZE             256
-#define MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS        256
-
-/*
  * WebGL-only GLenums
  */
 #define LOCAL_GL_BROWSER_DEFAULT_WEBGL                       0x9244
 #define LOCAL_GL_CONTEXT_LOST_WEBGL                          0x9242
 #define LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL               0x9247
 #define LOCAL_GL_UNPACK_COLORSPACE_CONVERSION_WEBGL          0x9243
 #define LOCAL_GL_UNPACK_FLIP_Y_WEBGL                         0x9240
 #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL              0x9241
@@ -1506,16 +1482,19 @@ public:
     GLuint MaxVertexAttribs() const {
         return mGLMaxVertexAttribs;
     }
 
     GLuint GLMaxTextureUnits() const {
         return mGLMaxTextureUnits;
     }
 
+    float mGLAliasedLineWidthRange[2];
+    float mGLAliasedPointSizeRange[2];
+
     bool IsFormatValidForFB(TexInternalFormat format) const;
 
 protected:
     // Represents current status of the context with respect to context loss.
     // That is, whether the context is lost, and what part of the context loss
     // process we currently are at.
     // This is used to support the WebGL spec's asyncronous nature in handling
     // context loss.
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -376,21 +376,17 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(refValue & stencilMask);
         }
 
         case LOCAL_GL_STENCIL_CLEAR_VALUE:
         case LOCAL_GL_UNPACK_ALIGNMENT:
         case LOCAL_GL_PACK_ALIGNMENT:
         case LOCAL_GL_SUBPIXEL_BITS:
         case LOCAL_GL_SAMPLE_BUFFERS:
-        case LOCAL_GL_SAMPLES:
-        case LOCAL_GL_MAX_VERTEX_ATTRIBS:
-        case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
-        case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
-        case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: {
+        case LOCAL_GL_SAMPLES: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
 
         case LOCAL_GL_RED_BITS:
         case LOCAL_GL_GREEN_BITS:
         case LOCAL_GL_BLUE_BITS:
@@ -409,16 +405,28 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(mGLMaxTextureSize);
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
             return JS::Int32Value(mGLMaxCubeMapTextureSize);
 
         case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
             return JS::Int32Value(mGLMaxRenderbufferSize);
 
+        case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
+            return JS::Int32Value(mGLMaxVertexTextureImageUnits);
+
+        case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
+            return JS::Int32Value(mGLMaxFragmentTextureImageUnits);
+
+        case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
+            return JS::Int32Value(mGLMaxCombinedTextureImageUnits);
+
+        case LOCAL_GL_MAX_VERTEX_ATTRIBS:
+            return JS::Int32Value(mGLMaxVertexAttribs);
+
         case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
             return JS::Int32Value(mGLMaxVertexUniformVectors);
 
         case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
             return JS::Int32Value(mGLMaxFragmentUniformVectors);
 
         case LOCAL_GL_MAX_VARYING_VECTORS:
             return JS::Int32Value(mGLMaxVaryingVectors);
@@ -489,25 +497,31 @@ WebGLContext::GetParameter(JSContext* cx
 
         ////////////////////////////////
         // Complex values
 
         // 2 floats
         case LOCAL_GL_DEPTH_RANGE:
         case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
         case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
-            GLenum driverPName = pname;
-            if (gl->IsCoreProfile() &&
-                driverPName == LOCAL_GL_ALIASED_POINT_SIZE_RANGE)
-            {
-                driverPName = LOCAL_GL_POINT_SIZE_RANGE;
+            GLfloat fv[2] = { 0 };
+            switch (pname) {
+            case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
+                fv[0] = mGLAliasedPointSizeRange[0];
+                fv[1] = mGLAliasedPointSizeRange[1];
+                break;
+            case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE:
+                fv[0] = mGLAliasedLineWidthRange[0];
+                fv[1] = mGLAliasedLineWidthRange[1];
+                break;
+            // case LOCAL_GL_DEPTH_RANGE:
+            default:
+                gl->fGetFloatv(pname, fv);
+                break;
             }
-
-            GLfloat fv[2] = { 0 };
-            gl->fGetFloatv(driverPName, fv);
             JSObject* obj = dom::Float32Array::Create(cx, this, 2, fv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // 4 floats
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -29,16 +29,76 @@
 #include "WebGLValidateStrings.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #if defined(MOZ_WIDGET_COCOA)
 #include "nsCocoaFeatures.h"
 #endif
 
+////////////////////
+// Minimum value constants defined in GLES 2.0.25 $6.2 "State Tables":
+const uint32_t kMinMaxVertexAttribs          =   8; // Page 164
+const uint32_t kMinMaxVertexUniformVectors   = 128; // Page 164
+const uint32_t kMinMaxFragmentUniformVectors =  16; // Page 164
+const uint32_t kMinMaxVaryingVectors         =   8; // Page 164
+
+const uint32_t kMinMaxVertexTextureImageUnits   = 0; // Page 164
+const uint32_t kMinMaxFragmentTextureImageUnits = 8; // Page 164
+const uint32_t kMinMaxCombinedTextureImageUnits = 8; // Page 164
+
+const uint32_t kMinMaxColorAttachments = 4;
+const uint32_t kMinMaxDrawBuffers      = 4;
+
+// These few deviate from the spec: (The minimum values in the spec are ridiculously low)
+const uint32_t kMinMaxTextureSize        = 1024; // ES2 spec says `64` (p162)
+const uint32_t kMinMaxCubeMapTextureSize =  512; // ES2 spec says `16` (p162)
+const uint32_t kMinMaxRenderbufferSize   = 1024; // ES2 spec says `1` (p164)
+
+// Minimum value constants defined in GLES 3.0.4 $6.2 "State Tables":
+const uint32_t kMinMax3DTextureSize      = 256;
+const uint32_t kMinMaxArrayTextureLayers = 256;
+
+////////////////////
+// "Common" but usable values to avoid WebGL fingerprinting:
+const uint32_t kCommonMaxTextureSize        = 2048;
+const uint32_t kCommonMaxCubeMapTextureSize = 2048;
+const uint32_t kCommonMaxRenderbufferSize   = 2048;
+
+const uint32_t kCommonMaxVertexTextureImageUnits   =  8;
+const uint32_t kCommonMaxFragmentTextureImageUnits =  8;
+const uint32_t kCommonMaxCombinedTextureImageUnits = 16;
+
+const uint32_t kCommonMaxVertexAttribs          =  16;
+const uint32_t kCommonMaxVertexUniformVectors   = 256;
+const uint32_t kCommonMaxFragmentUniformVectors = 224;
+const uint32_t kCommonMaxVaryingVectors         =   8;
+
+const uint32_t kCommonMaxViewportDims = 4096;
+
+// The following ranges came from a 2013 Moto E and an old macbook.
+const float kCommonAliasedPointSizeRangeMin =  1;
+const float kCommonAliasedPointSizeRangeMax = 63;
+const float kCommonAliasedLineWidthRangeMin =  1;
+const float kCommonAliasedLineWidthRangeMax =  5;
+
+template<class T>
+static bool
+RestrictCap(T* const cap, const T restrictedVal)
+{
+    if (*cap < restrictedVal) {
+        return false; // already too low!
+    }
+
+    *cap = restrictedVal;
+    return true;
+}
+
+////////////////////
+
 namespace mozilla {
 
 bool
 WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info)
 {
     switch (mode) {
     case LOCAL_GL_FUNC_ADD:
     case LOCAL_GL_FUNC_SUBTRACT:
@@ -449,16 +509,17 @@ WebGLContext::InitAndValidateGL(FailureR
         *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason };
         return false;
     }
 
     // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
     // even though the hardware supports much more.  The
     // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
     gl->GetUIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);
+    mGLMaxCombinedTextureImageUnits = mGLMaxTextureUnits;
 
     if (mGLMaxTextureUnits < 8) {
         const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %u is < 8!",
                                      mGLMaxTextureUnits);
         *out_failReason = { "FEATURE_FAILURE_WEBGL_T_UNIT", reason };
         return false;
     }
 
@@ -527,16 +588,86 @@ WebGLContext::InitAndValidateGL(FailureR
                                             maxFragmentInputComponents) / 4;
         } else {
             mGLMaxVaryingVectors = 16;
             // 16 = 64/4, and 64 is the min value for
             // maxVertexOutputComponents in the OpenGL 3.2 spec.
         }
     }
 
+    ////////////////
+
+    gl->fGetFloatv(LOCAL_GL_ALIASED_LINE_WIDTH_RANGE, mGLAliasedLineWidthRange);
+
+    const GLenum driverPName = gl->IsCoreProfile() ? LOCAL_GL_POINT_SIZE_RANGE
+                                                   : LOCAL_GL_ALIASED_POINT_SIZE_RANGE;
+    gl->fGetFloatv(driverPName, mGLAliasedPointSizeRange);
+
+    ////////////////
+
+    if (gfxPrefs::WebGLMinCapabilityMode()) {
+        bool ok = true;
+
+        ok &= RestrictCap(&mGLMaxVertexTextureImageUnits  , kMinMaxVertexTextureImageUnits);
+        ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits, kMinMaxFragmentTextureImageUnits);
+        ok &= RestrictCap(&mGLMaxCombinedTextureImageUnits, kMinMaxCombinedTextureImageUnits);
+
+        ok &= RestrictCap(&mGLMaxVertexAttribs         , kMinMaxVertexAttribs);
+        ok &= RestrictCap(&mGLMaxVertexUniformVectors  , kMinMaxVertexUniformVectors);
+        ok &= RestrictCap(&mGLMaxFragmentUniformVectors, kMinMaxFragmentUniformVectors);
+        ok &= RestrictCap(&mGLMaxVaryingVectors        , kMinMaxVaryingVectors);
+
+        ok &= RestrictCap(&mGLMaxColorAttachments, kMinMaxColorAttachments);
+        ok &= RestrictCap(&mGLMaxDrawBuffers     , kMinMaxDrawBuffers);
+
+        ok &= RestrictCap(&mGLMaxTextureSize       , kMinMaxTextureSize);
+        ok &= RestrictCap(&mGLMaxCubeMapTextureSize, kMinMaxCubeMapTextureSize);
+        ok &= RestrictCap(&mGLMax3DTextureSize     , kMinMax3DTextureSize);
+
+        ok &= RestrictCap(&mGLMaxArrayTextureLayers, kMinMaxArrayTextureLayers);
+        ok &= RestrictCap(&mGLMaxRenderbufferSize  , kMinMaxRenderbufferSize);
+
+        if (!ok) {
+            GenerateWarning("Unable to restrict WebGL limits to minimums.");
+            return false;
+        }
+
+        mDisableFragHighP = true;
+    } else if (nsContentUtils::ShouldResistFingerprinting()) {
+        bool ok = true;
+
+        ok &= RestrictCap(&mGLMaxTextureSize       , kCommonMaxTextureSize);
+        ok &= RestrictCap(&mGLMaxCubeMapTextureSize, kCommonMaxCubeMapTextureSize);
+        ok &= RestrictCap(&mGLMaxRenderbufferSize  , kCommonMaxRenderbufferSize);
+
+        ok &= RestrictCap(&mGLMaxVertexTextureImageUnits  , kCommonMaxVertexTextureImageUnits);
+        ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits, kCommonMaxFragmentTextureImageUnits);
+        ok &= RestrictCap(&mGLMaxCombinedTextureImageUnits, kCommonMaxCombinedTextureImageUnits);
+
+        ok &= RestrictCap(&mGLMaxVertexAttribs         , kCommonMaxVertexAttribs);
+        ok &= RestrictCap(&mGLMaxVertexUniformVectors  , kCommonMaxVertexUniformVectors);
+        ok &= RestrictCap(&mGLMaxFragmentUniformVectors, kCommonMaxFragmentUniformVectors);
+        ok &= RestrictCap(&mGLMaxVaryingVectors        , kCommonMaxVaryingVectors);
+
+        ok &= RestrictCap(&mGLAliasedLineWidthRange[0], kCommonAliasedLineWidthRangeMin);
+        ok &= RestrictCap(&mGLAliasedLineWidthRange[1], kCommonAliasedLineWidthRangeMax);
+        ok &= RestrictCap(&mGLAliasedPointSizeRange[0], kCommonAliasedPointSizeRangeMin);
+        ok &= RestrictCap(&mGLAliasedPointSizeRange[1], kCommonAliasedPointSizeRangeMax);
+
+        ok &= RestrictCap(&mGLMaxViewportDims[0], kCommonMaxViewportDims);
+        ok &= RestrictCap(&mGLMaxViewportDims[1], kCommonMaxViewportDims);
+
+        if (!ok) {
+            GenerateWarning("Unable to restrict WebGL limits in order to resist fingerprinting");
+            return false;
+        }
+    }
+
+    ////////////////
+
     if (gl->IsCompatibilityProfile()) {
         gl->fEnable(LOCAL_GL_POINT_SPRITE);
     }
 
     if (!gl->IsGLES()) {
         gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
     }