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

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

Issue 814543006: Move //mojo/{public, edk} underneath //third_party (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 11 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_FLAG_COMBINATION:
20 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION',
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 };
26
27 var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
28
29 function isStringClass(cls) {
30 return cls === codec.String || cls === codec.NullableString;
31 }
32
33 function isHandleClass(cls) {
34 return cls === codec.Handle || cls === codec.NullableHandle;
35 }
36
37 function isNullable(type) {
38 return type === codec.NullableString || type === codec.NullableHandle ||
39 type instanceof codec.NullableArrayOf ||
40 type instanceof codec.NullablePointerTo;
41 }
42
43 function Validator(message) {
44 this.message = message;
45 this.offset = 0;
46 this.handleIndex = 0;
47 }
48
49 Object.defineProperty(Validator.prototype, "offsetLimit", {
50 get: function() { return this.message.buffer.byteLength; }
51 });
52
53 Object.defineProperty(Validator.prototype, "handleIndexLimit", {
54 get: function() { return this.message.handles.length; }
55 });
56
57 // True if we can safely allocate a block of bytes from start to
58 // to start + numBytes.
59 Validator.prototype.isValidRange = function(start, numBytes) {
60 // Only positive JavaScript integers that are less than 2^53
61 // (Number.MAX_SAFE_INTEGER) can be represented exactly.
62 if (start < this.offset || numBytes <= 0 ||
63 !Number.isSafeInteger(start) ||
64 !Number.isSafeInteger(numBytes))
65 return false;
66
67 var newOffset = start + numBytes;
68 if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
69 return false;
70
71 return true;
72 }
73
74 Validator.prototype.claimRange = function(start, numBytes) {
75 if (this.isValidRange(start, numBytes)) {
76 this.offset = start + numBytes;
77 return true;
78 }
79 return false;
80 }
81
82 Validator.prototype.claimHandle = function(index) {
83 if (index === codec.kEncodedInvalidHandleValue)
84 return true;
85
86 if (index < this.handleIndex || index >= this.handleIndexLimit)
87 return false;
88
89 // This is safe because handle indices are uint32.
90 this.handleIndex = index + 1;
91 return true;
92 }
93
94 Validator.prototype.validateHandle = function(offset, nullable) {
95 var index = this.message.buffer.getUint32(offset);
96
97 if (index === codec.kEncodedInvalidHandleValue)
98 return nullable ?
99 validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
100
101 if (!this.claimHandle(index))
102 return validationError.ILLEGAL_HANDLE;
103 return validationError.NONE;
104 }
105
106 Validator.prototype.validateStructHeader =
107 function(offset, minNumBytes, minNumFields) {
108 if (!codec.isAligned(offset))
109 return validationError.MISALIGNED_OBJECT;
110
111 if (!this.isValidRange(offset, codec.kStructHeaderSize))
112 return validationError.ILLEGAL_MEMORY_RANGE;
113
114 var numBytes = this.message.buffer.getUint32(offset);
115 var numFields = this.message.buffer.getUint32(offset + 4);
116
117 if (numBytes < minNumBytes || numFields < minNumFields)
118 return validationError.UNEXPECTED_STRUCT_HEADER;
119
120 if (!this.claimRange(offset, numBytes))
121 return validationError.ILLEGAL_MEMORY_RANGE;
122
123 return validationError.NONE;
124 }
125
126 Validator.prototype.validateMessageHeader = function() {
127 var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 2);
128 if (err != validationError.NONE)
129 return err;
130
131 var numBytes = this.message.getHeaderNumBytes();
132 var numFields = this.message.getHeaderNumFields();
133
134 var validNumFieldsAndNumBytes =
135 (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
136 (numFields == 3 &&
137 numBytes == codec.kMessageWithRequestIDHeaderSize) ||
138 (numFields > 3 &&
139 numBytes >= codec.kMessageWithRequestIDHeaderSize);
140 if (!validNumFieldsAndNumBytes)
141 return validationError.UNEXPECTED_STRUCT_HEADER;
142
143 var expectsResponse = this.message.expectsResponse();
144 var isResponse = this.message.isResponse();
145
146 if (numFields == 2 && (expectsResponse || isResponse))
147 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
148
149 if (isResponse && expectsResponse)
150 return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
151
152 return validationError.NONE;
153 }
154
155 // Returns the message.buffer relative offset this pointer "points to",
156 // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
157 // pointer's value is not valid.
158 Validator.prototype.decodePointer = function(offset) {
159 var pointerValue = this.message.buffer.getUint64(offset);
160 if (pointerValue === 0)
161 return NULL_MOJO_POINTER;
162 var bufferOffset = offset + pointerValue;
163 return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
164 }
165
166 Validator.prototype.validateArrayPointer = function(
167 offset, elementSize, elementType, nullable, expectedDimensionSizes,
168 currentDimension) {
169 var arrayOffset = this.decodePointer(offset);
170 if (arrayOffset === null)
171 return validationError.ILLEGAL_POINTER;
172
173 if (arrayOffset === NULL_MOJO_POINTER)
174 return nullable ?
175 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
176
177 return this.validateArray(arrayOffset, elementSize, elementType,
178 expectedDimensionSizes, currentDimension);
179 }
180
181 Validator.prototype.validateStructPointer = function(
182 offset, structClass, nullable) {
183 var structOffset = this.decodePointer(offset);
184 if (structOffset === null)
185 return validationError.ILLEGAL_POINTER;
186
187 if (structOffset === NULL_MOJO_POINTER)
188 return nullable ?
189 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
190
191 return structClass.validate(this, structOffset);
192 }
193
194 // This method assumes that the array at arrayPointerOffset has
195 // been validated.
196
197 Validator.prototype.arrayLength = function(arrayPointerOffset) {
198 var arrayOffset = this.decodePointer(arrayPointerOffset);
199 return this.message.buffer.getUint32(arrayOffset + 4);
200 }
201
202 Validator.prototype.validateMapPointer = function(
203 offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
204 // Validate the implicit map struct:
205 // struct {array<keyClass> keys; array<valueClass> values};
206 var structOffset = this.decodePointer(offset);
207 if (structOffset === null)
208 return validationError.ILLEGAL_POINTER;
209
210 if (structOffset === NULL_MOJO_POINTER)
211 return mapIsNullable ?
212 validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
213
214 var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
215 var err = this.validateStructHeader(structOffset, mapEncodedSize, 2);
216 if (err !== validationError.NONE)
217 return err;
218
219 // Validate the keys array.
220 var keysArrayPointerOffset = structOffset + codec.kStructHeaderSize;
221 err = this.validateArrayPointer(
222 keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
223 if (err !== validationError.NONE)
224 return err;
225
226 // Validate the values array.
227 var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
228 var valuesArrayDimensions = [0]; // Validate the actual length below.
229 if (valueClass instanceof codec.ArrayOf)
230 valuesArrayDimensions =
231 valuesArrayDimensions.concat(valueClass.dimensions());
232 var err = this.validateArrayPointer(valuesArrayPointerOffset,
233 valueClass.encodedSize,
234 valueClass,
235 valueIsNullable,
236 valuesArrayDimensions,
237 0);
238 if (err !== validationError.NONE)
239 return err;
240
241 // Validate the lengths of the keys and values arrays.
242 var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
243 var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
244 if (keysArrayLength != valuesArrayLength)
245 return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
246
247 return validationError.NONE;
248 }
249
250 Validator.prototype.validateStringPointer = function(offset, nullable) {
251 return this.validateArrayPointer(
252 offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0);
253 }
254
255 // Similar to Array_Data<T>::Validate()
256 // mojo/public/cpp/bindings/lib/array_internal.h
257
258 Validator.prototype.validateArray =
259 function (offset, elementSize, elementType, expectedDimensionSizes,
260 currentDimension) {
261 if (!codec.isAligned(offset))
262 return validationError.MISALIGNED_OBJECT;
263
264 if (!this.isValidRange(offset, codec.kArrayHeaderSize))
265 return validationError.ILLEGAL_MEMORY_RANGE;
266
267 var numBytes = this.message.buffer.getUint32(offset);
268 var numElements = this.message.buffer.getUint32(offset + 4);
269
270 // Note: this computation is "safe" because elementSize <= 8 and
271 // numElements is a uint32.
272 var elementsTotalSize = (elementType === codec.PackedBool) ?
273 Math.ceil(numElements / 8) : (elementSize * numElements);
274
275 if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
276 return validationError.UNEXPECTED_ARRAY_HEADER;
277
278 if (expectedDimensionSizes[currentDimension] != 0 &&
279 numElements != expectedDimensionSizes[currentDimension]) {
280 return validationError.UNEXPECTED_ARRAY_HEADER;
281 }
282
283 if (!this.claimRange(offset, numBytes))
284 return validationError.ILLEGAL_MEMORY_RANGE;
285
286 // Validate the array's elements if they are pointers or handles.
287
288 var elementsOffset = offset + codec.kArrayHeaderSize;
289 var nullable = isNullable(elementType);
290
291 if (isHandleClass(elementType))
292 return this.validateHandleElements(elementsOffset, numElements, nullable);
293 if (isStringClass(elementType))
294 return this.validateArrayElements(
295 elementsOffset, numElements, codec.Uint8, nullable, [0], 0);
296 if (elementType instanceof codec.PointerTo)
297 return this.validateStructElements(
298 elementsOffset, numElements, elementType.cls, nullable);
299 if (elementType instanceof codec.ArrayOf)
300 return this.validateArrayElements(
301 elementsOffset, numElements, elementType.cls, nullable,
302 expectedDimensionSizes, currentDimension + 1);
303
304 return validationError.NONE;
305 }
306
307 // Note: the |offset + i * elementSize| computation in the validateFooElements
308 // methods below is "safe" because elementSize <= 8, offset and
309 // numElements are uint32, and 0 <= i < numElements.
310
311 Validator.prototype.validateHandleElements =
312 function(offset, numElements, nullable) {
313 var elementSize = codec.Handle.encodedSize;
314 for (var i = 0; i < numElements; i++) {
315 var elementOffset = offset + i * elementSize;
316 var err = this.validateHandle(elementOffset, nullable);
317 if (err != validationError.NONE)
318 return err;
319 }
320 return validationError.NONE;
321 }
322
323 // The elementClass parameter is the element type of the element arrays.
324 Validator.prototype.validateArrayElements =
325 function(offset, numElements, elementClass, nullable,
326 expectedDimensionSizes, currentDimension) {
327 var elementSize = codec.PointerTo.prototype.encodedSize;
328 for (var i = 0; i < numElements; i++) {
329 var elementOffset = offset + i * elementSize;
330 var err = this.validateArrayPointer(
331 elementOffset, elementClass.encodedSize, elementClass, nullable,
332 expectedDimensionSizes, currentDimension);
333 if (err != validationError.NONE)
334 return err;
335 }
336 return validationError.NONE;
337 }
338
339 Validator.prototype.validateStructElements =
340 function(offset, numElements, structClass, nullable) {
341 var elementSize = codec.PointerTo.prototype.encodedSize;
342 for (var i = 0; i < numElements; i++) {
343 var elementOffset = offset + i * elementSize;
344 var err =
345 this.validateStructPointer(elementOffset, structClass, nullable);
346 if (err != validationError.NONE)
347 return err;
348 }
349 return validationError.NONE;
350 }
351
352 var exports = {};
353 exports.validationError = validationError;
354 exports.Validator = Validator;
355 return exports;
356 });
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