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

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: Skip unexpected null tests" 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 is safe because handle indices are uint32.
72 this.handleIndex = index + 1;
73 return true;
74 }
75
76 Validator.prototype.validateHandle = function(offset) {
77 var index = this.message.buffer.getUint32(offset);
78 if (!this.claimHandle(index))
79 return validationError.ILLEGAL_HANDLE;
80 return validationError.NONE;
81 }
82
57 Validator.prototype.validateStructHeader = 83 Validator.prototype.validateStructHeader =
58 function(offset, minNumBytes, minNumFields) { 84 function(offset, minNumBytes, minNumFields) {
59 if (!codec.isAligned(offset)) 85 if (!codec.isAligned(offset))
60 return validationError.MISALIGNED_OBJECT; 86 return validationError.MISALIGNED_OBJECT;
61 87
62 if (!this.isValidRange(offset, codec.kStructHeaderSize)) 88 if (!this.isValidRange(offset, codec.kStructHeaderSize))
63 return validationError.ILLEGAL_MEMORY_RANGE; 89 return validationError.ILLEGAL_MEMORY_RANGE;
64 90
65 var numBytes = this.message.buffer.getUint32(offset); 91 var numBytes = this.message.buffer.getUint32(offset);
66 var numFields = this.message.buffer.getUint32(offset + 4); 92 var numFields = this.message.buffer.getUint32(offset + 4);
67 93
68 if (numBytes < minNumBytes || numFields < minNumFields) 94 if (numBytes < minNumBytes || numFields < minNumFields)
69 return validationError.UNEXPECTED_STRUCT_HEADER; 95 return validationError.UNEXPECTED_STRUCT_HEADER;
70 96
71 if (!this.claimRange(offset, numBytes)) 97 if (!this.claimRange(offset, numBytes))
72 return validationError.ILLEGAL_MEMORY_RANGE; 98 return validationError.ILLEGAL_MEMORY_RANGE;
73 99
74 return validationError.NONE; 100 return validationError.NONE;
75 } 101 }
76 102
77 Validator.prototype.validateMessageHeader = function() { 103 Validator.prototype.validateMessageHeader = function() {
104 var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 2);
105 if (err != validationError.NONE)
106 return err;
107
78 var numBytes = this.message.getHeaderNumBytes(); 108 var numBytes = this.message.getHeaderNumBytes();
79 var numFields = this.message.getHeaderNumFields(); 109 var numFields = this.message.getHeaderNumFields();
80 110
81 var validNumFieldsAndNumBytes = 111 var validNumFieldsAndNumBytes =
82 (numFields == 2 && numBytes == codec.kMessageHeaderSize) || 112 (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
83 (numFields == 3 && 113 (numFields == 3 &&
84 numBytes == codec.kMessageWithRequestIDHeaderSize) || 114 numBytes == codec.kMessageWithRequestIDHeaderSize) ||
85 (numFields > 3 && 115 (numFields > 3 &&
86 numBytes >= codec.kMessageWithRequestIDHeaderSize); 116 numBytes >= codec.kMessageWithRequestIDHeaderSize);
87 if (!validNumFieldsAndNumBytes) 117 if (!validNumFieldsAndNumBytes)
88 return validationError.UNEXPECTED_STRUCT_HEADER; 118 return validationError.UNEXPECTED_STRUCT_HEADER;
89 119
90 var expectsResponse = this.message.expectsResponse(); 120 var expectsResponse = this.message.expectsResponse();
91 var isResponse = this.message.isResponse(); 121 var isResponse = this.message.isResponse();
92 122
93 if (numFields == 2 && (expectsResponse || isResponse)) 123 if (numFields == 2 && (expectsResponse || isResponse))
94 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID; 124 return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
95 125
96 if (isResponse && expectsResponse) 126 if (isResponse && expectsResponse)
97 return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION; 127 return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
98 128
99 return validationError.NONE; 129 return validationError.NONE;
100 } 130 }
101 131
102 Validator.prototype.validateMessage = function() { 132 // Returns the message.buffer relative offset this pointer "points to",
103 var err = this.validateStructHeader(0, codec.kStructHeaderSize, 2); 133 // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
104 if (err != validationError.NONE) 134 // pointer's value is not valid.
105 return err; 135 Validator.prototype.decodePointer = function(offset) {
136 var pointerValue = this.message.buffer.getUint64(offset);
137 if (pointerValue === 0)
138 return NULL_MOJO_POINTER;
139 var bufferOffset = offset + pointerValue;
140 return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
141 }
106 142
107 return this.validateMessageHeader(); 143 Validator.prototype.validateArrayPointer =
144 function(offset, elementSize, expectedElementCount, elementType) {
145 var arrayOffset = this.decodePointer(offset);
146 if (arrayOffset === null)
147 return validationError.ILLEGAL_POINTER;
148 if (arrayOffset === NULL_MOJO_POINTER)
149 return validationError.NONE;
150 return this.validateArray(
151 arrayOffset, elementSize, expectedElementCount, elementType);
152 }
153
154 Validator.prototype.validateStructPointer = function(offset, structClass) {
155 var structOffset = this.decodePointer(offset);
156 if (structOffset === null)
157 return validationError.ILLEGAL_POINTER;
158 if (structOffset === NULL_MOJO_POINTER)
159 return validationError.NONE;
160 return structClass.validate(this, structOffset);
161 }
162
163 Validator.prototype.validateStringPointer = function(offset) {
164 return this.validateArrayPointer(
165 offset, codec.Uint8.encodedSize, 0, codec.Uint8);
166 }
167
168 // Similar to Array_Data<T>::Validate()
169 // mojo/public/cpp/bindings/lib/array_internal.h
170
171 Validator.prototype.validateArray =
172 function (offset, elementSize, expectedElementCount, elementType) {
173 if (!codec.isAligned(offset))
174 return validationError.MISALIGNED_OBJECT;
175
176 if (!this.isValidRange(offset, codec.kArrayHeaderSize))
177 return validationError.ILLEGAL_MEMORY_RANGE;
178
179 var numBytes = this.message.buffer.getUint32(offset);
180 var numElements = this.message.buffer.getUint32(offset + 4);
181
182 // Note: this computation is "safe" because elementSize <= 8 and
183 // numElements is a uint32.
184 var elementsTotalSize = (elementType === codec.PackedBool) ?
185 Math.ceil(numElements / 8) : (elementSize * numElements);
186
187 if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
188 return validationError.UNEXPECTED_ARRAY_HEADER;
189
190 if (expectedElementCount != 0 && numElements != expectedElementCount)
191 return validationError.UNEXPECTED_ARRAY_HEADER;
192
193 if (!this.claimRange(offset, numBytes))
194 return validationError.ILLEGAL_MEMORY_RANGE;
195
196 // Validate the array's elements if they are pointers or handles.
197
198 var elementsOffset = offset + codec.kArrayHeaderSize;
199 if (elementType === codec.Handle)
200 return this.validateHandleElements(elementsOffset, numElements);
201 if (elementType instanceof codec.PointerTo)
202 return this.validateStructElements(
203 elementsOffset, numElements, elementType.cls);
204 if (elementType instanceof codec.String)
205 return this.validateArrayElements(
206 elementsOffset, numElements, codec.Uint8);
207 if (elementType instanceof codec.ArrayOf)
208 return this.validateArrayElements(
209 elementsOffset, numElements, elementType.cls);
210
211 return validationError.NONE;
212 }
213
214 // Note: the |offset + i * elementSize| computation in the validateFooElements
215 // methods below is "safe" because elementSize <= 8, offset and
216 // numElements are uint32, and 0 <= i < numElements.
217
218 Validator.prototype.validateHandleElements = function(offset, numElements) {
219 var elementSize = codec.Handle.encodedSize;
220 for (var i = 0; i < numElements; i++) {
221 var index = this.message.buffer.getUint32(offset + i * elementSize);
222 if (!this.claimHandle(index))
223 return validationError.ILLEGAL_HANDLE;
224 }
225 return validationError.NONE;
226 }
227
228 // The elementClass parameter is the element type of the element arrays.
229 Validator.prototype.validateArrayElements =
230 function(offset, numElements, elementClass) {
231 var elementSize = codec.PointerTo.prototype.encodedSize;
232 for (var i = 0; i < numElements; i++) {
233 var elementOffset = offset + i * elementSize;
234 var err = this.validateArrayPointer(
235 elementOffset, elementClass.encodedSize, 0, elementClass);
236 if (err != validationError.NONE)
237 return err;
238 }
239 return validationError.NONE;
240 }
241
242 Validator.prototype.validateStructElements =
243 function(offset, numElements, structClass) {
244 var elementSize = codec.PointerTo.prototype.encodedSize;
245 for (var i = 0; i < numElements; i++) {
246 var elementOffset = offset + i * elementSize;
247 var err = this.validateStructPointer(elementOffset, structClass);
248 if (err != validationError.NONE)
249 return err;
250 }
251 return validationError.NONE;
108 } 252 }
109 253
110 var exports = {}; 254 var exports = {};
111 exports.validationError = validationError; 255 exports.validationError = validationError;
112 exports.Validator = Validator; 256 exports.Validator = Validator;
113 return exports; 257 return exports;
114 }); 258 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698