LCOV - code coverage report
Current view: directory - src/trusted/validator_ragel - dfa_validate_32.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 111 96 86.5 %
Date: 2014-07-02 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                 : /* Implement the Validator API for the x86-32 architecture. */
       8                 : #include <errno.h>
       9                 : #include <string.h>
      10                 : 
      11                 : #include "native_client/src/shared/platform/nacl_check.h"
      12                 : #include "native_client/src/trusted/validator/validation_cache.h"
      13                 : #include "native_client/src/trusted/validator_ragel/bitmap.h"
      14                 : #include "native_client/src/trusted/validator_ragel/dfa_validate_common.h"
      15                 : #include "native_client/src/trusted/validator_ragel/validator.h"
      16                 : 
      17                 : /*
      18                 :  * Be sure the correct compile flags are defined for this.
      19                 :  * TODO(khim): Try to figure out if these checks actually make any sense.
      20                 :  *             I don't foresee any use in cross-environment, but it should work
      21                 :  *             and may be useful in some case so why not?
      22                 :  */
      23                 : #if NACL_ARCH(NACL_TARGET_ARCH) != NACL_x86 || NACL_TARGET_SUBARCH != 32
      24                 : # error "Can't compile, target is for x86-32"
      25                 : #endif
      26                 : 
      27            2463 : NaClValidationStatus ApplyDfaValidator_x86_32(
      28                 :     uintptr_t guest_addr,
      29                 :     uint8_t *data,
      30                 :     size_t size,
      31                 :     int stubout_mode,
      32                 :     int readonly_text,
      33                 :     const NaClCPUFeatures *f,
      34                 :     const struct NaClValidationMetadata *metadata,
      35                 :     struct NaClValidationCache *cache) {
      36                 :   /* TODO(jfb) Use a safe cast here. */
      37            2463 :   NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
      38            2463 :   enum NaClValidationStatus status = NaClValidationFailed;
      39            2463 :   int did_stubout = 0;
      40            2463 :   void *query = NULL;
      41                 :   UNREFERENCED_PARAMETER(guest_addr);
      42                 : 
      43            2463 :   if (stubout_mode)
      44               0 :     return NaClValidationFailedNotImplemented;
      45            2463 :   if (!NaClArchSupportedX86(cpu_features))
      46               0 :     return NaClValidationFailedCpuNotSupported;
      47            2463 :   if (size & kBundleMask)
      48               0 :     return NaClValidationFailed;
      49                 : 
      50                 :   /*
      51                 :    * If the validation caching interface is available and it would be
      52                 :    * inexpensive to do so, perform a query.
      53                 :    */
      54            2463 :   if (cache != NULL && NaClCachingIsInexpensive(cache, metadata))
      55              11 :     query = cache->CreateQuery(cache->handle);
      56            2463 :   if (query != NULL) {
      57              11 :     const char validator_id[] = "x86-32 dfa";
      58              11 :     cache->AddData(query, (uint8_t *) validator_id, sizeof(validator_id));
      59              11 :     cache->AddData(query, (uint8_t *) cpu_features, sizeof(*cpu_features));
      60              11 :     NaClAddCodeIdentity(data, size, metadata, cache, query);
      61              11 :     if (cache->QueryKnownToValidate(query)) {
      62               3 :       cache->DestroyQuery(query);
      63               3 :       return NaClValidationSucceeded;
      64                 :     }
      65                 :   }
      66                 : 
      67            2460 :   if (readonly_text) {
      68               8 :     if (ValidateChunkIA32(data, size, 0 /*options*/, cpu_features,
      69                 :                           NaClDfaProcessValidationError,
      70                 :                           NULL))
      71               6 :       status = NaClValidationSucceeded;
      72                 :   } else {
      73            2452 :     if (ValidateChunkIA32(data, size, 0 /*options*/, cpu_features,
      74                 :                           NaClDfaStubOutCPUUnsupportedInstruction,
      75                 :                           &did_stubout))
      76            2411 :       status = NaClValidationSucceeded;
      77                 :   }
      78            2460 :   if (status != NaClValidationSucceeded && errno == ENOMEM)
      79               0 :     status = NaClValidationFailedOutOfMemory;
      80                 : 
      81                 :   /* Cache the result if validation succeeded and the code was not modified. */
      82            2460 :   if (query != NULL) {
      83               8 :     if (status == NaClValidationSucceeded && did_stubout == 0)
      84               6 :       cache->SetKnownToValidate(query);
      85               8 :     cache->DestroyQuery(query);
      86                 :   }
      87                 : 
      88            2460 :   return status;
      89                 : }
      90                 : 
      91                 : 
      92               7 : static NaClValidationStatus ValidatorCopy_x86_32(
      93                 :     uintptr_t guest_addr,
      94                 :     uint8_t *data_existing,
      95                 :     uint8_t *data_new,
      96                 :     size_t size,
      97                 :     const NaClCPUFeatures *f,
      98                 :     NaClCopyInstructionFunc copy_func) {
      99                 :   /* TODO(jfb) Use a safe cast here. */
     100               7 :   NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
     101                 :   struct CodeCopyCallbackData callback_data;
     102                 :   UNREFERENCED_PARAMETER(guest_addr);
     103                 : 
     104               7 :   if (size & kBundleMask)
     105               0 :     return NaClValidationFailed;
     106               7 :   callback_data.copy_func = copy_func;
     107               7 :   callback_data.existing_minus_new = data_existing - data_new;
     108               7 :   if (ValidateChunkIA32(data_new, size, CALL_USER_CALLBACK_ON_EACH_INSTRUCTION,
     109                 :                         cpu_features, NaClDfaProcessCodeCopyInstruction,
     110                 :                         &callback_data))
     111               7 :     return NaClValidationSucceeded;
     112               0 :   if (errno == ENOMEM)
     113               0 :     return NaClValidationFailedOutOfMemory;
     114               0 :   return NaClValidationFailed;
     115                 : }
     116                 : 
     117                 : /* This structure is used by callbacks ProcessCodeReplacementInstruction
     118                 :    and ProcessOriginalCodeInstruction during code replacement validation
     119                 :    in ValidatorCodeReplacement_x86_32.  */
     120                 : struct CodeReplacementCallbackData {
     121                 :   /* Bitmap with boundaries of the instructions in the old code bundle.  */
     122                 :   bitmap_word instruction_boundaries_existing;
     123                 :   /* Bitmap with boundaries of the instructions in the new code bundle.  */
     124                 :   bitmap_word instruction_boundaries_new;
     125                 :   /* cpu_features - needed for the call to ValidateChunkIA32.  */
     126                 :   const NaClCPUFeaturesX86 *cpu_features;
     127                 :   /* Pointer to the start of the current bundle in the old code.  */
     128                 :   const uint8_t *bundle_existing;
     129                 :   /* Pointer to the start of the new code.  */
     130                 :   const uint8_t *data_new;
     131                 :   /* Difference between addresses: data_existing - data_new. This is needed for
     132                 :      fast comparison between existing and new code.  */
     133                 :   ptrdiff_t existing_minus_new;
     134                 : };
     135                 : 
     136             383 : static Bool ProcessOriginalCodeInstruction(const uint8_t *begin_existing,
     137                 :                                            const uint8_t *end_existing,
     138                 :                                            uint32_t info_existing,
     139                 :                                            void *callback_data) {
     140             383 :   struct CodeReplacementCallbackData *data = callback_data;
     141             383 :   size_t instruction_length = end_existing - begin_existing;
     142             383 :   const uint8_t *begin_new = begin_existing - data->existing_minus_new;
     143                 : 
     144                 :   /* Sanity check: instruction must be no longer than 17 bytes.  */
     145             383 :   CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH);
     146                 : 
     147                 :   /* Sanity check: old code must be valid... except for jumps.  */
     148             383 :   CHECK(!(info_existing &
     149                 :           (VALIDATION_ERRORS_MASK & ~DIRECT_JUMP_OUT_OF_RANGE)));
     150                 : 
     151                 :   /*
     152                 :    * Return failure if code replacement attempts to delete or modify a special
     153                 :    * instruction such as naclcall or nacljmp.
     154                 :    */
     155             391 :   if ((info_existing & SPECIAL_INSTRUCTION) &&
     156               8 :       memcmp(begin_new, begin_existing, instruction_length) != 0)
     157               4 :     return FALSE;
     158                 : 
     159             379 :   BitmapSetBit(&data->instruction_boundaries_existing,
     160             379 :                begin_existing - data->bundle_existing);
     161                 : 
     162             379 :   return TRUE;
     163                 : }
     164                 : 
     165             424 : static INLINE Bool ProcessCodeReplacementInstructionInfoFlags(
     166                 :     const uint8_t *begin_existing,
     167                 :     const uint8_t *begin_new,
     168                 :     size_t instruction_length,
     169                 :     uint32_t info_new,
     170                 :     struct CodeReplacementCallbackData *data) {
     171                 : 
     172                 :   /* Unsupported instruction must have been replaced with HLTs.  */
     173             424 :   if ((info_new & VALIDATION_ERRORS_MASK) == CPUID_UNSUPPORTED_INSTRUCTION) {
     174                 :     /*
     175                 :      * The new instruction will be replaced with a bunch of HLTs by
     176                 :      * ValidatorCopy_x86_32 and they are single-byte instructions.
     177                 :      * Mark all the bytes as instruction boundaries.
     178                 :      */
     179               0 :     if (NaClDfaCodeReplacementIsStubouted(begin_existing, instruction_length)) {
     180               0 :       BitmapSetBits(&data->instruction_boundaries_new,
     181               0 :                     (begin_new - data->data_new) & kBundleMask,
     182                 :                     instruction_length);
     183               0 :       return TRUE;
     184                 :     }
     185               0 :     return FALSE;
     186                 :   }
     187                 : 
     188                 :   /* If we have jump which jumps out of its range...  */
     189             424 :   if (info_new & DIRECT_JUMP_OUT_OF_RANGE) {
     190                 :     /* then everything is fine if it's the only error and jump is unchanged!  */
     191               4 :     if ((info_new & VALIDATION_ERRORS_MASK) == DIRECT_JUMP_OUT_OF_RANGE &&
     192               2 :         memcmp(begin_new, begin_existing, instruction_length) == 0)
     193               1 :       return TRUE;
     194               1 :     return FALSE;
     195                 :   }
     196                 : 
     197                 :   /* If instruction is not accepted then we have nothing to do here.  */
     198             422 :   if (info_new & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET))
     199               3 :     return FALSE;
     200                 : 
     201                 :   /* Instruction is untouched: we are done.  */
     202             419 :   if (memcmp(begin_new, begin_existing, instruction_length) == 0)
     203             376 :     return TRUE;
     204                 : 
     205                 :   /*
     206                 :    * Return failure if code replacement attempts to create or modify a special
     207                 :    * instruction such as naclcall or nacljmp.
     208                 :    */
     209              43 :   if (info_new & SPECIAL_INSTRUCTION)
     210               3 :     return FALSE;
     211                 : 
     212                 :   /* No problems found.  */
     213              40 :   return TRUE;
     214                 : }
     215                 : 
     216                 : 
     217             424 : static Bool ProcessCodeReplacementInstruction(const uint8_t *begin_new,
     218                 :                                               const uint8_t *end_new,
     219                 :                                               uint32_t info_new,
     220                 :                                               void *callback_data) {
     221             424 :   struct CodeReplacementCallbackData *data = callback_data;
     222             424 :   size_t instruction_length = end_new - begin_new;
     223             424 :   const uint8_t *begin_existing = begin_new + data->existing_minus_new;
     224                 : 
     225                 :   /* Sanity check: instruction must be no longer than 17 bytes.  */
     226             424 :   CHECK(instruction_length <= MAX_INSTRUCTION_LENGTH);
     227                 : 
     228             424 :   if (!ProcessCodeReplacementInstructionInfoFlags(begin_existing, begin_new,
     229                 :                                                  instruction_length,
     230                 :                                                  info_new, data))
     231               7 :     return FALSE;
     232                 : 
     233                 :   /* We mark the end of the instruction as valid jump target here.  */
     234             417 :   BitmapSetBit(&data->instruction_boundaries_new,
     235             417 :                (begin_new - data->data_new) & kBundleMask);
     236                 : 
     237                 :   /* If we at the end of a bundle then check boundaries.  */
     238             417 :   if (((end_new - data->data_new) & kBundleMask) == 0) {
     239                 :     Bool rc;
     240              16 :     data->bundle_existing = end_new - kBundleSize + data->existing_minus_new;
     241                 : 
     242                 :     /* Check existing code and mark its instruction boundaries.  */
     243              16 :     rc = ValidateChunkIA32(data->bundle_existing, kBundleSize,
     244                 :                            CALL_USER_CALLBACK_ON_EACH_INSTRUCTION,
     245                 :                            data->cpu_features, ProcessOriginalCodeInstruction,
     246                 :                            callback_data);
     247                 :     /* Attempt to delete superinstruction is detected.  */
     248              16 :     if (!rc)
     249               4 :       return FALSE;
     250                 : 
     251              24 :     if (data->instruction_boundaries_existing !=
     252              12 :         data->instruction_boundaries_new)
     253               3 :       return FALSE;
     254                 :     /* Mark all boundaries in the next bundle invalid.  */
     255               9 :     data->instruction_boundaries_existing = 0;
     256               9 :     data->instruction_boundaries_new = 0;
     257                 :   }
     258                 : 
     259             410 :   return TRUE;
     260                 : }
     261                 : 
     262              19 : static NaClValidationStatus ValidatorCodeReplacement_x86_32(
     263                 :     uintptr_t guest_addr,
     264                 :     uint8_t *data_existing,
     265                 :     uint8_t *data_new,
     266                 :     size_t size,
     267                 :     const NaClCPUFeatures *f) {
     268                 :   /* TODO(jfb) Use a safe cast here. */
     269              19 :   NaClCPUFeaturesX86 *cpu_features = (NaClCPUFeaturesX86 *) f;
     270                 :   struct CodeReplacementCallbackData callback_data;
     271                 :   UNREFERENCED_PARAMETER(guest_addr);
     272                 : 
     273              19 :   if (size & kBundleMask)
     274               0 :     return NaClValidationFailed;
     275                 :   /* Mark all boundaries in the bundle invalid.  */
     276              19 :   callback_data.instruction_boundaries_existing = 0;
     277              19 :   callback_data.instruction_boundaries_new = 0;
     278              19 :   callback_data.cpu_features = cpu_features;
     279                 :   /* Note: bundle_existing is used when we call second validator.  */
     280              19 :   callback_data.data_new = data_new;
     281              19 :   callback_data.existing_minus_new = data_existing - data_new;
     282              19 :   if (ValidateChunkIA32(data_new, size, CALL_USER_CALLBACK_ON_EACH_INSTRUCTION,
     283                 :                         cpu_features, ProcessCodeReplacementInstruction,
     284                 :                         &callback_data))
     285               7 :     return NaClValidationSucceeded;
     286              12 :   if (errno == ENOMEM)
     287               0 :     return NaClValidationFailedOutOfMemory;
     288              12 :   return NaClValidationFailed;
     289                 : }
     290                 : 
     291                 : static const struct NaClValidatorInterface validator = {
     292                 :   FALSE, /* Optional stubout_mode is not implemented.            */
     293                 :   TRUE,  /* Optional readonly_text mode is implemented.          */
     294                 :   TRUE,  /* Optional code replacement functions are implemented. */
     295                 :   ApplyDfaValidator_x86_32,
     296                 :   ValidatorCopy_x86_32,
     297                 :   ValidatorCodeReplacement_x86_32,
     298                 :   sizeof(NaClCPUFeaturesX86),
     299                 :   NaClSetAllCPUFeaturesX86,
     300                 :   NaClGetCurrentCPUFeaturesX86,
     301                 :   NaClFixCPUFeaturesX86,
     302                 : };
     303                 : 
     304             321 : const struct NaClValidatorInterface *NaClDfaValidatorCreate_x86_32(void) {
     305             321 :   return &validator;
     306                 : }

Generated by: LCOV version 1.7