2010-07-02 Zhenyao Mo <zmo@google.com>
authorzmo@google.com <zmo@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Jul 2010 19:24:08 +0000 (19:24 +0000)
committerzmo@google.com <zmo@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Jul 2010 19:24:08 +0000 (19:24 +0000)
        Reviewed by Dimitri Glazkov.

        Fix issues in boundary situations for WebGLRenderingContext::drawArrays/drawElements
        https://bugs.webkit.org/show_bug.cgi?id=41473

        * fast/canvas/webgl/draw-arrays-out-of-bounds-expected.txt: Test against overflow, size-0 buffer, and count==0 situations.
        * fast/canvas/webgl/draw-arrays-out-of-bounds.html: Ditto.
        * fast/canvas/webgl/draw-elements-out-of-bounds-expected.txt: Ditto.
        * fast/canvas/webgl/draw-elements-out-of-bounds.html: Ditto.
2010-07-02  Zhenyao Mo  <zmo@google.com>

        Reviewed by Dimitri Glazkov.

        Fix issues in boundary situations for WebGLRenderingContext::drawArrays/drawElements
        https://bugs.webkit.org/show_bug.cgi?id=41473

        * WebCore.gypi: Add CheckedInt.h.
        * WebCore.xcodeproj/project.pbxproj: Add CheckedInt.h.
        * html/canvas/CheckedInt.h: Added support of safe integer operations.
        (mozilla::CheckedInt_internal::integer_type_manually_recorded_info::):
        (mozilla::CheckedInt_internal::is_unsupported_type::):
        (mozilla::CheckedInt_internal::):
        (mozilla::CheckedInt_internal::integer_traits::):
        (mozilla::CheckedInt_internal::integer_traits::min):
        (mozilla::CheckedInt_internal::integer_traits::max):
        (mozilla::CheckedInt_internal::has_sign_bit):
        (mozilla::CheckedInt_internal::binary_complement):
        (mozilla::CheckedInt_internal::is_in_range):
        (mozilla::CheckedInt_internal::is_add_valid):
        (mozilla::CheckedInt_internal::is_sub_valid):
        (mozilla::CheckedInt_internal::is_mul_valid):
        (mozilla::CheckedInt_internal::is_div_valid):
        (mozilla::CheckedInt::CheckedInt):
        (mozilla::CheckedInt::value):
        (mozilla::CheckedInt::valid):
        (mozilla::CheckedInt::operator -):
        (mozilla::CheckedInt::operator ==):
        (mozilla::CheckedInt::operator !=):
        (mozilla::operator /):
        (mozilla::cast_to_CheckedInt_impl::run):
        (mozilla::):
        (mozilla::cast_to_CheckedInt):
        (mozilla::operator ==):
        * html/canvas/WebGLRenderingContext.cpp:
        (WebCore::WebGLRenderingContext::validateIndexArrayConservative): Fix a bug against 0-size buffer situation.
        (WebCore::WebGLRenderingContext::drawArrays): Deal with overflows and count==0 situation.
        (WebCore::WebGLRenderingContext::drawElements): Deal with count==0 situation.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@62397 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/draw-arrays-out-of-bounds-expected.txt
LayoutTests/fast/canvas/webgl/draw-arrays-out-of-bounds.html
LayoutTests/fast/canvas/webgl/draw-elements-out-of-bounds-expected.txt
LayoutTests/fast/canvas/webgl/draw-elements-out-of-bounds.html
WebCore/ChangeLog
WebCore/WebCore.gypi
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/html/canvas/CheckedInt.h [new file with mode: 0644]
WebCore/html/canvas/WebGLRenderingContext.cpp

index 37d6531cb8e20207a6e75cbc13b77884a088deaf..c8dfb87f311d8b4f44be8c126ef24daaaa424817 100644 (file)
@@ -1,3 +1,15 @@
+2010-07-02  Zhenyao Mo  <zmo@google.com>
+
+        Reviewed by Dimitri Glazkov.
+
+        Fix issues in boundary situations for WebGLRenderingContext::drawArrays/drawElements
+        https://bugs.webkit.org/show_bug.cgi?id=41473
+
+        * fast/canvas/webgl/draw-arrays-out-of-bounds-expected.txt: Test against overflow, size-0 buffer, and count==0 situations.
+        * fast/canvas/webgl/draw-arrays-out-of-bounds.html: Ditto.
+        * fast/canvas/webgl/draw-elements-out-of-bounds-expected.txt: Ditto.
+        * fast/canvas/webgl/draw-elements-out-of-bounds.html: Ditto.
+
 2010-07-02  Zhenyao Mo  <zmo@google.com>
 
         Reviewed by Dimitri Glazkov.
