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

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

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 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
« no previous file with comments | « mojo/public/js/validation_unittests.js ('k') | mojo/public/mojo.gni » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 define("mojo/public/js/validator", [
6 "mojo/public/js/codec",
7 ], function(codec) {
8
9 var validationError = {
10 NONE: 'VALIDATION_ERROR_NONE',
11 MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
12 ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
13 UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
14 UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
15 ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
16 UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
17 ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
18 UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
19 MESSAGE_HEADER_INVALID_FLAGS:
20 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS',
21 MESSAGE_HEADER_MISSING_REQUEST_ID:
22 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID',
23 DIFFERENT_SIZED_ARRAYS_IN_MAP:
24 'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
25 INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
26 UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
27 };
28
29 var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
30
31 function isStringClass(cls) {
32 return cls === codec.String || cls === codec.NullableString;
33 }
34
35 function isHandleClass(cls) {
36 return cls === codec.Handle || cls === codec.NullableHandle;
37 }
38
39 function isInterfaceClass(cls) {
40 return cls === codec.Interface || cls === codec.NullableInterface;
41 }
42
43 function isNullable(type) {
44 return type === codec.NullableString || type === codec.NullableHandle ||
45 type === codec.NullableInterface ||
46 type instanceof codec.NullableArrayOf ||
47 type instanceof codec.NullablePointerTo ||
48 type instanceof codec.NullableUnionWrapper;
49 }
50
51 function Validator(message) {
52 this.message = message;
53 this.offset = 0;
54 this.handleIndex = 0;
55 }
56
57 Object.defineProperty(Validator.prototype, "offsetLimit", {
58 get: function() { return this.message.buffer.byteLength; }
59 });
60
61 Object.defineProperty(Validator.prototype, "handleIndexLimit", {
62 get: function() { return this.message.handles.length; }
63 });
64
65 // True if we can safely allocate a block of bytes from start to
66 // to start + numBytes.
67 Validator.prototype.isValidRange = function(start, numBytes) {
68 // Only positive JavaScript integers that are less than 2^53
69 // (Number.MAX_SAFE_INTEGER) can be represented exactly.
70 if (start < this.offset || numBytes <= 0 ||
71 !Number.isSafeInteger(start) ||
72 !Number.isSafeInteger(numBytes))
73 return false;
74
75 var newOffset = start + numBytes;
76 if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
77 return false;
78
79 return true;
80 }
81
82 Validator.prototype.claimRange = function(start, numBytes) {
83 if (this.isValidRange(start, numBytes)) {
84 this.offset = start + numBytes;
85 return true;
86 }
87 return false;
88 }
89
90 Validator.prototype.claimHandle = function(index) {
91 if (index === codec.kEncodedInvalidHandleValue)
92 return true;
93
94 if (index < this.handleIndex || index >= this.handleIndexLimit)
95 return false;
96
97 // This is safe because handle indices are uint32.
98 this.handleIndex = index + 1;
99 return true;
100 }
101
102 Validator.prototype.validateHandle = function(offset, nullable) {
103 var index = this.message.buffer.getUint32(offset);
104
105 if (index === codec.kEncodedInvalidHandleValue)
106 return nullable ?
107 validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
108
109 if (!this.claimHandle(index))
110 return validationError.ILLEGAL_HANDLE;
111 return validationError.NONE;
112 }
113
114 Validator.prototype.validateInterface = function(offset, nullable) {
115 return this.validateHandle(offset, nullable);
116 }
117
118 Validator.prototype.validateStructHeader =
119 function(offset, minNumBytes, minVersion) {
120 if (!codec.isAligned(offset))
121 return validationError.MISALIGNED_OBJECT;
122
123 if (!this.isValidRange(offset, codec.kStructHeaderSize))
124 return validationError.ILLEGAL_MEMORY_RANGE;
125
126 var numBytes = this.message.buffer.getUint32(offset);
127 var version = this.message.buffer.getUint32(offset + 4);
128
129 // Backward compatibility is not yet supported.
130 if (numBytes < minNumBytes || version < minVersion)
131 return validationError.UNEXPECTED_STRUCT_HEADER;
132
133 if (!this.claimRange(offset, numBytes))
134 return validationError.ILLEGAL_MEMORY_RANGE;
135
136 return validationError.NONE;
137 }
138
139 Validator.prototype.validateMessageHeader = function() {
140 var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0);
141 if (err != validationError.NONE)
142 return err;
143
144 var numBytes = this.message.getHeaderNumBytes();
145 var version = this.message.getHeaderVersion();
146
147 var validVersionAndNumBytes =
148 (version == 0 && numBytes == codec.kMessageHeaderSize) ||
149 (version == 1 &&
150 numBytes == codec.kMessageWithRequestIDHeaderSize) ||
151 (version > 1 &&
152 numBytes >= codec.kMessageWithRequestIDHeaderSize);
153 if (!validVersionAndNumBytes)
154 return validationError.UNEXPECTED_STRUCT_HEADER;
155
156 var expectsResponse = this.message.expectsResponse();
157 var isResponse = this.message.isResponse();
158
159 if (version == 0 && (expectsResponse || isResponse))
160 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
161
162 if (isResponse && expectsResponse)
163 return validationError.MESSAGE_HEADER_INVALID_FLAGS;
164
165 return validationError.NONE;
166 }
167
168 // Returns the message.buffer relative offset this pointer "points to",
169 // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
170 // pointer's value is not valid.
171 Validator.prototype.decodePointer = function(offset) {
172 var pointerValue = this.message.buffer.getUint64(offset);
173 if (pointerValue === 0)
174 return NULL_MOJO_POINTER;
175 var bufferOffset = offset + pointerValue;
176 return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
177 }
178
179 Validator.prototype.decodeUnionSize = function(offset) {
180 return this.message.buffer.getUint32(offset);
181 };
182
183 Validator.prototype.decodeUnionTag = function(offset) {
184 return this.message.buffer.getUint32(offset + 4);
185 };
186
187 Validator.prototype.validateArrayPointer = function(
188 offset, elementSize, elementType, nullable, expectedDimensionSizes,
189 currentDimension) {
190 var arrayOffset = this.decodePointer(offset);
191 if (arrayOffset === null)
192 return validationError.ILLEGAL_POINTER;
193
194 if (arrayOffset === NULL_MOJO_POINTER)
195 return nullable ?
196 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
197
198 return this.validateArray(arrayOffset, elementSize, elementType,
199 expectedDimensionSizes, currentDimension);
200 }
201
202 Validator.prototype.validateStructPointer = function(
203 offset, structClass, nullable) {
204 var structOffset = this.decodePointer(offset);
205 if (structOffset === null)
206 return validationError.ILLEGAL_POINTER;
207
208 if (structOffset === NULL_MOJO_POINTER)
209 return nullable ?
210 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
211
212 return structClass.validate(this, structOffset);
213 }
214
215 Validator.prototype.validateUnion = function(
216 offset, unionClass, nullable) {
217 var size = this.message.buffer.getUint32(offset);
218 if (size == 0) {
219 return nullable ?
220 validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
221 }
222
223 return unionClass.validate(this, offset);
224 }
225
226 Validator.prototype.validateNestedUnion = function(
227 offset, unionClass, nullable) {
228 var unionOffset = this.decodePointer(offset);
229 if (unionOffset === null)
230 return validationError.ILLEGAL_POINTER;
231
232 if (unionOffset === NULL_MOJO_POINTER)
233 return nullable ?
234 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
235
236 return this.validateUnion(unionOffset, unionClass, nullable);
237 }
238
239 // This method assumes that the array at arrayPointerOffset has
240 // been validated.
241
242 Validator.prototype.arrayLength = function(arrayPointerOffset) {
243 var arrayOffset = this.decodePointer(arrayPointerOffset);
244 return this.message.buffer.getUint32(arrayOffset + 4);
245 }
246
247 Validator.prototype.validateMapPointer = function(
248 offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
249 // Validate the implicit map struct:
250 // struct {array<keyClass> keys; array<valueClass> values};
251 var structOffset = this.decodePointer(offset);
252 if (structOffset === null)
253 return validationError.ILLEGAL_POINTER;
254
255 if (structOffset === NULL_MOJO_POINTER)
256 return mapIsNullable ?
257 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
258
259 var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
260 var err = this.validateStructHeader(structOffset, mapEncodedSize, 0);
261 if (err !== validationError.NONE)
262 return err;
263
264 // Validate the keys array.
265 var keysArrayPointerOffset = structOffset + codec.kStructHeaderSize;
266 err = this.validateArrayPointer(
267 keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
268 if (err !== validationError.NONE)
269 return err;
270
271 // Validate the values array.
272 var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
273 var valuesArrayDimensions = [0]; // Validate the actual length below.
274 if (valueClass instanceof codec.ArrayOf)
275 valuesArrayDimensions =
276 valuesArrayDimensions.concat(valueClass.dimensions());
277 var err = this.validateArrayPointer(valuesArrayPointerOffset,
278 valueClass.encodedSize,
279 valueClass,
280 valueIsNullable,
281 valuesArrayDimensions,
282 0);
283 if (err !== validationError.NONE)
284 return err;
285
286 // Validate the lengths of the keys and values arrays.
287 var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
288 var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
289 if (keysArrayLength != valuesArrayLength)
290 return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
291
292 return validationError.NONE;
293 }
294
295 Validator.prototype.validateStringPointer = function(offset, nullable) {
296 return this.validateArrayPointer(
297 offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0);
298 }
299
300 // Similar to Array_Data<T>::Validate()
301 // mojo/public/cpp/bindings/lib/array_internal.h
302
303 Validator.prototype.validateArray =
304 function (offset, elementSize, elementType, expectedDimensionSizes,
305 currentDimension) {
306 if (!codec.isAligned(offset))
307 return validationError.MISALIGNED_OBJECT;
308
309 if (!this.isValidRange(offset, codec.kArrayHeaderSize))
310 return validationError.ILLEGAL_MEMORY_RANGE;
311
312 var numBytes = this.message.buffer.getUint32(offset);
313 var numElements = this.message.buffer.getUint32(offset + 4);
314
315 // Note: this computation is "safe" because elementSize <= 8 and
316 // numElements is a uint32.
317 var elementsTotalSize = (elementType === codec.PackedBool) ?
318 Math.ceil(numElements / 8) : (elementSize * numElements);
319
320 if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
321 return validationError.UNEXPECTED_ARRAY_HEADER;
322
323 if (expectedDimensionSizes[currentDimension] != 0 &&
324 numElements != expectedDimensionSizes[currentDimension]) {
325 return validationError.UNEXPECTED_ARRAY_HEADER;
326 }
327
328 if (!this.claimRange(offset, numBytes))
329 return validationError.ILLEGAL_MEMORY_RANGE;
330
331 // Validate the array's elements if they are pointers or handles.
332
333 var elementsOffset = offset + codec.kArrayHeaderSize;
334 var nullable = isNullable(elementType);
335
336 if (isHandleClass(elementType))
337 return this.validateHandleElements(elementsOffset, numElements, nullable);
338 if (isInterfaceClass(elementType))
339 return this.validateInterfaceElements(
340 elementsOffset, numElements, nullable);
341 if (isStringClass(elementType))
342 return this.validateArrayElements(
343 elementsOffset, numElements, codec.Uint8, nullable, [0], 0);
344 if (elementType instanceof codec.PointerTo)
345 return this.validateStructElements(
346 elementsOffset, numElements, elementType.cls, nullable);
347 if (elementType instanceof codec.ArrayOf)
348 return this.validateArrayElements(
349 elementsOffset, numElements, elementType.cls, nullable,
350 expectedDimensionSizes, currentDimension + 1);
351 if (elementType instanceof codec.UnionWrapper) {
352 return this.validateUnionElements(
353 elementsOffset, numElements, elementType.cls, nullable);
354 }
355
356 return validationError.NONE;
357 }
358
359 // Note: the |offset + i * elementSize| computation in the validateFooElements
360 // methods below is "safe" because elementSize <= 8, offset and
361 // numElements are uint32, and 0 <= i < numElements.
362
363 Validator.prototype.validateHandleElements =
364 function(offset, numElements, nullable) {
365 var elementSize = codec.Handle.encodedSize;
366 for (var i = 0; i < numElements; i++) {
367 var elementOffset = offset + i * elementSize;
368 var err = this.validateHandle(elementOffset, nullable);
369 if (err != validationError.NONE)
370 return err;
371 }
372 return validationError.NONE;
373 }
374
375 Validator.prototype.validateInterfaceElements =
376 function(offset, numElements, nullable) {
377 var elementSize = codec.Interface.encodedSize;
378 for (var i = 0; i < numElements; i++) {
379 var elementOffset = offset + i * elementSize;
380 var err = this.validateInterface(elementOffset, nullable);
381 if (err != validationError.NONE)
382 return err;
383 }
384 return validationError.NONE;
385 }
386
387 // The elementClass parameter is the element type of the element arrays.
388 Validator.prototype.validateArrayElements =
389 function(offset, numElements, elementClass, nullable,
390 expectedDimensionSizes, currentDimension) {
391 var elementSize = codec.PointerTo.prototype.encodedSize;
392 for (var i = 0; i < numElements; i++) {
393 var elementOffset = offset + i * elementSize;
394 var err = this.validateArrayPointer(
395 elementOffset, elementClass.encodedSize, elementClass, nullable,
396 expectedDimensionSizes, currentDimension);
397 if (err != validationError.NONE)
398 return err;
399 }
400 return validationError.NONE;
401 }
402
403 Validator.prototype.validateStructElements =
404 function(offset, numElements, structClass, nullable) {
405 var elementSize = codec.PointerTo.prototype.encodedSize;
406 for (var i = 0; i < numElements; i++) {
407 var elementOffset = offset + i * elementSize;
408 var err =
409 this.validateStructPointer(elementOffset, structClass, nullable);
410 if (err != validationError.NONE)
411 return err;
412 }
413 return validationError.NONE;
414 }
415
416 Validator.prototype.validateUnionElements =
417 function(offset, numElements, unionClass, nullable) {
418 var elementSize = codec.UnionWrapper.prototype.encodedSize;
419 for (var i = 0; i < numElements; i++) {
420 var elementOffset = offset + i * elementSize;
421 var err =
422 this.validateUnion(elementOffset, unionClass, nullable);
423 if (err != validationError.NONE)
424 return err;
425 }
426 return validationError.NONE;
427 }
428
429 var exports = {};
430 exports.validationError = validationError;
431 exports.Validator = Validator;
432 return exports;
433 });
OLDNEW
« no previous file with comments | « mojo/public/js/validation_unittests.js ('k') | mojo/public/mojo.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698