LCOV - code coverage report
Current view: directory - src/trusted/validator_x86 - nccopycode.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 89 0 0.0 %
Date: 2012-02-16 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                 : /*
       8                 :  * nccopycode.c
       9                 :  * Copies two code streams in a thread-safe way
      10                 :  *
      11                 :  */
      12                 : 
      13                 : #include "native_client/src/include/portability.h"
      14                 : 
      15                 : #if NACL_WINDOWS == 1
      16                 : #include <windows.h>
      17                 : #else
      18                 : #include <sys/mman.h>
      19                 : #endif
      20                 : 
      21                 : #include <stdio.h>
      22                 : #include <stdlib.h>
      23                 : #include <errno.h>
      24                 : #include <string.h>
      25                 : #include <assert.h>
      26                 : #include "native_client/src/include/nacl_platform.h"
      27                 : #include "native_client/src/shared/platform/nacl_check.h"
      28                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      29                 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
      30                 : #include "native_client/src/trusted/validator/ncvalidate.h"
      31                 : 
      32                 : #if NACL_ARCH(NACL_TARGET_ARCH) == NACL_x86
      33                 : # if NACL_TARGET_SUBARCH == 32
      34                 : #  include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode.h"
      35                 : #  include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h"
      36                 : # elif NACL_TARGET_SUBARCH == 64
      37                 : #  include "native_client/src/trusted/validator/x86/decoder/nc_inst_iter.h"
      38                 : #  include "native_client/src/trusted/validator/x86/decoder/nc_inst_state_internal.h"
      39                 : #  include "native_client/src/trusted/validator/x86/nacl_cpuid.h"
      40                 : #  include "native_client/src/trusted/validator/x86/ncval_reg_sfi/ncval_decode_tables.h"
      41                 : #  include "native_client/src/trusted/validator/x86/nc_segment.h"
      42                 : # else
      43                 : #  error "Unknown Platform"
      44                 : # endif
      45                 : #else
      46                 : # error "Unknown Platform"
      47                 : #endif
      48                 : 
      49                 : /* x86 HALT opcode */
      50                 : static const uint8_t kNaClFullStop = 0xf4;
      51                 : 
      52                 : /*
      53                 :  * Max size of aligned writes we may issue to code without syncing.
      54                 :  * 8 is a safe value according to:
      55                 :  *   [1] Advance Micro Devices Inc. AMD64 Architecture Program-
      56                 :  *   mers Manual Volume 1: Application Programming, 2009.
      57                 :  *   [2] Intel Inc. Intel 64 and IA-32 Architectures Software Developers
      58                 :  *   Manual Volume 3A: System Programming Guide, Part 1, 2010.
      59                 :  *   [3] Vijay Sundaresan, Daryl Maier, Pramod Ramarao, and Mark
      60                 :  *   Stoodley. Experiences with multi-threading and dynamic class
      61                 :  *   loading in a java just-in-time compiler. Code Generation and
      62                 :  *   Optimization, IEEE/ACM International Symposium on, 0:87–
      63                 :  *   97, 2006.
      64                 :  */
      65                 : static const int kTrustAligned = 8;
      66                 : 
      67                 : /*
      68                 :  * Max size of unaligned writes we may issue to code.
      69                 :  * Empirically this may be larger, however no docs to support it.
      70                 :  * 1 means disabled.
      71                 :  */
      72                 : static const int kTrustUnaligned = 1;
      73                 : 
      74                 : /*
      75                 :  * Boundary no write may ever cross.
      76                 :  * On AMD machines must be leq 8.  Intel machines leq cache line.
      77                 :  */
      78                 : static const int kInstructionFetchSize = 8;
      79                 : 
      80                 : /* defined in nccopycode_stores.S */
      81                 : void _cdecl onestore_memmove4(uint8_t* dst, uint8_t* src);
      82                 : 
      83                 : /* defined in nccopycode_stores.S */
      84                 : void _cdecl onestore_memmove8(uint8_t* dst, uint8_t* src);
      85                 : 
      86               0 : static INLINE int IsAligned(uint8_t *dst, int size, int align) {
      87               0 :   uintptr_t startaligned = ((uintptr_t)dst)        & ~(align-1);
      88               0 :   uintptr_t stopaligned  = ((uintptr_t)dst+size-1) & ~(align-1);
      89               0 :   return startaligned == stopaligned;
      90                 : }
      91                 : 
      92                 : /*
      93                 :  * Test if it is safe to issue a unsynced change to dst/size using a
      94                 :  * writesize write.  Outputs the offset to start the write at.
      95                 :  * 1 if it is ok, 0 if it is not ok.
      96                 :  */
      97                 : static int IsTrustedWrite(uint8_t *dst,
      98                 :                           int size,
      99                 :                           int writesize,
     100               0 :                           intptr_t* offset) {
     101               0 :   if (size > writesize) {
     102               0 :     return 0;
     103                 :   }
     104               0 :   if (!IsAligned(dst, size, kInstructionFetchSize)) {
     105               0 :     return 0;
     106                 :   }
     107               0 :   if (writesize <= kTrustAligned && IsAligned(dst, size, writesize)) {
     108                 :     /* aligned write is trusted */
     109               0 :     *offset = (intptr_t) dst & (writesize - 1);
     110               0 :     return 1;
     111                 :   }
     112               0 :   if (writesize <= kTrustUnaligned) {
     113                 :     /* unaligned write is trusted */
     114               0 :     *offset = 0;
     115               0 :     return 1;
     116                 :   }
     117               0 :   return 0;
     118                 : }
     119                 : 
     120                 : /* this is global to prevent a (very smart) compiler from optimizing it out */
     121                 : void* g_squashybuffer = NULL;
     122                 : char g_firstbyte = 0;
     123                 : 
     124               0 : static Bool SerializeAllProcessors() {
     125                 :   /*
     126                 :    * We rely on the OS mprotect() call to issue interprocessor interrupts,
     127                 :    * which will cause other processors to execute an IRET, which is
     128                 :    * serializing.
     129                 :    *
     130                 :    * This code is based on two main considerations:
     131                 :    * 1. Only switching the page from exec to non-exec state is guaranteed
     132                 :    * to invalidate processors' instruction caches.
     133                 :    * 2. It's bad to have a page that is both writeable and executable,
     134                 :    * even if that happens not simultaneously.
     135                 :    */
     136                 : 
     137               0 :   int size = NACL_MAP_PAGESIZE;
     138               0 :   if (NULL == g_squashybuffer) {
     139               0 :     if ((0 != NaCl_page_alloc(&g_squashybuffer, size)) ||
     140                 :         (0 != NaCl_mprotect(g_squashybuffer, size, PROT_READ|PROT_WRITE))) {
     141               0 :       NaClLog(0,
     142                 :               ("SerializeAllProcessors: initial squashybuffer allocation"
     143                 :                " failed\n"));
     144               0 :       return FALSE;
     145                 :     }
     146                 : 
     147               0 :     NaClFillMemoryRegionWithHalt(g_squashybuffer, size);
     148               0 :     g_firstbyte = *(char *) g_squashybuffer;
     149               0 :     NaClLog(0, "SerializeAllProcessors: g_firstbyte is %d\n", g_firstbyte);
     150                 :   }
     151                 : 
     152               0 :   if ((0 != NaCl_mprotect(g_squashybuffer, size, PROT_READ|PROT_EXEC))) {
     153               0 :     NaClLog(0,
     154                 :             ("SerializeAllProcessors: interprocessor interrupt"
     155                 :              " generation failed: could not reverse shield polarity (1)\n"));
     156               0 :     return FALSE;
     157                 :   }
     158                 :   /*
     159                 :    * Make a read to ensure that the potential kernel laziness
     160                 :    * would not affect this hack.
     161                 :    */
     162               0 :   if (*(char *) g_squashybuffer != g_firstbyte) {
     163               0 :     NaClLog(0,
     164                 :             ("SerializeAllProcessors: interprocessor interrupt"
     165                 :              " generation failed: could not reverse shield polarity (2)\n"));
     166               0 :     NaClLog(0, "SerializeAllProcessors: g_firstbyte is %d\n", g_firstbyte);
     167               0 :     NaClLog(0, "SerializeAllProcessors: *g_squashybuffer is %d\n",
     168                 :             *(char *) g_squashybuffer);
     169               0 :     return FALSE;
     170                 :   }
     171                 :   /*
     172                 :    * We would like to set the protection to PROT_NONE, but on Windows
     173                 :    * there's an ugly hack in NaCl_mprotect where PROT_NONE can result
     174                 :    * in MEM_DECOMMIT, causing the contents of the page(s) to be lost!
     175                 :    */
     176               0 :   if (0 != NaCl_mprotect(g_squashybuffer, size, PROT_READ)) {
     177               0 :     NaClLog(0,
     178                 :             ("SerializeAllProcessors: interprocessor interrupt"
     179                 :              " generation failed: could not reverse shield polarity (3)\n"));
     180               0 :     return FALSE;
     181                 :   }
     182               0 :   return TRUE;
     183                 : }
     184                 : 
     185                 : /*
     186                 :  * Copy a single instruction, avoiding the possibility of other threads
     187                 :  * executing a partially changed instruction.
     188                 :  */
     189                 : static Bool CopyInstructionInternal(uint8_t *dst,
     190                 :                                     uint8_t *src,
     191               0 :                                     uint8_t sz) {
     192               0 :   intptr_t offset = 0;
     193               0 :   uint8_t *firstbyte_p = dst;
     194                 : 
     195               0 :   while (sz > 0 && dst[0] == src[0]) {
     196                 :     /* scroll to first changed byte */
     197               0 :     dst++, src++, sz--;
     198                 :   }
     199                 : 
     200               0 :   if (sz == 0) {
     201                 :     /* instructions are identical, we are done */
     202               0 :     return TRUE;
     203                 :   }
     204                 : 
     205               0 :   while (sz > 0 && dst[sz-1] == src[sz-1]) {
     206                 :     /* trim identical bytes at end */
     207               0 :     sz--;
     208                 :   }
     209                 : 
     210               0 :   if (sz == 1) {
     211                 :     /* we assume a 1-byte change is atomic */
     212               0 :     *dst = *src;
     213               0 :   } else if (IsTrustedWrite(dst, sz, 4, &offset)) {
     214                 :     uint8_t tmp[4];
     215               0 :     memcpy(tmp, dst-offset, sizeof tmp);
     216               0 :     memcpy(tmp+offset, src, sz);
     217               0 :     onestore_memmove4(dst-offset, tmp);
     218               0 :   } else if (IsTrustedWrite(dst, sz, 8, &offset)) {
     219                 :     uint8_t tmp[8];
     220               0 :     memcpy(tmp, dst-offset, sizeof tmp);
     221               0 :     memcpy(tmp+offset, src, sz);
     222               0 :     onestore_memmove8(dst-offset, tmp);
     223                 :   } else {
     224                 :     /* the slow path, first flip first byte to halt*/
     225               0 :     uint8_t firstbyte = firstbyte_p[0];
     226               0 :     firstbyte_p[0] = kNaClFullStop;
     227                 : 
     228               0 :     if (!SerializeAllProcessors()) return FALSE;
     229                 : 
     230                 :     /* copy the rest of instruction */
     231               0 :     if (dst == firstbyte_p) {
     232                 :       /* but not the first byte! */
     233               0 :       firstbyte = *src;
     234               0 :       dst++, src++, sz--;
     235                 :     }
     236               0 :     memcpy(dst, src, sz);
     237                 : 
     238               0 :     if (!SerializeAllProcessors()) return FALSE;
     239                 : 
     240                 :     /* flip first byte back */
     241               0 :     firstbyte_p[0] = firstbyte;
     242                 :   }
     243               0 :   return TRUE;
     244                 : }
     245                 : 
     246                 : #if NACL_TARGET_SUBARCH == 32
     247                 : 
     248                 : /*
     249                 :  * Copy a single instruction, avoiding the possibility of other threads
     250                 :  * executing a partially changed instruction.
     251                 :  */
     252                 : static Bool CopyInstruction(NCDecoderStatePair* tthis,
     253                 :                             NCDecoderInst *dinst_old,
     254               0 :                             NCDecoderInst *dinst_new) {
     255               0 :   NCRemainingMemory* mem_old = &dinst_old->dstate->memory;
     256               0 :   NCRemainingMemory* mem_new = &dinst_new->dstate->memory;
     257                 : 
     258               0 :   return CopyInstructionInternal(mem_old->mpc,
     259                 :                                  mem_new->mpc,
     260                 :                                  mem_old->read_length);
     261                 : }
     262                 : 
     263                 : int NCCopyCode(uint8_t *dst, uint8_t *src, NaClPcAddress vbase,
     264               0 :                size_t sz, int bundle_size) {
     265                 :   NCDecoderState dst_dstate;
     266                 :   NCDecoderInst  dst_inst;
     267                 :   NCDecoderState src_dstate;
     268                 :   NCDecoderInst  src_inst;
     269                 :   NCDecoderStatePair pair;
     270               0 :   int result = 0;
     271                 : 
     272               0 :   NCDecoderStateConstruct(&dst_dstate, dst, vbase, sz, &dst_inst, 1);
     273               0 :   NCDecoderStateConstruct(&src_dstate, src, vbase, sz, &src_inst, 1);
     274               0 :   NCDecoderStatePairConstruct(&pair, &dst_dstate, &src_dstate);
     275               0 :   pair.action_fn = CopyInstruction;
     276               0 :   if (NCDecoderStatePairDecode(&pair)) result = 1;
     277               0 :   NCDecoderStatePairDestruct(&pair);
     278               0 :   NCDecoderStateDestruct(&src_dstate);
     279               0 :   NCDecoderStateDestruct(&dst_dstate);
     280                 : 
     281               0 :   return result;
     282                 : }
     283                 : 
     284                 : NaClValidationStatus NACL_SUBARCH_NAME(ApplyValidatorCopy,
     285                 :                                        NACL_TARGET_ARCH,
     286                 :                                        NACL_TARGET_SUBARCH)
     287                 :     (enum NaClSBKind sb_kind,
     288                 :      uintptr_t guest_addr,
     289                 :      uint8_t *data_old,
     290                 :      uint8_t *data_new,
     291                 :      size_t size,
     292                 :      int bundle_size,
     293               0 :      NaClCPUFeaturesX86 *cpu_features) {
     294               0 :   NaClValidationStatus status = NaClValidationFailedNotImplemented;
     295               0 :   assert(NACL_SB_DEFAULT == sb_kind);
     296               0 :   if (bundle_size == 16 || bundle_size == 32) {
     297               0 :     if (!NaClArchSupported(cpu_features)) {
     298               0 :       status = NaClValidationFailedCpuNotSupported;
     299                 :     } else {
     300               0 :       status = ((0 == NCCopyCode(data_old, data_new, guest_addr,
     301                 :                                 size, bundle_size))
     302                 :                 ? NaClValidationFailed : NaClValidationSucceeded);
     303                 :     }
     304                 :   }
     305               0 :   return status;
     306                 : }
     307                 : 
     308                 : #elif NACL_TARGET_SUBARCH == 64
     309                 : 
     310                 : int NaClCopyCodeIter(uint8_t *dst, uint8_t *src,
     311                 :                      NaClPcAddress vbase, size_t size) {
     312                 :   NaClSegment segment_old, segment_new;
     313                 :   NaClInstIter *iter_old, *iter_new;
     314                 :   NaClInstState *istate_old, *istate_new;
     315                 :   int still_good = 1;
     316                 : 
     317                 :   NaClSegmentInitialize(dst, vbase, size, &segment_old);
     318                 :   NaClSegmentInitialize(src, vbase, size, &segment_new);
     319                 : 
     320                 :   iter_old = NaClInstIterCreate(kNaClValDecoderTables, &segment_old);
     321                 :   if (NULL == iter_old) return 0;
     322                 :   iter_new = NaClInstIterCreate(kNaClValDecoderTables, &segment_new);
     323                 :   if (NULL == iter_new) {
     324                 :     NaClInstIterDestroy(iter_old);
     325                 :     return 0;
     326                 :   }
     327                 :   while (1) {
     328                 :     /* March over every instruction, which means NaCl pseudo-instructions are
     329                 :      * treated as multiple instructions.  Checks in NaClValidateCodeReplacement
     330                 :      * guarantee that only valid replacements will happen, and no pseudo-
     331                 :      * instructions should be touched.
     332                 :      */
     333                 :     if (!(NaClInstIterHasNext(iter_old) && NaClInstIterHasNext(iter_new))) {
     334                 :       if (NaClInstIterHasNext(iter_old) || NaClInstIterHasNext(iter_new)) {
     335                 :         NaClLog(LOG_ERROR,
     336                 :                 "Segment replacement: copy failed: iterators "
     337                 :                 "length mismatch\n");
     338                 :         still_good = 0;
     339                 :       }
     340                 :       break;
     341                 :     }
     342                 :     istate_old = NaClInstIterGetState(iter_old);
     343                 :     istate_new = NaClInstIterGetState(iter_new);
     344                 :     if (istate_old->bytes.length != istate_new->bytes.length ||
     345                 :         iter_old->memory.read_length != iter_new->memory.read_length ||
     346                 :         istate_new->inst_addr != istate_old->inst_addr) {
     347                 :       /* Sanity check: this should never happen based on checks in
     348                 :        * NaClValidateInstReplacement.
     349                 :        */
     350                 :       NaClLog(LOG_ERROR,
     351                 :               "Segment replacement: copied instructions misaligned\n");
     352                 :       still_good = 0;
     353                 :       break;
     354                 :     }
     355                 :     /* Replacing all modified instructions at once could yield a speedup here
     356                 :      * as every time we modify instructions we must serialize all processors
     357                 :      * twice.  Re-evaluate if code modification performance is an issue.
     358                 :      */
     359                 :     if (!CopyInstructionInternal(iter_old->memory.mpc,
     360                 :                                  iter_new->memory.mpc,
     361                 :                                  iter_old->memory.read_length)) {
     362                 :       NaClLog(LOG_ERROR,
     363                 :               "Segment replacement: copy failed: unable to copy instruction\n");
     364                 :       still_good = 0;
     365                 :       break;
     366                 :     }
     367                 :     NaClInstIterAdvance(iter_old);
     368                 :     NaClInstIterAdvance(iter_new);
     369                 :   }
     370                 : 
     371                 :   NaClInstIterDestroy(iter_old);
     372                 :   NaClInstIterDestroy(iter_new);
     373                 :   return still_good;
     374                 : }
     375                 : 
     376                 : NaClValidationStatus NACL_SUBARCH_NAME(ApplyValidatorCopy,
     377                 :                                        NACL_TARGET_ARCH,
     378                 :                                        NACL_TARGET_SUBARCH)
     379                 :     (enum NaClSBKind sb_kind,
     380                 :      uintptr_t guest_addr,
     381                 :      uint8_t *data_old,
     382                 :      uint8_t *data_new,
     383                 :      size_t size,
     384                 :      int bundle_size,
     385                 :      NaClCPUFeaturesX86 *cpu_features) {
     386                 :   NaClValidationStatus status = NaClValidationFailedNotImplemented;
     387                 :   assert(NACL_SB_DEFAULT == sb_kind);
     388                 :   if (bundle_size == 16 || bundle_size == 32) {
     389                 :     if (!NaClArchSupported(cpu_features)) {
     390                 :       status = NaClValidationFailedCpuNotSupported;
     391                 :     } else {
     392                 :       status = ((0 == NaClCopyCodeIter(data_old, data_new, guest_addr, size))
     393                 :                 ? NaClValidationFailed : NaClValidationSucceeded);
     394                 :     }
     395                 :   }
     396                 :   return status;
     397                 : }
     398                 : 
     399                 : #endif

Generated by: LCOV version 1.7