index 54ca9f116e0d75760db454cc89dedaac5860be1b..092a128b43a53f81cf2af6c950bdaedfea66cf5f 100644 (file)
@@ -7,7 +7,7 @@ PASS context.drawArrays(context.TRIANGLES, 0, 10000) generated expected GL error
 PASS context.drawArrays(context.TRIANGLES, 0, -1) generated expected GL error: INVALID_VALUE.
 PASS context.drawArrays(context.TRIANGLES, -1, 0) generated expected GL error: INVALID_VALUE.
 PASS context.drawArrays(context.TRIANGLES, 0, 0) generated expected GL error: NO_ERROR.
-PASS context.drawArrays(context.TRIANGLES, 100, 0) generated expected GL error: NO_ERROR.
+PASS context.drawArrays(context.TRIANGLES, 100, 0) generated expected GL error: INVALID_OPERATION.
 
 Test buffer with 3 float vectors
 PASS context.checkFramebufferStatus(context.FRAMEBUFFER) is context.FRAMEBUFFER_COMPLETE
@@ -19,7 +19,12 @@ PASS context.drawArrays(context.TRIANGLES, 0, 10000) generated expected GL error
 PASS context.drawArrays(context.TRIANGLES, 0, -1) generated expected GL error: INVALID_VALUE.
 PASS context.drawArrays(context.TRIANGLES, -1, 0) generated expected GL error: INVALID_VALUE.
 PASS context.drawArrays(context.TRIANGLES, 0, 0) generated expected GL error: NO_ERROR.
-PASS context.drawArrays(context.TRIANGLES, 100, 0) generated expected GL error: NO_ERROR.
+PASS context.drawArrays(context.TRIANGLES, 100, 0) generated expected GL error: INVALID_OPERATION.
+PASS context.drawArrays(context.TRIANGLES, 0, 0xffffffff) generated expected GL error: INVALID_VALUE.
+PASS context.drawArrays(context.TRIANGLES, 0xffffffff, 1) generated expected GL error: INVALID_VALUE.
+PASS context.drawArrays(context.TRIANGLES, 0xffffffff, 0xffffffff) generated expected GL error: INVALID_VALUE.
+PASS context.drawArrays(context.TRIANGLES, 0x7fffffff, 1) generated expected GL error: INVALID_OPERATION.
+PASS context.drawArrays(context.TRIANGLES, 0x7fffffff, 0x7fffffff) generated expected GL error: INVALID_OPERATION.
 
 PASS successfullyParsed is true
 
index 88a81b58127099d5b32e2567dfce2dd9689ce159..ab7878b8561871a26716877082289b320fd1622a 100644 (file)
@@ -26,7 +26,7 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 0)");
-shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 100, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 100, 0)");
 
 debug("")
 debug("Test buffer with 3 float vectors")
@@ -41,7 +41,16 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 0)");
-shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 100, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 100, 0)");
+
+// 0xffffffff needs to convert to a 'long' IDL argument as -1, as per
+// WebIDL 4.1.7.  JS ToInt32(0xffffffff) == -1, which is the first step
+// of the conversion.  Thus INVALID_VALUE.
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, 0xffffffff)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 0xffffffff)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 1)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 0x7fffffff)");
 
 debug("")
 successfullyParsed = true;
index 256445bb9ee1cdac7a624a2a25fc40f85fdda344..4d341cb7dd30ac50c1ec2071808944c3826298b0 100644 (file)
@@ -21,7 +21,10 @@ PASS context.drawElements(context.TRIANGLES, 10000, context.UNSIGNED_BYTE, 0) ge
 PASS context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 0) generated expected GL error: INVALID_VALUE.
 PASS context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1) generated expected GL error: INVALID_VALUE.
 PASS context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1) generated expected GL error: INVALID_VALUE.
-PASS context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4) generated expected GL error: NO_ERROR.
+PASS context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4) generated expected GL error: INVALID_OPERATION.
+PASS context.drawElements(context.TRIANGLES, 0xffffffff, context.UNSIGNED_BYTE, 0) generated expected GL error: INVALID_VALUE.
+PASS context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0) generated expected GL error: INVALID_OPERATION.
+PASS context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0x7fffffff) generated expected GL error: INVALID_OPERATION.
 
 PASS successfullyParsed is true
 
index f34fa0e33125bfe55dffac47f4d00202a3e03e41..9ad809297c3520c0b2a2dfc2744318422389984e 100644 (file)
@@ -47,7 +47,10 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)");
-shouldGenerateGLError(context, context.NO_ERROR, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0xffffffff, context.UNSIGNED_BYTE, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0x7fffffff)");
 
 debug("")
 successfullyParsed = true;
