LCOV - code coverage report
Current view: directory - src/trusted/validator - validation_cache_test.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 232 220 94.8 %
Date: 2014-06-18 Functions: 0 0 -

       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                 : }

Generated by: LCOV version 1.7