Bug 1080262 - Disallow ctypes from converting array buffers and typed arrays to pointers, except when passed as arguments r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 03 Nov 2014 11:38:12 -0800
changeset 248843 266d204f97bf8096d39c03fe59d2b6ae2ccc647d
parent 248842 d2796361d388eeec5b24498edfa483cfaa8fe880
child 248844 11a02a002dfa28706e4db87af8d3bccf5fbb9e0e
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1080262
milestone37.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 1080262 - Disallow ctypes from converting array buffers and typed arrays to pointers, except when passed as arguments r=sfink
js/src/ctypes/CTypes.cpp
toolkit/components/ctypes/tests/unit/test_jsctypes.js
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2211,17 +2211,17 @@ ConvertToJS(JSContext* cx,
   return true;
 }
 
 // Determine if the contents of a typed array can be converted without
 // ambiguity to a C type. Elements of a Int8Array are converted to
 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
 bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) {
   TypeCode baseTypeCode = CType::GetTypeCode(baseType);
-  if (baseTypeCode == TYPE_void_t) {
+  if (baseTypeCode == TYPE_void_t || baseTypeCode == TYPE_char) {
     return true;
   }
   TypeCode elementTypeCode;
   switch (JS_GetArrayBufferViewType(valObj)) {
   case Scalar::Int8:
     elementTypeCode = TYPE_int8_t;
     break;
   case Scalar::Uint8:
@@ -2244,16 +2244,17 @@ bool CanConvertTypedArrayItemTo(JSObject
     elementTypeCode = TYPE_float32_t;
     break;
   case Scalar::Float64:
     elementTypeCode = TYPE_float64_t;
     break;
   default:
     return false;
   }
+
   return elementTypeCode == baseTypeCode;
 }
 
 // Implicitly convert jsval 'val' to a C binary representation of CType
 // 'targetType', storing the result in 'buffer'. Adequate space must be
 // provided in 'buffer' by the caller. This function generally does minimal
 // coercion between types. There are two cases in which this function is used:
 // 1) The target buffer is internal to a CData object; we simply write data
@@ -2446,35 +2447,42 @@ ImplicitConvert(JSContext* cx,
         (*char16Buffer)[sourceLength] = 0;
         break;
       }
       default:
         return TypeError(cx, "string pointer", val);
       }
       break;
     } else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
-      // Convert ArrayBuffer to pointer without any copy. Just as with C
-      // arrays, we make no effort to keep the ArrayBuffer alive. This
-      // functionality will be removed for all but arguments in bug 1080262.
+      // Convert ArrayBuffer to pointer without any copy. This is only valid
+      // when converting an argument to a function call, as it is possible for
+      // the pointer to be invalidated by anything that runs JS code. (It is
+      // invalid to invoke JS code from a ctypes function call.)
+      if (!isArgument) {
+        return TypeError(cx, "arraybuffer pointer", val);
+      }
       void* ptr;
       {
           JS::AutoCheckCannotGC nogc;
           ptr = JS_GetArrayBufferData(valObj, nogc);
       }
       if (!ptr) {
         return TypeError(cx, "arraybuffer pointer", val);
       }
       *static_cast<void**>(buffer) = ptr;
       break;
-    } if (val.isObject() && JS_IsArrayBufferViewObject(valObj)) {
-      // Same as ArrayBuffer, above, though note that this will take the offset
-      // of the view into account.
+    } else if (val.isObject() && JS_IsArrayBufferViewObject(valObj)) {
+      // Same as ArrayBuffer, above, though note that this will take the
+      // offset of the view into account.
       if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
         return TypeError(cx, "typed array with the appropriate type", val);
       }
