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