Bug 1217290 - Refactor WebGL max & min attribute constants for WebGL fingerprinting
MozReview-Commit-ID: 5fxOdV8euJ0
--- 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);
}