Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: mojo/public/js/bindings/validator.js

Issue 468713002: JavaScript bindings for Mojo message validation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Simplified the interface definition template Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 define("mojo/public/js/bindings/validator", [ 5 define("mojo/public/js/bindings/validator", [
6 "mojo/public/js/bindings/codec", 6 "mojo/public/js/bindings/codec",
7 ], function(codec) { 7 ], function(codec) {
8 8
9 var validationError = { 9 var validationError = {
10 NONE: 'VALIDATION_ERROR_NONE', 10 NONE: 'VALIDATION_ERROR_NONE',
11 MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT', 11 MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
12 ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE', 12 ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
13 UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER', 13 UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
14 UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER', 14 UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
15 ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE', 15 ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
16 ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER', 16 ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
17 MESSAGE_HEADER_INVALID_FLAG_COMBINATION: 17 MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
18 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION', 18 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION',
19 MESSAGE_HEADER_MISSING_REQUEST_ID: 19 MESSAGE_HEADER_MISSING_REQUEST_ID:
20 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID' 20 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID'
21 }; 21 };
22 22
23 var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
24
23 function Validator(message) { 25 function Validator(message) {
24 this.message = message; 26 this.message = message;
25 this.offset = 0; 27 this.offset = 0;
28 this.handleIndex = 0;
26 } 29 }
27 30
28 Object.defineProperty(Validator.prototype, "offsetLimit", { 31 Object.defineProperty(Validator.prototype, "offsetLimit", {
29 get: function() { return this.message.buffer.byteLength; } 32 get: function() { return this.message.buffer.byteLength; }
30 }); 33 });
31 34
35 Object.defineProperty(Validator.prototype, "handleIndexLimit", {
36 get: function() { return this.message.handles.length; }
37 });
38
32 // True if we can safely allocate a block of bytes from start to 39 // True if we can safely allocate a block of bytes from start to
33 // to start + numBytes. 40 // to start + numBytes.
34 Validator.prototype.isValidRange = function(start, numBytes) { 41 Validator.prototype.isValidRange = function(start, numBytes) {
35 // Only positive JavaScript integers that are less than 2^53 42 // Only positive JavaScript integers that are less than 2^53
36 // (Number.MAX_SAFE_INTEGER) can be represented exactly. 43 // (Number.MAX_SAFE_INTEGER) can be represented exactly.
37 if (start < this.offset || numBytes <= 0 || 44 if (start < this.offset || numBytes <= 0 ||
38 !Number.isSafeInteger(start) || 45 !Number.isSafeInteger(start) ||
39 !Number.isSafeInteger(numBytes)) 46 !Number.isSafeInteger(numBytes))
40 return false; 47 return false;
41 48
42 var newOffset = start + numBytes; 49 var newOffset = start + numBytes;
43 if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit) 50 if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
44 return false; 51 return false;
45 52
46 return true; 53 return true;
47 } 54 }
48 55
49 Validator.prototype.claimRange = function(start, numBytes) { 56 Validator.prototype.claimRange = function(start, numBytes) {
50 if (this.isValidRange(start, numBytes)) { 57 if (this.isValidRange(start, numBytes)) {
51 this.offset = start + numBytes; 58 this.offset = start + numBytes;
52 return true; 59 return true;
53 } 60 }
54 return false; 61 return false;
55 } 62 }
56 63
64 Validator.prototype.claimHandle = function(index) {
65 if (index === codec.kEncodedInvalidHandleValue)
66 return true;
67
68 if (index < this.handleIndex || index >= this.handleIndexLimit)
69 return false;
70
71 this.handleIndex = index + 1; // safe because handle indices are uint32
yzshen1 2014/08/14 18:02:21 - Two spaces before // - Please use complete sente
hansmuller 2014/08/14 23:51:45 Done.
72 return true;
73 }
74
75 Validator.prototype.validateHandle = function(offset) {
76 var index = this.message.buffer.getUint32(offset);
77 if (!this.claimHandle(index))
78 return validationError.ILLEGAL_HANDLE;
79 return validationError.NONE;
80 }
81
57 Validator.prototype.validateStructHeader = 82 Validator.prototype.validateStructHeader =
58 function(offset, minNumBytes, minNumFields) { 83 function(offset, minNumBytes, minNumFields) {
59 if (!codec.isAligned(offset)) 84 if (!codec.isAligned(offset))
60 return validationError.MISALIGNED_OBJECT; 85 return validationError.MISALIGNED_OBJECT;
61 86
62 if (!this.isValidRange(offset, codec.kStructHeaderSize)) 87 if (!this.isValidRange(offset, codec.kStructHeaderSize))
63 return validationError.ILLEGAL_MEMORY_RANGE; 88 return validationError.ILLEGAL_MEMORY_RANGE;
64 89
65 var numBytes = this.message.buffer.getUint32(offset); 90 var numBytes = this.message.buffer.getUint32(offset);
66 var numFields = this.message.buffer.getUint32(offset + 4); 91 var numFields = this.message.buffer.getUint32(offset + 4);
67 92
68 if (numBytes < minNumBytes || numFields < minNumFields) 93 if (numBytes < minNumBytes || numFields < minNumFields)
69 return validationError.UNEXPECTED_STRUCT_HEADER; 94 return validationError.UNEXPECTED_STRUCT_HEADER;
70 95
71 if (!this.claimRange(offset, numBytes)) 96 if (!this.claimRange(offset, numBytes))
72 return validationError.ILLEGAL_MEMORY_RANGE; 97 return validationError.ILLEGAL_MEMORY_RANGE;
73 98
74 return validationError.NONE; 99 return validationError.NONE;
75 } 100 }
76 101
77 Validator.prototype.validateMessageHeader = function() { 102 Validator.prototype.validateMessageHeader = function() {
103 var err = this.validateStructHeader(0, codec.kStructHeaderSize, 2);
104 if (err != validationError.NONE)
105 return err;
106
78 var numBytes = this.message.getHeaderNumBytes(); 107 var numBytes = this.message.getHeaderNumBytes();
79 var numFields = this.message.getHeaderNumFields(); 108 var numFields = this.message.getHeaderNumFields();
80 109
81 var validNumFieldsAndNumBytes = 110 var validNumFieldsAndNumBytes =
82 (numFields == 2 && numBytes == codec.kMessageHeaderSize) || 111 (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
83 (numFields == 3 && 112 (numFields == 3 &&
84 numBytes == codec.kMessageWithRequestIDHeaderSize) || 113 numBytes == codec.kMessageWithRequestIDHeaderSize) ||
85 (numFields > 3 && 114 (numFields > 3 &&
86 numBytes >= codec.kMessageWithRequestIDHeaderSize); 115 numBytes >= codec.kMessageWithRequestIDHeaderSize);
87 if (!validNumFieldsAndNumBytes) 116 if (!validNumFieldsAndNumBytes)
88 return validationError.UNEXPECTED_STRUCT_HEADER; 117 return validationError.UNEXPECTED_STRUCT_HEADER;
89 118
90 var expectsResponse = this.message.expectsResponse(); 119 var expectsResponse = this.message.expectsResponse();
91 var isResponse = this.message.isResponse(); 120 var isResponse = this.message.isResponse();
92 121
93 if (numFields == 2 && (expectsResponse || isResponse)) 122 if (numFields == 2 && (expectsResponse || isResponse))
94 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID; 123 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
95 124
96 if (isResponse && expectsResponse) 125 if (isResponse && expectsResponse)
97 return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION; 126 return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
98 127
99 return validationError.NONE; 128 return validationError.NONE;
100 } 129 }
101 130
102 Validator.prototype.validateMessage = function() { 131 // Returns the message.buffer relative offset this pointer "points to",
103 var err = this.validateStructHeader(0, codec.kStructHeaderSize, 2); 132 // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
104 if (err != validationError.NONE) 133 // pointer's value is not valid.
105 return err; 134 Validator.prototype.decodePointer = function(offset) {
135 var pointerValue = this.message.buffer.getUint64(offset);
136 if (pointerValue === 0)
137 return NULL_MOJO_POINTER;
138 var bufferOffset = offset + pointerValue;
139 return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
140 }
106 141
107 return this.validateMessageHeader(); 142 Validator.prototype.validateArrayPointer =
143 function(offset, elementSize, elementCount, elementType) {
144 var arrayOffset = this.decodePointer(offset);
145 if (arrayOffset === null)
146 return validationError.ILLEGAL_POINTER;
147 if (arrayOffset === NULL_MOJO_POINTER)
148 return validationError.NONE;
149 return this.validateArray(
150 arrayOffset, elementSize, elementCount, elementType);
151 }
152
153 Validator.prototype.validateStructPointer = function(offset, structClass) {
154 var structOffset = this.decodePointer(offset);
155 if (structOffset === null)
156 return validationError.ILLEGAL_POINTER;
157 if (structOffset === NULL_MOJO_POINTER)
158 return validationError.NONE;
159 return structClass.validate(this, structOffset);
160 }
161
162 Validator.prototype.validateStringPointer = function(offset) {
163 return this.validateArrayPointer(offset, codec.Uint8.encodedSize, 0);
164 }
165
166 // Similar to Array_Data<T>::Validate()
167 // mojo/public/cpp/bindings/lib/array_internal.h
168
169 Validator.prototype.validateArray =
170 function (offset, elementSize, elementCount, elementType) {
171 if (!codec.isAligned(offset))
172 return validationError.MISALIGNED_OBJECT;
173
174 if (!this.isValidRange(offset, codec.kArrayHeaderSize))
175 return validationError.ILLEGAL_MEMORY_RANGE;
176
177 var numBytes = this.message.buffer.getUint32(offset);
178 var numElements = this.message.buffer.getUint32(offset + 4);
179
180 // Note: this computation is "safe" because elementSize <= 8 and
181 // numElements is a uint32.
182 var elementsTotalSize = (elementType === codec.PackedBool) ?
183 Math.ceil(numElements / 8) : (elementSize * numElements);
184
185 if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
186 return validationError.UNEXPECTED_ARRAY_HEADER;
187
188 if (elementCount != 0 && numElements != elementCount)
189 return validationError.UNEXPECTED_ARRAY_HEADER;
190
191 if (!this.claimRange(offset, numBytes))
192 return validationError.ILLEGAL_MEMORY_RANGE;
193
194 var elementsOffset = offset + codec.kArrayHeaderSize;
195 if (elementType === codec.Handle)
196 return this.validateHandleElements(elementsOffset, numElements);
197 if (elementType instanceof codec.PointerTo)
198 return this.validateStructElements(
199 elementsOffset, numElements, elementType.cls);
200 if (elementType instanceof codec.ArrayOf)
201 return this.validateArrayElements(
202 elementsOffset, numElements, elementType.cls);
203
204 return validationError.NONE;
205 }
206
207 // Note: the |offset + i * elementSize| computation in the validateFooElements
208 // methods below is "safe" because elementSize <= 8, offset and
209 // numElements are uint32, and 0 <= i < numElements.
210
211 Validator.prototype.validateHandleElements = function(offset, numElements) {
212 var elementSize = codec.Handle.encodedSize;
213 for (var i = 0; i < numElements; i++) {
214 var index = this.message.buffer.getUint32(offset + i * elementSize);
215 if (!this.claimHandle(index))
216 return validationError.ILLEGAL_HANDLE;
217 }
218 return validationError.NONE;
219 }
220
221 Validator.prototype.validateArrayElements =
222 function(offset, numElements, elementClass) {
223 var elementSize = codec.PointerTo.prototype.encodedSize;
224 for (var i = 0; i < numElements; i++) {
225 var elementOffset = offset + i * elementSize;
226 var err = this.validateArrayPointer(
227 elementOffset, elementClass.encodedSize, 0, elementClass);
228 if (err != validationError.NONE)
229 return err;
230 }
231 return validationError.NONE;
232 }
233
234 Validator.prototype.validateStructElements =
235 function(offset, numElements, structClass) {
236 var elementSize = codec.PointerTo.prototype.encodedSize;
237 for (var i = 0; i < numElements; i++) {
238 var elementOffset = offset + i * elementSize;
239 var err = this.validateStructPointer(elementOffset, structClass);
240 if (err != validationError.NONE)
241 return err;
242 }
243 return validationError.NONE;
108 } 244 }
109 245
110 var exports = {}; 246 var exports = {};
111 exports.validationError = validationError; 247 exports.validationError = validationError;
112 exports.Validator = Validator; 248 exports.Validator = Validator;
113 return exports; 249 return exports;
114 }); 250 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698