+      if (!isArgument) {
+        return TypeError(cx, "typed array pointer", val);
+      }
       void* ptr;
       {
           JS::AutoCheckCannotGC nogc;
           ptr = JS_GetArrayBufferViewData(valObj, nogc);
       }
       if (!ptr) {
         return TypeError(cx, "typed array pointer", val);
       }
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -1748,63 +1748,44 @@ function run_PointerType_tests() {
   let z_t = ctypes.int32_t.array().ptr;
   do_check_eq(ptrValue(z_t()), 0);
   do_check_throws(function() { z_t().contents }, Error);
   z_t = ctypes.int32_t.array(0).ptr;
   do_check_eq(ptrValue(z_t()), 0);
   let z = ctypes.int32_t.array(0)().address();
   do_check_eq(z.contents.length, 0);
 
-  // Check that you can use an ArrayBuffer or a typed array as a pointer
   let c_arraybuffer = new ArrayBuffer(256);
   let typed_array_samples =
        [
          [new Int8Array(c_arraybuffer), ctypes.int8_t],
          [new Uint8Array(c_arraybuffer), ctypes.uint8_t],
          [new Int16Array(c_arraybuffer), ctypes.int16_t],
          [new Uint16Array(c_arraybuffer), ctypes.uint16_t],
          [new Int32Array(c_arraybuffer), ctypes.int32_t],
          [new Uint32Array(c_arraybuffer), ctypes.uint32_t],
          [new Float32Array(c_arraybuffer), ctypes.float32_t],
          [new Float64Array(c_arraybuffer), ctypes.float64_t]
         ];
+
+  // Check that you can convert ArrayBuffer or typed array to a C array
   for (let i = 0; i < typed_array_samples.length; ++i) {
     for (let j = 0; j < typed_array_samples.length; ++j) {
       let view = typed_array_samples[i][0];
       let item_type = typed_array_samples[j][1];
       let number_of_items = c_arraybuffer.byteLength / item_type.size;
       let array_type = item_type.array(number_of_items);
 
       if (i != j) {
         do_print("Checking that typed array " + (view.constructor.name) +
-                 " canNOT be converted to " + item_type + " pointer/array");
-        do_check_throws(function() { item_type.ptr(view); }, TypeError);
+                 " can NOT be converted to " + item_type + " array");
         do_check_throws(function() { array_type(view); }, TypeError);
-
       } else {
         do_print("Checking that typed array " + (view.constructor.name) +
-                 " can be converted to " + item_type + " pointer/array");
-        // Fill buffer using view
-        for (let k = 0; k < number_of_items; ++k) {
-          view[k] = k;
-        }
-
-        // Convert ArrayBuffer to pointer then array and check contents
-        let c_ptr = item_type.ptr(c_arraybuffer);
-        let c_array = ctypes.cast(c_ptr, array_type.ptr).contents;
-        for (let k = 0; k < number_of_items; ++k) {
-          do_check_eq(c_array[k], view[k]);
-        }
-
-        // Convert view to pointer then array and check contents
-        c_ptr = item_type.ptr(view);
-        c_array = ctypes.cast(c_ptr, array_type.ptr).contents;
-        for (let k = 0; k < number_of_items; ++k) {
-          do_check_eq(c_array[k], view[k]);
-        }
+                 " can be converted to " + item_type + " array");
 
         // Convert ArrayBuffer to array of the right size and check contents
         c_array = array_type(c_arraybuffer);
         for (let k = 0; k < number_of_items; ++k) {
           do_check_eq(c_array[k], view[k]);
         }
 
         // Convert typed array to array of the right size and check contents
@@ -1822,25 +1803,34 @@ function run_PointerType_tests() {
         do_check_throws(function() { array_type_too_large(view); }, Error);
         do_check_throws(function() { array_type_too_small(view); }, Error);
 
         // Convert subarray of typed array to array of right size and check contents
         c_array = array_type_too_small(view.subarray(1));
         for (let k = 1; k < number_of_items; ++k) {
           do_check_eq(c_array[k - 1], view[k]);
         }
-
-        // Convert array to void*
-        ctypes.voidptr_t(c_arraybuffer);
-
-        // Convert view to void*
-        ctypes.voidptr_t(view);
       }
     }
   }
+
+  // Check that you can't use an ArrayBuffer or a typed array as a pointer
+  for (let i = 0; i < typed_array_samples.length; ++i) {
+    for (let j = 0; j < typed_array_samples.length; ++j) {
+      let view = typed_array_samples[i][0];
+      let item_type = typed_array_samples[j][1];
+
+      do_print("Checking that typed array " + (view.constructor.name) +
+               " can NOT be converted to " + item_type + " pointer/array");
+      do_check_throws(function() { item_type.ptr(c_arraybuffer); }, TypeError);
+      do_check_throws(function() { item_type.ptr(view); }, TypeError);
+      do_check_throws(function() { ctypes.voidptr_t(c_arraybuffer); }, TypeError);
+      do_check_throws(function() { ctypes.voidptr_t(view); }, TypeError);
+    }
+  }
 }
 
 function run_FunctionType_tests() {
   run_type_ctor_class_tests(ctypes.FunctionType,
     ctypes.FunctionType(ctypes.default_abi, ctypes.void_t),
     ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.int32_t ]),
     [ "abi", "returnType", "argTypes", "isVariadic" ],
     undefined, undefined, undefined, undefined);