index 3777462e1497877c8f6edbc256232eb51419daf4..381283727931625296c17963851280568bb87543 100644 (file)
@@ -1,3 +1,42 @@
+2010-07-02  Zhenyao Mo  <zmo@google.com>
+
+        Reviewed by Dimitri Glazkov.
+
+        Fix issues in boundary situations for WebGLRenderingContext::drawArrays/drawElements
+        https://bugs.webkit.org/show_bug.cgi?id=41473
+
+        * WebCore.gypi: Add CheckedInt.h.
+        * WebCore.xcodeproj/project.pbxproj: Add CheckedInt.h.
+        * html/canvas/CheckedInt.h: Added support of safe integer operations.
+        (mozilla::CheckedInt_internal::integer_type_manually_recorded_info::):
+        (mozilla::CheckedInt_internal::is_unsupported_type::):
+        (mozilla::CheckedInt_internal::):
+        (mozilla::CheckedInt_internal::integer_traits::):
+        (mozilla::CheckedInt_internal::integer_traits::min):
+        (mozilla::CheckedInt_internal::integer_traits::max):
+        (mozilla::CheckedInt_internal::has_sign_bit):
+        (mozilla::CheckedInt_internal::binary_complement):
+        (mozilla::CheckedInt_internal::is_in_range):
+        (mozilla::CheckedInt_internal::is_add_valid):
+        (mozilla::CheckedInt_internal::is_sub_valid):
+        (mozilla::CheckedInt_internal::is_mul_valid):
+        (mozilla::CheckedInt_internal::is_div_valid):
+        (mozilla::CheckedInt::CheckedInt):
+        (mozilla::CheckedInt::value):
+        (mozilla::CheckedInt::valid):
+        (mozilla::CheckedInt::operator -):
+        (mozilla::CheckedInt::operator ==):
+        (mozilla::CheckedInt::operator !=):
+        (mozilla::operator /):
+        (mozilla::cast_to_CheckedInt_impl::run):
+        (mozilla::):
+        (mozilla::cast_to_CheckedInt):
+        (mozilla::operator ==):
+        * html/canvas/WebGLRenderingContext.cpp:
+        (WebCore::WebGLRenderingContext::validateIndexArrayConservative): Fix a bug against 0-size buffer situation.
+        (WebCore::WebGLRenderingContext::drawArrays): Deal with overflows and count==0 situation.
+        (WebCore::WebGLRenderingContext::drawElements): Deal with count==0 situation.
+
 2010-07-02  Zhenyao Mo  <zmo@google.com>
 
         Reviewed by Dimitri Glazkov.
index d0d736da2d7ff41da15d5847c869ca1898ab422c..6011edd1a1f5ae0c2341c3161052655e9cd3e2f0 100644 (file)
             'html/canvas/Uint32Array.h',
             'html/canvas/Uint16Array.cpp',
             'html/canvas/Uint16Array.h',
+            'html/canvas/CheckedInt.h',
             'html/CollectionCache.cpp',
             'html/CollectionCache.h',
             'html/CollectionType.h',
index b3f6fcf5d2b9504c436c52bc7e4ceffcfcd8638d..fdfc6bdd7cc21e593a7ae4a57f2db78c66de1d9a 100644 (file)
                89CD029311C85B870070B791 /* JSBlobBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 89CD029111C85B870070B791 /* JSBlobBuilder.cpp */; };
                89CD029411C85B870070B791 /* JSBlobBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 89CD029211C85B870070B791 /* JSBlobBuilder.h */; };
                8A81BF8511DCFD9000DA2B98 /* ResourceLoadTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A81BF8411DCFD9000DA2B98 /* ResourceLoadTiming.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               8A844CFE11D3C17C0014065C /* NavigationTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8A844CFB11D3C17C0014065C /* NavigationTiming.cpp */; };
-               8A844CFF11D3C17C0014065C /* NavigationTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A844CFC11D3C17C0014065C /* NavigationTiming.h */; };
                8A844D0411D3C18E0014065C /* Performance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8A844D0111D3C18E0014065C /* Performance.cpp */; };
                8A844D0511D3C18E0014065C /* Performance.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A844D0211D3C18E0014065C /* Performance.h */; };
                8AF4E55511DC5A36000ED3DE /* Navigation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8AF4E55211DC5A36000ED3DE /* Navigation.cpp */; };
                9FFE3E7B11B59C5D0037874E /* MemoryInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FFE3E7911B59C5D0037874E /* MemoryInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9FFE3EA511B5A4390037874E /* JSMemoryInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9FFE3EA311B5A4390037874E /* JSMemoryInfo.cpp */; };
                9FFE3EA611B5A4390037874E /* JSMemoryInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FFE3EA411B5A4390037874E /* JSMemoryInfo.h */; };
