LCOV - code coverage report
Current view: directory - src/trusted/validator_x86 - nccopycode.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 79 63 79.7 %
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                 : /*
       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                 : 
      27                 : #include "native_client/src/include/nacl_platform.h"
      28                 : #include "native_client/src/shared/platform/nacl_check.h"
      29                 : #include "native_client/src/shared/utils/types.h"
      30                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      31                 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
      32                 : #include "native_client/src/trusted/validator/ncvalidate.h"
      33                 : 
      34                 : /* x86 HALT opcode */
      35                 : static const uint8_t kNaClFullStop = 0xf4;
      36                 : 
      37                 : /*
      38                 :  * Max size of aligned writes we may issue to code without syncing.
      39                 :  * 8 is a safe value according to:
      40                 :  *   [1] Advance Micro Devices Inc. AMD64 Architecture Program-
      41                 :  *   mers Manual Volume 1: Application Programming, 2009.
      42                 :  *   [2] Intel Inc. Intel 64 and IA-32 Architectures Software Developers
      43                 :  *   Manual Volume 3A: System Programming Guide, Part 1, 2010.
      44                 :  *   [3] Vijay Sundaresan, Daryl Maier, Pramod Ramarao, and Mark
      45                 :  *   Stoodley. Experiences with multi-threading and dynamic class
      46                 :  *   loading in a java just-in-time compiler. Code Generation and
      47                 :  *   Optimization, IEEE/ACM International Symposium on, 0:87–
      48                 :  *   97, 2006.
      49                 :  */
      50                 : static const int kTrustAligned = 8;
      51                 : 
      52                 : /*
      53                 :  * Max size of unaligned writes we may issue to code.
      54                 :  * Empirically this may be larger, however no docs to support it.
      55                 :  * 1 means disabled.
      56                 :  */
      57                 : static const int kTrustUnaligned = 1;
      58                 : 
      59                 : /*
      60                 :  * Boundary no write may ever cross.
      61                 :  * On AMD machines must be leq 8.  Intel machines leq cache line.
      62                 :  */
      63                 : static const int kInstructionFetchSize = 8;
      64                 : 
      65                 : /* defined in nccopycode_stores.S */
      66                 : void _cdecl onestore_memmove4(uint8_t* dst, uint8_t* src);
      67                 : 
      68                 : /* defined in nccopycode_stores.S */
      69                 : void _cdecl onestore_memmove8(uint8_t* dst, uint8_t* src);
      70                 : 
      71              16 : static INLINE int IsAligned(uint8_t *dst, int size, int align) {
      72              16 :   uintptr_t startaligned = ((uintptr_t)dst)        & ~(align-1);
      73              16 :   uintptr_t stopaligned  = ((uintptr_t)dst+size-1) & ~(align-1);
      74              16 :   return startaligned == stopaligned;
      75                 : }
      76                 : 
      77                 : /*
      78                 :  * Test if it is safe to issue a unsynced change to dst/size using a
      79                 :  * writesize write.  Outputs the offset to start the write at.
      80                 :  * 1 if it is ok, 0 if it is not ok.
      81                 :  */
      82              10 : static int IsTrustedWrite(uint8_t *dst,
      83              10 :                           int size,
      84              10 :                           int writesize,
      85              10 :                           intptr_t* offset) {
      86              10 :   if (size > writesize) {
      87               0 :     return 0;
      88                 :   }
      89              10 :   if (!IsAligned(dst, size, kInstructionFetchSize)) {
      90               4 :     return 0;
      91                 :   }
      92              12 :   if (writesize <= kTrustAligned && IsAligned(dst, size, writesize)) {
      93                 :     /* aligned write is trusted */
      94               4 :     *offset = (intptr_t) dst & (writesize - 1);
      95               4 :     return 1;
      96                 :   }
      97               2 :   if (writesize <= kTrustUnaligned) {
      98                 :     /* unaligned write is trusted */
      99               0 :     *offset = 0;
     100               0 :     return 1;
     101                 :   }
     102               2 :   return 0;
     103              10 : }
     104                 : 
     105                 : /* this is global to prevent a (very smart) compiler from optimizing it out */
     106                 : void* g_squashybuffer = NULL;
     107                 : char g_firstbyte = 0;
     108                 : 
     109                 : static Bool SerializeAllProcessors(void) {
     110                 :   /*
     111                 :    * We rely on the OS mprotect() call to issue interprocessor interrupts,
     112                 :    * which will cause other processors to execute an IRET, which is
     113                 :    * serializing.
     114                 :    *
     115                 :    * This code is based on two main considerations:
     116                 :    * 1. Only switching the page from exec to non-exec state is guaranteed
     117                 :    * to invalidate processors' instruction caches.
     118                 :    * 2. It's bad to have a page that is both writeable and executable,
     119                 :    * even if that happens not simultaneously.
     120                 :    */
     121                 : 
     122               8 :   int size = NACL_MAP_PAGESIZE;
     123               4 :   if (NULL == g_squashybuffer) {
     124               1 :     if ((0 != NaClPageAlloc(&g_squashybuffer, size)) ||
     125               1 :         (0 != NaClMprotect(g_squashybuffer, size, PROT_READ|PROT_WRITE))) {
     126               0 :       NaClLog(0,
     127                 :               ("SerializeAllProcessors: initial squashybuffer allocation"
     128                 :                " failed\n"));
     129               0 :       return FALSE;
     130                 :     }
     131                 : 
     132               1 :     NaClFillMemoryRegionWithHalt(g_squashybuffer, size);
     133               1 :     g_firstbyte = *(char *) g_squashybuffer;
     134               1 :     NaClLog(0, "SerializeAllProcessors: g_firstbyte is %d\n", g_firstbyte);
     135               1 :   }
     136                 : 
     137               4 :   if ((0 != NaClMprotect(g_squashybuffer, size, PROT_READ|PROT_EXEC))) {
     138               0 :     NaClLog(0,
     139                 :             ("SerializeAllProcessors: interprocessor interrupt"
     140                 :              " generation failed: could not reverse shield polarity (1)\n"));
     141               0 :     return FALSE;
     142                 :   }
     143                 :   /*
     144                 :    * Make a read to ensure that the potential kernel laziness
     145                 :    * would not affect this hack.
     146                 :    */
     147               4 :   if (*(char *) g_squashybuffer != g_firstbyte) {
     148               0 :     NaClLog(0,
     149                 :             ("SerializeAllProcessors: interprocessor interrupt"
     150                 :              " generation failed: could not reverse shield polarity (2)\n"));
     151               0 :     NaClLog(0, "SerializeAllProcessors: g_firstbyte is %d\n", g_firstbyte);
     152               0 :     NaClLog(0, "SerializeAllProcessors: *g_squashybuffer is %d\n",
     153                 :             *(char *) g_squashybuffer);
     154               0 :     return FALSE;
     155                 :   }
     156                 :   /*
     157                 :    * We would like to set the protection to PROT_NONE, but on Windows
     158                 :    * there's an ugly hack in NaClMprotect where PROT_NONE can result
     159                 :    * in MEM_DECOMMIT, causing the contents of the page(s) to be lost!
     160                 :    */
     161               4 :   if (0 != NaClMprotect(g_squashybuffer, size, PROT_READ)) {
     162               0 :     NaClLog(0,
     163                 :             ("SerializeAllProcessors: interprocessor interrupt"
     164                 :              " generation failed: could not reverse shield polarity (3)\n"));
     165               0 :     return FALSE;
     166                 :   }
     167               4 :   return TRUE;
     168               4 : }
     169                 : 
     170             159 : int NaClCopyInstruction(uint8_t *dst, uint8_t *src, uint8_t sz) {
     171             159 :   intptr_t offset = 0;
     172             159 :   uint8_t *firstbyte_p = dst;
     173                 : 
     174             930 :   while (sz > 0 && dst[0] == src[0]) {
     175                 :     /* scroll to first changed byte */
     176             301 :     dst++, src++, sz--;
     177             301 :   }
     178                 : 
     179             159 :   if (sz == 0) {
     180                 :     /* instructions are identical, we are done */
     181             149 :     return 1;
     182                 :   }
     183                 : 
     184              66 :   while (sz > 0 && dst[sz-1] == src[sz-1]) {
     185                 :     /* trim identical bytes at end */
     186              18 :     sz--;
     187              18 :   }
     188                 : 
     189              10 :   if (sz == 1) {
     190                 :     /* we assume a 1-byte change is atomic */
     191               4 :     *dst = *src;
     192              10 :   } else if (IsTrustedWrite(dst, sz, 4, &offset)) {
     193               2 :     uint8_t tmp[4];
     194               2 :     memcpy(tmp, dst-offset, sizeof tmp);
     195               6 :     memcpy(tmp+offset, src, sz);
     196               2 :     onestore_memmove4(dst-offset, tmp);
     197               6 :   } else if (IsTrustedWrite(dst, sz, 8, &offset)) {
     198               2 :     uint8_t tmp[8];
     199               2 :     memcpy(tmp, dst-offset, sizeof tmp);
     200               6 :     memcpy(tmp+offset, src, sz);
     201               2 :     onestore_memmove8(dst-offset, tmp);
     202               2 :   } else {
     203                 :     /* the slow path, first flip first byte to halt*/
     204               2 :     uint8_t firstbyte = firstbyte_p[0];
     205               2 :     firstbyte_p[0] = kNaClFullStop;
     206                 : 
     207               2 :     if (!SerializeAllProcessors()) return 0;
     208                 : 
     209                 :     /* copy the rest of instruction */
     210               2 :     if (dst == firstbyte_p) {
     211                 :       /* but not the first byte! */
     212               0 :       firstbyte = *src;
     213               0 :       dst++, src++, sz--;
     214               0 :     }
     215               6 :     memcpy(dst, src, sz);
     216                 : 
     217               2 :     if (!SerializeAllProcessors()) return 0;
     218                 : 
     219                 :     /* flip first byte back */
     220               2 :     firstbyte_p[0] = firstbyte;
     221                 :   }
     222              10 :   return 1;
     223             159 : }

Generated by: LCOV version 1.7