Chromium Code Reviews| Index: mojo/public/js/bindings/validator.js |
| diff --git a/mojo/public/js/bindings/validator.js b/mojo/public/js/bindings/validator.js |
| index eac95d93d41c85fec85a2e8cd599c76123a2c0a7..841d9f982c2a9e5cf5070ef1df672ef7896f8df4 100644 |
| --- a/mojo/public/js/bindings/validator.js |
| +++ b/mojo/public/js/bindings/validator.js |
| @@ -4,7 +4,7 @@ |
| define("mojo/public/js/bindings/validator", [ |
| "mojo/public/js/bindings/codec", |
| - ], function(codec) { |
| +], function(codec) { |
| var validationError = { |
| NONE: 'VALIDATION_ERROR_NONE', |
| @@ -20,15 +20,22 @@ define("mojo/public/js/bindings/validator", [ |
| 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID' |
| }; |
| + var NULL_MOJO_POINTER = "NULL_MOJO_POINTER"; |
| + |
| function Validator(message) { |
| this.message = message; |
| this.offset = 0; |
| + this.handleIndex = 0; |
| } |
| Object.defineProperty(Validator.prototype, "offsetLimit", { |
| get: function() { return this.message.buffer.byteLength; } |
| }); |
| + Object.defineProperty(Validator.prototype, "handleIndexLimit", { |
| + get: function() { return this.message.handles.length; } |
| + }); |
| + |
| // True if we can safely allocate a block of bytes from start to |
| // to start + numBytes. |
| Validator.prototype.isValidRange = function(start, numBytes) { |
| @@ -54,6 +61,24 @@ define("mojo/public/js/bindings/validator", [ |
| return false; |
| } |
| + Validator.prototype.claimHandle = function(index) { |
| + if (index === codec.kEncodedInvalidHandleValue) |
| + return true; |
| + |
| + if (index < this.handleIndex || index >= this.handleIndexLimit) |
| + return false; |
| + |
| + this.handleIndex = index + 1; // safe because handle indices are uint32 |
| + return true; |
| + } |
| + |
| + Validator.prototype.validateHandle = function(offset) { |
| + var index = this.message.buffer.getUint32(offset); |
| + if (!this.claimHandle(index)) |
| + return validationError.ILLEGAL_HANDLE; |
| + return validationError.NONE; |
| + } |
| + |
| Validator.prototype.validateStructHeader = |
| function(offset, minNumBytes, minNumFields) { |
| if (!codec.isAligned(offset)) |
| @@ -75,6 +100,10 @@ define("mojo/public/js/bindings/validator", [ |
| } |
| Validator.prototype.validateMessageHeader = function() { |
| + var err = this.validateStructHeader(0, codec.kStructHeaderSize, 2); |
| + if (err != validationError.NONE) |
| + return err; |
| + |
| var numBytes = this.message.getHeaderNumBytes(); |
| var numFields = this.message.getHeaderNumFields(); |
| @@ -99,12 +128,119 @@ define("mojo/public/js/bindings/validator", [ |
| return validationError.NONE; |
| } |
| - Validator.prototype.validateMessage = function() { |
| - var err = this.validateStructHeader(0, codec.kStructHeaderSize, 2); |
| - if (err != validationError.NONE) |
| - return err; |
| + // Returns the message.buffer relative offset this pointer "points to", |
| + // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the |
| + // pointer's value is not valid. |
| + Validator.prototype.decodePointer = function(offset) { |
| + var pointerValue = this.message.buffer.getUint64(offset); |
| + if (pointerValue === 0) |
| + return NULL_MOJO_POINTER; |
| + var bufferOffset = offset + pointerValue; |
| + return Number.isSafeInteger(bufferOffset) ? bufferOffset : null; |
| + } |
| + |
| + Validator.prototype.validateArrayPointer = |
| + function(offset, elementSize, elementCount, elementType) { |
| + var arrayOffset = this.decodePointer(offset); |
| + if (arrayOffset === null) |
| + return validationError.ILLEGAL_POINTER; |
| + if (arrayOffset === NULL_MOJO_POINTER) |
| + return validationError.NONE; |
| + return this.validateArray( |
| + arrayOffset, elementSize, elementCount, elementType); |
| + } |
| + |
| + Validator.prototype.validateStructPointer = function(offset, structClass) { |
| + var structOffset = this.decodePointer(offset); |
| + if (structOffset === null) |
| + return validationError.ILLEGAL_POINTER; |
| + if (structOffset === NULL_MOJO_POINTER) |
| + return validationError.NONE; |
| + return structClass.validate(this, structOffset); |
| + } |
| + |
| + Validator.prototype.validateStringPointer = function(offset) { |
| + return this.validateArrayPointer(offset, codec.Uint8.encodedSize, 0); |
| + } |
| + |
| + // Similar to Array_Data<T>::Validate() |
| + // mojo/public/cpp/bindings/lib/array_internal.h |
| - return this.validateMessageHeader(); |
| + Validator.prototype.validateArray = |
| + function (offset, elementSize, elementCount, elementType) { |
| + if (!codec.isAligned(offset)) |
| + return validationError.MISALIGNED_OBJECT; |
| + |
| + if (!this.isValidRange(offset, codec.kArrayHeaderSize)) |
| + return validationError.ILLEGAL_MEMORY_RANGE; |
| + |
| + var numBytes = this.message.buffer.getUint32(offset); |
| + var numElements = this.message.buffer.getUint32(offset + 4); |
| + |
| + // Note: this computation is "safe" because elementSize <= 8 and |
| + // numElements is a uint32. |
| + var elementsTotalSize = (elementType === codec.PackedBool) ? |
| + Math.ceil(numElements / 8) : (elementSize * numElements); |
| + |
| + if (numBytes < codec.kArrayHeaderSize + elementsTotalSize) |
| + return validationError.UNEXPECTED_ARRAY_HEADER; |
| + |
| + if (elementCount != 0 && numElements != elementCount) |
| + return validationError.UNEXPECTED_ARRAY_HEADER; |
| + |
| + if (!this.claimRange(offset, numBytes)) |
| + return validationError.ILLEGAL_MEMORY_RANGE; |
| + |
| + var elementsOffset = offset + codec.kArrayHeaderSize; |
| + if (elementType === codec.Handle) |
| + return this.validateHandleElements(elementsOffset, numElements); |
| + if (elementType instanceof codec.PointerTo) |
| + return this.validateStructElements( |
| + elementsOffset, numElements, elementType.cls); |
| + if (elementType instanceof codec.ArrayOf) |
| + return this.validateArrayElements( |
| + elementsOffset, numElements, elementType.cls); |
| + |
| + return validationError.NONE; |
| + } |
| + |
| + // Note: the |offset + i * elementSize| computation in the validateFooElements |
| + // methods below is "safe" because elementSize <= 8, offset and |
| + // numElements are uint32, and 0 <= i < numElements. |
| + |
| + Validator.prototype.validateHandleElements = function(offset, numElements) { |
| + var elementSize = codec.Handle.encodedSize; |
| + for (var i = 0; i < numElements; i++) { |
| + var index = this.message.buffer.getUint32(offset + i * elementSize); |
| + if (!this.claimHandle(index)) |
| + return validationError.ILLEGAL_HANDLE; |
| + } |
| + return validationError.NONE; |
| + } |
| + |
| + Validator.prototype.validateArrayElements = |
| + function(offset, numElements, elementClass) { |
| + var elementSize = codec.PointerTo.prototype.encodedSize; |
|
Matt Perry
2014/08/13 19:31:01
I'm confused about this one. How do you know the a
hansmuller
2014/08/13 21:23:40
I apologize, this is a little confusing. We're val
Matt Perry
2014/08/13 21:30:44
Just to make sure I understand you correctly, you'
hansmuller
2014/08/13 21:53:21
Yes. We only validate array elements if they're ha
|
| + for (var i = 0; i < numElements; i++) { |
| + var elementOffset = offset + i * elementSize; |
| + var err = this.validateArrayPointer( |
| + elementOffset, elementClass.encodedSize, 0, elementClass); |
| + if (err != validationError.NONE) |
| + return err; |
| + } |
| + return validationError.NONE; |
| + } |
| + |
| + Validator.prototype.validateStructElements = |
| + function(offset, numElements, structClass) { |
| + var elementSize = codec.PointerTo.prototype.encodedSize; |
| + for (var i = 0; i < numElements; i++) { |
| + var elementOffset = offset + i * elementSize; |
| + var err = this.validateStructPointer(elementOffset, structClass); |
| + if (err != validationError.NONE) |
| + return err; |
| + } |
| + return validationError.NONE; |
| } |
| var exports = {}; |