+               A00B721A11DE6428008AB9FF /* CheckedInt.h in Headers */ = {isa = PBXBuildFile; fileRef = A00B721911DE6427008AB9FF /* CheckedInt.h */; };
                A136A00C1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A136A00A1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.cpp */; };
                A136A00D1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.h in Headers */ = {isa = PBXBuildFile; fileRef = A136A00B1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.h */; };
                A17C81220F2A5CF7005DAAEB /* HTMLElementFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A17C81200F2A5CF7005DAAEB /* HTMLElementFactory.cpp */; };
                89CD029111C85B870070B791 /* JSBlobBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBlobBuilder.cpp; sourceTree = "<group>"; };
                89CD029211C85B870070B791 /* JSBlobBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBlobBuilder.h; sourceTree = "<group>"; };
                8A81BF8411DCFD9000DA2B98 /* ResourceLoadTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceLoadTiming.h; sourceTree = "<group>"; };
-               8A844CFB11D3C17C0014065C /* NavigationTiming.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NavigationTiming.cpp; sourceTree = "<group>"; };
-               8A844CFC11D3C17C0014065C /* NavigationTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationTiming.h; sourceTree = "<group>"; };
-               8A844CFD11D3C17C0014065C /* NavigationTiming.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NavigationTiming.idl; sourceTree = "<group>"; };
                8A844D0111D3C18E0014065C /* Performance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Performance.cpp; sourceTree = "<group>"; };
                8A844D0211D3C18E0014065C /* Performance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Performance.h; sourceTree = "<group>"; };
                8A844D0311D3C18E0014065C /* Performance.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Performance.idl; sourceTree = "<group>"; };
                9FFE3E7C11B59C6E0037874E /* MemoryInfo.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MemoryInfo.idl; sourceTree = "<group>"; };
                9FFE3EA311B5A4390037874E /* JSMemoryInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMemoryInfo.cpp; sourceTree = "<group>"; };
                9FFE3EA411B5A4390037874E /* JSMemoryInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMemoryInfo.h; sourceTree = "<group>"; };
+               A00B721911DE6427008AB9FF /* CheckedInt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CheckedInt.h; path = canvas/CheckedInt.h; sourceTree = "<group>"; };
                A136A00A1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequestProgressEventThrottle.cpp; sourceTree = "<group>"; };
                A136A00B1134DBD200CC8D50 /* XMLHttpRequestProgressEventThrottle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLHttpRequestProgressEventThrottle.h; sourceTree = "<group>"; };
                A17C81200F2A5CF7005DAAEB /* HTMLElementFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLElementFactory.cpp; sourceTree = "<group>"; };
                49484FAE102CF01E00187DD3 /* canvas */ = {
                        isa = PBXGroup;
                        children = (
+                               A00B721911DE6427008AB9FF /* CheckedInt.h */,
                                49EECDCA10503C2300099FAB /* ArrayBuffer.cpp */,
                                49EECDCB10503C2300099FAB /* ArrayBuffer.h */,
                                49EECDCC10503C2300099FAB /* ArrayBuffer.idl */,
                                8A81BF8511DCFD9000DA2B98 /* ResourceLoadTiming.h in Headers */,
                                8AF4E55611DC5A36000ED3DE /* Navigation.h in Headers */,
                                8AF4E55C11DC5A63000ED3DE /* Timing.h in Headers */,
+                               A00B721A11DE6428008AB9FF /* CheckedInt.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/WebCore/html/canvas/CheckedInt.h b/WebCore/html/canvas/CheckedInt.h
new file mode 100644 (file)
index 0000000..861e8e6
--- /dev/null
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Benoit Jacob <bjacob@mozilla.com>
+ *  Jeff Muizelaar <jmuizelaar@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// Necessary modifications are made to the original CheckedInt.h file to remove
+// dependencies on prtypes.
+// Also, change define Mozilla_CheckedInt_h to CheckedInt_h, change namespace
+// from mozilla to WebCore for easier usage.
+
+#ifndef CheckedInt_h
+#define CheckedInt_h
+
+#include <climits>
+
+namespace WebCore {
+
+namespace CheckedInt_internal {
+
+/* we don't want to use std::numeric_limits here because int... types may not support it,
+ * depending on the platform, e.g. on certain platform they use nonstandard built-in types
+ */
+
+/*** Step 1: manually record information for all the types that we want to support
+ ***/
+
+struct unsupported_type {};
+
+template<typename T> struct integer_type_manually_recorded_info
+{
+    enum { is_supported = 0 };
+    typedef unsupported_type twice_bigger_type;
+};
+
+
+#define CHECKEDINT_REGISTER_SUPPORTED_TYPE(T,_twice_bigger_type)  \
+template<> struct integer_type_manually_recorded_info<T>       \
+{                                                              \
+    enum { is_supported = 1 };                                 \
+    typedef _twice_bigger_type twice_bigger_type;              \
+    static void TYPE_NOT_SUPPORTED_BY_CheckedInt() {}             \
+};
+
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(int8_t,   int16_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint8_t,  uint16_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(int16_t,  int32_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint16_t, uint32_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(int32_t,  int64_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint32_t, uint64_t)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(int64_t,  unsupported_type)
+CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint64_t, unsupported_type)
+
+
+/*** Step 2: record some info about a given integer type,
+ ***         including whether it is supported, whether a twice bigger integer type
+ ***         is supported, what that twice bigger type is, and some stuff as found
+ ***         in std::numeric_limits (which we don't use because int.. types may
+ ***         not support it, if they are defined directly from compiler built-in types).
+ ***/
+
+template<typename T> struct is_unsupported_type { enum { answer = 0 }; };
+template<> struct is_unsupported_type<unsupported_type> { enum { answer = 1 }; };
+
+template<typename T> struct integer_traits
+{
+    typedef typename integer_type_manually_recorded_info<T>::twice_bigger_type twice_bigger_type;
+
+    enum {
+        is_supported = integer_type_manually_recorded_info<T>::is_supported,
+        twice_bigger_type_is_supported
+            = is_unsupported_type<
+                  typename integer_type_manually_recorded_info<T>::twice_bigger_type
+              >::answer ? 0 : 1,
+        size = sizeof(T),
+        position_of_sign_bit = CHAR_BIT * size - 1,
+        is_signed = (T(-1) > T(0)) ? 0 : 1
+    };
+
+    static T min()
+    {
+        // bitwise ops may return a larger type, that's why we cast explicitly to T
+        return is_signed ? T(T(1) << position_of_sign_bit) : T(0);
+    }
+
+    static T max()
+    {
+        return ~min();
+    }
+};
+
+/*** Step 3: Implement the actual validity checks --- ideas taken from IntegerLib, code different.
+ ***/
+
+// bitwise ops may return a larger type, so it's good to use these inline helpers guaranteeing that
+// the result is really of type T
+
+template<typename T> inline T has_sign_bit(T x)
+{
+    return x >> integer_traits<T>::position_of_sign_bit;
+}
+
+template<typename T> inline T binary_complement(T x)
+{
+    return ~x;
+}
+
+template<typename T, typename U,
+         bool is_T_signed = integer_traits<T>::is_signed,
+         bool is_U_signed = integer_traits<U>::is_signed>
+struct is_in_range_impl {};
+
+template<typename T, typename U>
+struct is_in_range_impl<T, U, true, true>
+{
+    static T run(U x)
+    {
+        return (x <= integer_traits<T>::max()) &
+               (x >= integer_traits<T>::min());
+    }
+};
+
+template<typename T, typename U>
+struct is_in_range_impl<T, U, false, false>
+{
+    static T run(U x)
+    {
+        return x <= integer_traits<T>::max();
+    }
+};
+
+template<typename T, typename U>
+struct is_in_range_impl<T, U, true, false>
+{
+    static T run(U x)
+    {
+        if (sizeof(T) > sizeof(U))
+            return 1;
+        else
+            return x <= U(integer_traits<T>::max());
+    }
+};
+
+template<typename T, typename U>
+struct is_in_range_impl<T, U, false, true>
+{
+    static T run(U x)
+    {
+        if (sizeof(T) >= sizeof(U))
+            return x >= 0;
+        else
+            return x >= 0 && x <= U(integer_traits<T>::max());
+    }
+};
+
+template<typename T, typename U> inline T is_in_range(U x)
+{
+    return is_in_range_impl<T, U>::run(x);
+}
+
+template<typename T> inline T is_add_valid(T x, T y, T result)
+{
+    return integer_traits<T>::is_signed ?
+                        // addition is valid if the sign of x+y is equal to either that of x or that of y.
+                        // Beware! These bitwise operations can return a larger integer type, if T was a
+                        // small type like int8, so we explicitly cast to T.
+                        has_sign_bit(binary_complement(T((result^x) & (result^y))))
+                    :
+                        binary_complement(x) >= y;
+}
+
+template<typename T> inline T is_sub_valid(T x, T y, T result)
+{
+    return integer_traits<T>::is_signed ?
+                        // substraction is valid if either x and y have same sign, or x-y and x have same sign
+                        has_sign_bit(binary_complement(T((result^x) & (x^y))))
+                    :
+                        x >= y;
+}
+
+template<typename T,
+         bool is_signed =  integer_traits<T>::is_signed,
+         bool twice_bigger_type_is_supported = integer_traits<T>::twice_bigger_type_is_supported>
+struct is_mul_valid_impl {};
+
+template<typename T>
+struct is_mul_valid_impl<T, true, true>
+{
+    static T run(T x, T y)
+    {
+        typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type;
+        twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y);
+        return is_in_range<T>(product);
+    }
+};
+
+template<typename T>
+struct is_mul_valid_impl<T, false, true>
+{
+    static T run(T x, T y)
+    {
+        typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type;
+        twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y);
+        return is_in_range<T>(product);
+    }
+};
+
+template<typename T>
+struct is_mul_valid_impl<T, true, false>
+{
+    static T run(T x, T y)
+    {
+        const T max_value = integer_traits<T>::max();
+        const T min_value = integer_traits<T>::min();
+
+        if (x == 0 || y == 0) return true;
+
+        if (x > 0) {
+            if (y > 0)
+                return x <= max_value / y;
+            else
+                return y >= min_value / x;
+        } else {
+            if (y > 0)
+                return x >= min_value / y;
+            else
+                return y >= max_value / x;
+        }
+    }
+};
+
+template<typename T>
+struct is_mul_valid_impl<T, false, false>
+{
+    static T run(T x, T y)
+    {
+        const T max_value = integer_traits<T>::max();
+        if (x == 0 || y == 0) return true;
+        return x <= max_value / y;
+    }
+};
+
+template<typename T> inline T is_mul_valid(T x, T y, T /*result not used*/)
+{
+    return is_mul_valid_impl<T>::run(x, y);
+}
+
+template<typename T> inline T is_div_valid(T x, T y)
+{
+    return integer_traits<T>::is_signed ?
+                        // keep in mind that min/-1 is invalid because abs(min)>max
+                        y != 0 && (x != integer_traits<T>::min() || y != T(-1))
+                    :
+                        y != 0;
+}
+
+} // end namespace CheckedInt_internal
+
+
+/*** Step 4: Now define the CheckedInt class.
+ ***/
+
+/** \class CheckedInt
+  * \brief Integer wrapper class checking for integer overflow and other errors
+  * \param T the integer type to wrap. Can be any of int8_t, uint8_t, int16_t, uint16_t,
+  *          int32_t, uint32_t, int64_t, uint64_t.
+  *
+  * This class implements guarded integer arithmetic. Do a computation, then check that
+  * valid() returns true, you then have a guarantee that no problem, such as integer overflow,
+  * happened during this computation.
+  *
+  * The arithmetic operators in this class are guaranteed not to crash your app
+  * in case of a division by zero.
+  *
+  * For example, suppose that you want to implement a function that computes (x+y)/z,
+  * that doesn't crash if z==0, and that reports on error (divide by zero or integer overflow).
+  * You could code it as follows:
+    \code
+    bool compute_x_plus_y_over_z(int32_t x, int32_t y, int32_t z, int32_t *result)
+    {
+        CheckedInt<int32_t> checked_result = (CheckedInt<int32_t>(x) + y) / z;
+        *result = checked_result.value();
+        return checked_result.valid();
+    }
+    \endcode
+  *
+  * Implicit conversion from plain integers to checked integers is allowed. The plain integer
+  * is checked to be in range before being casted to the destination type. This means that the following
+  * lines all compile, and the resulting CheckedInts are correctly detected as valid or invalid:
+  * \code
+    CheckedInt<uint8_t> x(1);   // 1 is of type int, is found to be in range for uint8_t, x is valid
+    CheckedInt<uint8_t> x(-1);  // -1 is of type int, is found not to be in range for uint8_t, x is invalid
+    CheckedInt<int8_t> x(-1);   // -1 is of type int, is found to be in range for int8_t, x is valid
+    CheckedInt<int8_t> x(int16_t(1000)); // 1000 is of type int16_t, is found not to be in range for int8_t, x is invalid
+    CheckedInt<int32_t> x(uint32_t(123456789)); // 3123456789 is of type uint32_t, is found not to be in range
+                                             // for int32_t, x is invalid
+  * \endcode
+  * Implicit conversion from
+  * checked integers to plain integers is not allowed. As shown in the
+  * above example, to get the value of a checked integer as a normal integer, call value().
+  *
+  * Arithmetic operations between checked and plain integers is allowed; the result type
+  * is the type of the checked integer.
+  *
+  * Safe integers of different types cannot be used in the same arithmetic expression.
+  */
+template<typename T>
+class CheckedInt
+{
+protected:
+    T mValue;
+    T mIsValid; // stored as a T to limit the number of integer conversions when
+                // evaluating nested arithmetic expressions.
+
+    template<typename U>
+    CheckedInt(const U& value, bool isValid) : mValue(value), mIsValid(isValid)
+    {
+        CheckedInt_internal::integer_type_manually_recorded_info<T>
+            ::TYPE_NOT_SUPPORTED_BY_CheckedInt();
+    }
+
+public:
+    /** Constructs a checked integer with given \a value. The checked integer is initialized as valid or invalid
+      * depending on whether the \a value is in range.
+      *
+      * This constructor is not explicit. Instead, the type of its argument is a separate template parameter,
+      * ensuring that no conversion is performed before this constructor is actually called.
+      * As explained in the above documentation for class CheckedInt, this constructor checks that its argument is
+      * valid.
+      */
+    template<typename U>
+    CheckedInt(const U& value)
+        : mValue(value),
+          mIsValid(CheckedInt_internal::is_in_range<T>(value))
+    {
+        CheckedInt_internal::integer_type_manually_recorded_info<T>
+            ::TYPE_NOT_SUPPORTED_BY_CheckedInt();
+    }
+
+    /** Constructs a valid checked integer with uninitialized value */
+    CheckedInt() : mIsValid(1)
+    {
+        CheckedInt_internal::integer_type_manually_recorded_info<T>
+            ::TYPE_NOT_SUPPORTED_BY_CheckedInt();
+    }
+
+    /** \returns the actual value */
+    T value() const { return mValue; }
+
+    /** \returns true if the checked integer is valid, i.e. is not the result
+      * of an invalid operation or of an operation involving an invalid checked integer
+      */
+    bool valid() const { return mIsValid; }
+
+    /** \returns the sum. Checks for overflow. */
+    template<typename U> friend CheckedInt<U> operator +(const CheckedInt<U>& lhs, const CheckedInt<U>& rhs);
+    /** Adds. Checks for overflow. \returns self reference */
+    template<typename U> CheckedInt& operator +=(const U &rhs);
+    /** \returns the difference. Checks for overflow. */
+    template<typename U> friend CheckedInt<U> operator -(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
+    /** Substracts. Checks for overflow. \returns self reference */
+    template<typename U> CheckedInt& operator -=(const U &rhs);
+    /** \returns the product. Checks for overflow. */
+    template<typename U> friend CheckedInt<U> operator *(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
+    /** Multiplies. Checks for overflow. \returns self reference */
+    template<typename U> CheckedInt& operator *=(const U &rhs);
+    /** \returns the quotient. Checks for overflow and for divide-by-zero. */
+    template<typename U> friend CheckedInt<U> operator /(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
+    /** Divides. Checks for overflow and for divide-by-zero. \returns self reference */
+    template<typename U> CheckedInt& operator /=(const U &rhs);
+
+    /** \returns the opposite value. Checks for overflow. */
+    CheckedInt operator -() const
+    {
+        T result = -value();
+        /* give the compiler a good chance to perform RVO */
+        return CheckedInt(result,
+                       mIsValid & CheckedInt_internal::is_sub_valid(T(0), value(), result));
+    }
+
+    /** \returns true if the left and right hand sides are valid and have the same value. */
+    bool operator ==(const CheckedInt& other) const
+    {
+        return bool(mIsValid & other.mIsValid & T(value() == other.value()));
+    }
+
+private:
+    /** operator!= is disabled. Indeed: (a!=b) should be the same as !(a==b) but that
+      * would mean that if a or b is invalid, (a!=b) is always true, which is very tricky.
+      */
+    template<typename U>
+    bool operator !=(const U& other) const { return !(*this == other); }
+};
+
+#define CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP)               \
+template<typename T>                                          \
+inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs) \
+{                                                             \
+    T x = lhs.value();                                        \
+    T y = rhs.value();                                        \
+    T result = x OP y;                                        \
+    T is_op_valid                                             \
+        = CheckedInt_internal::is_##NAME##_valid(x, y, result);  \
+    /* give the compiler a good chance to perform RVO */      \
+    return CheckedInt<T>(result,                                 \
+                      lhs.mIsValid &                          \
+                      rhs.mIsValid &                          \
+                      is_op_valid);                           \
+}
+
+CHECKEDINT_BASIC_BINARY_OPERATOR(add, +)
+CHECKEDINT_BASIC_BINARY_OPERATOR(sub, -)
+CHECKEDINT_BASIC_BINARY_OPERATOR(mul, *)
+
+// division can't be implemented by CHECKEDINT_BASIC_BINARY_OPERATOR
+// because if rhs == 0, we are not allowed to even try to compute the quotient.
+template<typename T>
+inline CheckedInt<T> operator /(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs)
+{
+    T x = lhs.value();
+    T y = rhs.value();
+    T is_op_valid = CheckedInt_internal::is_div_valid(x, y);
+    T result = is_op_valid ? (x / y) : 0;
+    /* give the compiler a good chance to perform RVO */
+    return CheckedInt<T>(result,
+                      lhs.mIsValid &
+                      rhs.mIsValid &
+                      is_op_valid);
+}
+
+// implement cast_to_CheckedInt<T>(x), making sure that
+//  - it allows x to be either a CheckedInt<T> or any integer type that can be casted to T
+//  - if x is already a CheckedInt<T>, we just return a reference to it, instead of copying it (optimization)
+
+template<typename T, typename U>
+struct cast_to_CheckedInt_impl
+{
+    typedef CheckedInt<T> return_type;
+    static CheckedInt<T> run(const U& u) { return u; }
+};
+
+template<typename T>
+struct cast_to_CheckedInt_impl<T, CheckedInt<T> >
+{
+    typedef const CheckedInt<T>& return_type;
+    static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; }
+};
+
+template<typename T, typename U>
+inline typename cast_to_CheckedInt_impl<T, U>::return_type
+cast_to_CheckedInt(const U& u)
+{
+    return cast_to_CheckedInt_impl<T, U>::run(u);
+}
+
+#define CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
+template<typename T>                                          \
+template<typename U>                                          \
+CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(const U &rhs)    \
+{                                                             \
+    *this = *this OP cast_to_CheckedInt<T>(rhs);                 \
+    return *this;                                             \
+}                                                             \
+template<typename T, typename U>                              \
+inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, const U &rhs) \
+{                                                             \
+    return lhs OP cast_to_CheckedInt<T>(rhs);                    \
+}                                                             \
+template<typename T, typename U>                              \
+inline CheckedInt<T> operator OP(const U & lhs, const CheckedInt<T> &rhs) \
+{                                                             \
+    return cast_to_CheckedInt<T>(lhs) OP rhs;                    \
+}
+
+CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
+CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
+CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
+CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
+
+template<typename T, typename U>
+inline bool operator ==(const CheckedInt<T> &lhs, const U &rhs)
+{
+    return lhs == cast_to_CheckedInt<T>(rhs);
+}
+
+template<typename T, typename U>
+inline bool operator ==(const U & lhs, const CheckedInt<T> &rhs)
+{
+    return cast_to_CheckedInt<T>(lhs) == rhs;
+}
+
+} // end namespace WebCore
+
+#endif /* CheckedInt_h */
index 94f6de380077701e8926396f05781de7411309ea..1a750dfeec2fda9386b83c93d46e08b49fbe13a9 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "WebGLRenderingContext.h"
 
