1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include "gtest/gtest.h"
8 :
9 : #include <fcntl.h>
10 :
11 : #include "native_client/src/include/nacl_compiler_annotations.h"
12 : #include "native_client/src/shared/platform/nacl_host_desc.h"
13 : #include "native_client/src/shared/platform/nacl_log.h"
14 : #include "native_client/src/shared/utils/types.h"
15 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
16 : #include "native_client/src/trusted/validator/ncvalidate.h"
17 : #include "native_client/src/trusted/validator/validation_cache.h"
18 : #include "native_client/src/trusted/validator/validation_cache_internal.h"
19 : #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86.h"
20 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
21 : #include "native_client/src/trusted/validator/rich_file_info.h"
22 : #include "native_client/src/trusted/validator/validation_metadata.h"
23 :
24 : #define CONTEXT_MARKER 31
25 : #define QUERY_MARKER 37
26 :
27 : #define CODE_SIZE 32
28 :
29 : #if defined(__mips__)
30 : # define NOP 0x00
31 : #else // x86
32 : # define NOP 0x90
33 : #endif
34 :
35 :
36 : #if defined(__mips__)
37 : // jr ra
38 : const char ret[CODE_SIZE + 1] =
39 : "\x08\x00\xe0\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
40 : "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
41 : #else // x86
42 : // ret
43 : const char ret[CODE_SIZE + 1] =
44 : "\xc3\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
45 : "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90";
46 : #endif
47 :
48 : // pblendw $0xc0,%xmm0,%xmm2
49 : const char sse41[CODE_SIZE + 1] =
50 : "\x66\x0f\x3a\x0e\xd0\xc0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
51 : "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90";
52 :
53 : struct MockContext {
54 : int marker; /* Sanity check that we're getting the right object. */
55 : int query_result;
56 : int add_count_expected;
57 : bool set_validates_expected;
58 : bool query_destroyed;
59 : };
60 :
61 : enum MockQueryState {
62 : QUERY_CREATED,
63 : QUERY_GET_CALLED,
64 : QUERY_SET_CALLED,
65 : QUERY_DESTROYED
66 : };
67 :
68 : struct MockQuery {
69 : /* Sanity check that we're getting the right object. */
70 : int marker;
71 : MockQueryState state;
72 : int add_count;
73 : MockContext *context;
74 : };
75 :
76 8 : void *MockCreateQuery(void *handle) {
77 8 : MockContext *mcontext = (MockContext *) handle;
78 8 : MockQuery *mquery = (MockQuery *) malloc(sizeof(MockQuery));
79 32 : EXPECT_EQ(CONTEXT_MARKER, mcontext->marker);
80 8 : mquery->marker = QUERY_MARKER;
81 8 : mquery->state = QUERY_CREATED;
82 8 : mquery->add_count = 0;
83 8 : mquery->context = mcontext;
84 8 : return mquery;
85 0 : }
86 :
87 38 : void MockAddData(void *query, const unsigned char *data, size_t length) {
88 76 : UNREFERENCED_PARAMETER(data);
89 38 : MockQuery *mquery = (MockQuery *) query;
90 190 : ASSERT_EQ(QUERY_MARKER, mquery->marker);
91 152 : EXPECT_EQ(QUERY_CREATED, mquery->state);
92 : /* Small data is suspicious. */
93 152 : EXPECT_LE((size_t) 2, length);
94 38 : mquery->add_count += 1;
95 76 : }
96 :
97 8 : int MockQueryCodeValidates(void *query) {
98 8 : MockQuery *mquery = (MockQuery *) query;
99 32 : EXPECT_EQ(QUERY_MARKER, mquery->marker);
100 32 : EXPECT_EQ(QUERY_CREATED, mquery->state);
101 32 : EXPECT_EQ(mquery->context->add_count_expected, mquery->add_count);
102 8 : mquery->state = QUERY_GET_CALLED;
103 8 : return mquery->context->query_result;
104 0 : }
105 :
106 2 : void MockSetCodeValidates(void *query) {
107 2 : MockQuery *mquery = (MockQuery *) query;
108 10 : ASSERT_EQ(QUERY_MARKER, mquery->marker);
109 8 : EXPECT_EQ(QUERY_GET_CALLED, mquery->state);
110 8 : EXPECT_EQ(true, mquery->context->set_validates_expected);
111 2 : mquery->state = QUERY_SET_CALLED;
112 4 : }
113 :
114 8 : void MockDestroyQuery(void *query) {
115 8 : MockQuery *mquery = (MockQuery *) query;
116 40 : ASSERT_EQ(QUERY_MARKER, mquery->marker);
117 8 : if (mquery->context->set_validates_expected) {
118 8 : EXPECT_EQ(QUERY_SET_CALLED, mquery->state);
119 2 : } else {
120 24 : EXPECT_EQ(QUERY_GET_CALLED, mquery->state);
121 : }
122 8 : mquery->state = QUERY_DESTROYED;
123 8 : mquery->context->query_destroyed = true;
124 8 : free(mquery);
125 16 : }
126 :
127 : /* Hint that the validation should use the (fake) cache. */
128 7 : int MockCachingIsInexpensive(const struct NaClValidationMetadata *metadata) {
129 14 : UNREFERENCED_PARAMETER(metadata);
130 7 : return 1;
131 : }
132 :
133 18 : class ValidationCachingInterfaceTests : public ::testing::Test {
134 : protected:
135 : MockContext context;
136 : NaClValidationMetadata *metadata_ptr;
137 : NaClValidationCache cache;
138 : const struct NaClValidatorInterface *validator;
139 : NaClCPUFeatures *cpu_features;
140 :
141 : unsigned char code_buffer[CODE_SIZE];
142 :
143 : void SetUp() {
144 9 : context.marker = CONTEXT_MARKER;
145 9 : context.query_result = 1;
146 9 : context.add_count_expected = 4;
147 9 : context.set_validates_expected = false;
148 9 : context.query_destroyed = false;
149 :
150 9 : metadata_ptr = NULL;
151 :
152 9 : cache.handle = &context;
153 9 : cache.CreateQuery = MockCreateQuery;
154 9 : cache.AddData = MockAddData;
155 9 : cache.QueryKnownToValidate = MockQueryCodeValidates;
156 9 : cache.SetKnownToValidate = MockSetCodeValidates;
157 9 : cache.DestroyQuery = MockDestroyQuery;
158 9 : cache.CachingIsInexpensive = MockCachingIsInexpensive;
159 :
160 9 : validator = NaClCreateValidator();
161 9 : cpu_features = (NaClCPUFeatures *) malloc(validator->CPUFeatureSize);
162 36 : EXPECT_NE(cpu_features, (NaClCPUFeatures *) NULL);
163 9 : validator->SetAllCPUFeatures(cpu_features);
164 :
165 9 : memset(code_buffer, NOP, sizeof(code_buffer));
166 9 : }
167 :
168 : NaClValidationStatus Validate() {
169 7 : return validator->Validate(0, code_buffer, 32,
170 : FALSE, /* stubout_mode */
171 : FALSE, /* readonly_test */
172 : cpu_features,
173 : metadata_ptr,
174 : &cache);
175 : }
176 :
177 : void TearDown() {
178 9 : free(cpu_features);
179 9 : }
180 : };
181 :
182 8 : TEST_F(ValidationCachingInterfaceTests, Sanity) {
183 1 : void *query = cache.CreateQuery(cache.handle);
184 1 : context.add_count_expected = 2;
185 1 : cache.AddData(query, NULL, 6);
186 1 : cache.AddData(query, NULL, 128);
187 4 : EXPECT_EQ(1, cache.QueryKnownToValidate(query));
188 1 : cache.DestroyQuery(query);
189 4 : EXPECT_EQ(true, context.query_destroyed);
190 1 : }
191 :
192 8 : TEST_F(ValidationCachingInterfaceTests, NoCache) {
193 1 : const struct NaClValidatorInterface *validator = NaClCreateValidator();
194 1 : NaClValidationStatus status = validator->Validate(
195 : 0, code_buffer, CODE_SIZE,
196 : FALSE, /* stubout_mode */
197 : FALSE, /* readonly_test */
198 : cpu_features,
199 : NULL, /* metadata */
200 : NULL);
201 4 : EXPECT_EQ(NaClValidationSucceeded, status);
202 1 : }
203 :
204 8 : TEST_F(ValidationCachingInterfaceTests, CacheHit) {
205 1 : NaClValidationStatus status = Validate();
206 4 : EXPECT_EQ(NaClValidationSucceeded, status);
207 4 : EXPECT_EQ(true, context.query_destroyed);
208 1 : }
209 :
210 8 : TEST_F(ValidationCachingInterfaceTests, CacheMiss) {
211 1 : context.query_result = 0;
212 1 : context.set_validates_expected = true;
213 1 : NaClValidationStatus status = Validate();
214 4 : EXPECT_EQ(NaClValidationSucceeded, status);
215 4 : EXPECT_EQ(true, context.query_destroyed);
216 1 : }
217 :
218 : #if defined(__x86_64__) || defined(__i386__)
219 8 : TEST_F(ValidationCachingInterfaceTests, SSE4Allowed) {
220 1 : memcpy(code_buffer, sse41, CODE_SIZE);
221 1 : context.query_result = 0;
222 1 : context.set_validates_expected = true;
223 1 : NaClValidationStatus status = Validate();
224 4 : EXPECT_EQ(NaClValidationSucceeded, status);
225 4 : EXPECT_EQ(true, context.query_destroyed);
226 1 : }
227 :
228 8 : TEST_F(ValidationCachingInterfaceTests, SSE4Stubout) {
229 1 : memcpy(code_buffer, sse41, CODE_SIZE);
230 1 : context.query_result = 0;
231 : /* TODO(jfb) Use a safe cast here, this test should only run for x86. */
232 1 : NaClSetCPUFeatureX86((NaClCPUFeaturesX86 *) cpu_features,
233 : NaClCPUFeatureX86_SSE41, 0);
234 1 : NaClValidationStatus status = Validate();
235 4 : EXPECT_EQ(NaClValidationSucceeded, status);
236 4 : EXPECT_EQ(true, context.query_destroyed);
237 1 : }
238 : #endif
239 :
240 8 : TEST_F(ValidationCachingInterfaceTests, IllegalInst) {
241 1 : memcpy(code_buffer, ret, CODE_SIZE);
242 1 : context.query_result = 0;
243 1 : NaClValidationStatus status = Validate();
244 4 : EXPECT_EQ(NaClValidationFailed, status);
245 4 : EXPECT_EQ(true, context.query_destroyed);
246 1 : }
247 :
248 8 : TEST_F(ValidationCachingInterfaceTests, IllegalCacheHit) {
249 1 : memcpy(code_buffer, ret, CODE_SIZE);
250 1 : NaClValidationStatus status = Validate();
251 : // Success proves the cache shortcircuted validation.
252 4 : EXPECT_EQ(NaClValidationSucceeded, status);
253 4 : EXPECT_EQ(true, context.query_destroyed);
254 1 : }
255 :
256 8 : TEST_F(ValidationCachingInterfaceTests, Metadata) {
257 1 : NaClValidationMetadata metadata;
258 1 : memset(&metadata, 0, sizeof(metadata));
259 1 : metadata.identity_type = NaClCodeIdentityFile;
260 1 : metadata.file_name = (char *) "foobar";
261 1 : metadata.file_name_length = strlen(metadata.file_name);
262 1 : metadata.file_size = CODE_SIZE;
263 1 : metadata.mtime = 100;
264 1 : metadata_ptr = &metadata;
265 1 : context.add_count_expected = 12;
266 1 : NaClValidationStatus status = Validate();
267 4 : EXPECT_EQ(NaClValidationSucceeded, status);
268 4 : EXPECT_EQ(true, context.query_destroyed);
269 1 : }
270 :
271 8 : class ValidationCachingSerializationTests : public ::testing::Test {
272 : protected:
273 : struct NaClRichFileInfo info;
274 : uint8_t *buffer;
275 : uint32_t buffer_length;
276 : struct NaClRichFileInfo inp;
277 : struct NaClRichFileInfo outp;
278 :
279 : void SetUp() {
280 4 : buffer = 0;
281 4 : buffer_length = 0;
282 4 : NaClRichFileInfoCtor(&inp);
283 4 : NaClRichFileInfoCtor(&outp);
284 4 : }
285 :
286 : void TearDown() {
287 4 : free(buffer);
288 : // Don't free the inp structure, it does not contain malloced memory.
289 4 : NaClRichFileInfoDtor(&outp);
290 4 : }
291 : };
292 :
293 8 : TEST_F(ValidationCachingSerializationTests, NormalOperationSimple) {
294 1 : inp.known_file = 0;
295 1 : inp.file_path = "foo";
296 1 : inp.file_path_length = 3;
297 4 : EXPECT_EQ(0, NaClSerializeNaClDescMetadata(&inp, &buffer, &buffer_length));
298 4 : EXPECT_EQ(0, NaClDeserializeNaClDescMetadata(buffer, buffer_length, &outp));
299 :
300 4 : EXPECT_EQ((uint8_t) 0, outp.known_file);
301 4 : EXPECT_EQ((uint32_t) 0, outp.file_path_length);
302 4 : EXPECT_EQ(NULL, outp.file_path);
303 1 : }
304 :
305 8 : TEST_F(ValidationCachingSerializationTests, NormalOperationFull) {
306 1 : inp.known_file = 1;
307 1 : inp.file_path = "foo";
308 1 : inp.file_path_length = 3;
309 :
310 4 : EXPECT_EQ(0, NaClSerializeNaClDescMetadata(&inp, &buffer, &buffer_length));
311 4 : EXPECT_EQ(0, NaClDeserializeNaClDescMetadata(buffer, buffer_length, &outp));
312 :
313 4 : EXPECT_EQ((uint8_t) 1, outp.known_file);
314 4 : EXPECT_EQ((uint32_t) 3, outp.file_path_length);
315 4 : EXPECT_EQ(0, memcmp("foo", outp.file_path, outp.file_path_length));
316 1 : }
317 :
318 8 : TEST_F(ValidationCachingSerializationTests, BadSizeSimple) {
319 1 : inp.known_file = 0;
320 4 : EXPECT_EQ(0, NaClSerializeNaClDescMetadata(&inp, &buffer, &buffer_length));
321 2 : for (uint32_t i = -1; i <= buffer_length + 4; i++) {
322 : /* The only case that is OK. */
323 0 : if (i == buffer_length)
324 0 : continue;
325 :
326 : /* Wrong number of bytes, fail. */
327 0 : EXPECT_EQ(1, NaClDeserializeNaClDescMetadata(buffer, i, &outp));
328 0 : }
329 1 : }
330 :
331 8 : TEST_F(ValidationCachingSerializationTests, BadSizeFull) {
332 1 : inp.known_file = 1;
333 1 : inp.file_path = "foo";
334 1 : inp.file_path_length = 3;
335 4 : EXPECT_EQ(0, NaClSerializeNaClDescMetadata(&inp, &buffer, &buffer_length));
336 2 : for (uint32_t i = -1; i <= buffer_length + 4; i++) {
337 : /* The only case that is OK. */
338 0 : if (i == buffer_length)
339 0 : continue;
340 :
341 : /* Wrong number of bytes, fail. */
342 0 : EXPECT_EQ(1, NaClDeserializeNaClDescMetadata(buffer, i, &outp));
343 : /* Paranoia. */
344 0 : EXPECT_EQ(0, outp.known_file);
345 : /* Make sure we don't leak on failure. */
346 0 : EXPECT_EQ(NULL, outp.file_path);
347 0 : }
348 1 : }
349 :
350 : static char *AN_ARBITRARY_FILE_PATH = NULL;
351 :
352 6 : class ValidationCachingFileOriginTests : public ::testing::Test {
353 : protected:
354 : struct NaClDesc *desc;
355 :
356 : struct NaClRichFileInfo inp;
357 : struct NaClRichFileInfo outp;
358 :
359 : void SetUp() {
360 3 : struct NaClHostDesc *host_desc = NULL;
361 3 : int fd = open(AN_ARBITRARY_FILE_PATH, O_RDONLY);
362 :
363 3 : desc = NULL;
364 3 : NaClRichFileInfoCtor(&inp);
365 3 : NaClRichFileInfoCtor(&outp);
366 :
367 15 : ASSERT_NE(-1, fd);
368 3 : host_desc = NaClHostDescPosixMake(fd, NACL_ABI_O_RDONLY);
369 3 : desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
370 15 : ASSERT_NE((struct NaClDesc *) NULL, desc);
371 3 : }
372 :
373 : void TearDown() {
374 : // Don't free the inp structure, it does not contain malloced memory.
375 3 : NaClRichFileInfoDtor(&outp);
376 3 : NaClDescSafeUnref(desc);
377 3 : }
378 : };
379 :
380 8 : TEST_F(ValidationCachingFileOriginTests, None) {
381 4 : EXPECT_EQ(1, NaClGetFileOriginInfo(desc, &outp));
382 1 : }
383 :
384 8 : TEST_F(ValidationCachingFileOriginTests, Simple) {
385 1 : inp.known_file = 0;
386 1 : inp.file_path = "foobar";
387 1 : inp.file_path_length = 6;
388 4 : EXPECT_EQ(0, NaClSetFileOriginInfo(desc, &inp));
389 4 : EXPECT_EQ(0, NaClGetFileOriginInfo(desc, &outp));
390 :
391 4 : EXPECT_EQ(0, outp.known_file);
392 4 : EXPECT_EQ((uint32_t) 0, outp.file_path_length);
393 4 : EXPECT_EQ(NULL, outp.file_path);
394 1 : }
395 :
396 :
397 8 : TEST_F(ValidationCachingFileOriginTests, Full) {
398 1 : inp.known_file = 1;
399 1 : inp.file_path = "foobar";
400 1 : inp.file_path_length = 6;
401 4 : EXPECT_EQ(0, NaClSetFileOriginInfo(desc, &inp));
402 4 : EXPECT_EQ(0, NaClGetFileOriginInfo(desc, &outp));
403 :
404 4 : EXPECT_EQ(1, outp.known_file);
405 4 : EXPECT_EQ((uint32_t) 6, outp.file_path_length);
406 4 : EXPECT_EQ(0, memcmp("foobar", outp.file_path, outp.file_path_length));
407 1 : }
408 :
409 : // Test driver function.
410 1 : int main(int argc, char *argv[]) {
411 : // One file we know must exist is this executable.
412 1 : AN_ARBITRARY_FILE_PATH = argv[0];
413 : // The IllegalInst test touches the log mutex deep inside the validator.
414 : // This causes an SEH exception to be thrown on Windows if the mutex is not
415 : // initialized.
416 : // http://code.google.com/p/nativeclient/issues/detail?id=1696
417 1 : NaClLogModuleInit();
418 1 : testing::InitGoogleTest(&argc, argv);
419 1 : return RUN_ALL_TESTS();
420 : }
|