+#include "CheckedInt.h"
 #include "CanvasPixelArray.h"
 #include "Console.h"
 #include "DOMWindow.h"
@@ -735,16 +736,24 @@ bool WebGLRenderingContext::validateIndexArrayConservative(unsigned long type, l
         switch (type) {
         case GraphicsContext3D::UNSIGNED_BYTE: {
             unsigned numElements = m_boundElementArrayBuffer->byteLength();
-            const unsigned char* p = static_cast<const unsigned char*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
-            for (unsigned i = 0; i < numElements; i++)
-                maxIndex = max(maxIndex, static_cast<long>(p[i]));
+            if (!numElements)
+                maxIndex = 0;
+            else {
+                const unsigned char* p = static_cast<const unsigned char*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
+                for (unsigned i = 0; i < numElements; i++)
+                    maxIndex = max(maxIndex, static_cast<long>(p[i]));
+            }
             break;
         }
         case GraphicsContext3D::UNSIGNED_SHORT: {
             unsigned numElements = m_boundElementArrayBuffer->byteLength() / sizeof(unsigned short);
-            const unsigned short* p = static_cast<const unsigned short*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
-            for (unsigned i = 0; i < numElements; i++)
-                maxIndex = max(maxIndex, static_cast<long>(p[i]));
+            if (!numElements)
+                maxIndex = 0;
+            else {
+                const unsigned short* p = static_cast<const unsigned short*>(m_boundElementArrayBuffer->elementArrayBuffer()->data());
+                for (unsigned i = 0; i < numElements; i++)
+                    maxIndex = max(maxIndex, static_cast<long>(p[i]));
+            }
             break;
         }
         default:
@@ -862,11 +871,11 @@ void WebGLRenderingContext::drawArrays(unsigned long mode, long first, long coun
         return;
     }
 
-    if (!count)
-        return;
-
     // Ensure we have a valid rendering state
-    if (!validateRenderingState(first + count)) {
+    CheckedInt<int32_t> checkedFirst(first);
+    CheckedInt<int32_t> checkedCount(count);
+    CheckedInt<int32_t> checkedSum = checkedFirst + checkedCount;
+    if (!checkedSum.valid() || !validateRenderingState(checkedSum.value())) {
         m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION);
         return;
     }
@@ -906,9 +915,6 @@ void WebGLRenderingContext::drawElements(unsigned long mode, long count, unsigne
         return;
     }
 
-    if (!count)
-        return;
-
     // Ensure we have a valid rendering state
     long numElements;