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 <assert.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/mman.h>
14
15 #include <nacl/nacl_dyncode.h>
16
17 #include "native_client/tests/dynamic_code_loading/dynamic_segment.h"
18 #include "native_client/tests/dynamic_code_loading/templates.h"
19 #include "native_client/tests/inbrowser_test_runner/test_runner.h"
20
21 #if defined(__x86_64__)
22 #define BUF_SIZE 64
23 #else
24 #define BUF_SIZE 32
25 #endif
26
27 #define NACL_BUNDLE_SIZE 32
28 /*
29 * TODO(bsy): get this value from the toolchain. Get the toolchain
30 * team to provide this value.
31 */
32 #define NUM_BUNDLES_FOR_HLT 3
33
34 struct code_section {
35 char *name;
36 char *start;
37 char *end;
38 };
39
40 struct code_section illegal_code_sections[] = {
41 { "misaligned_replacement",
42 &template_func_misaligned_replacement,
43 &template_func_misaligned_replacement_end },
44 { "illegal_register_replacement",
45 &template_func_illegal_register_replacement,
46 &template_func_illegal_register_replacement_end },
47 { "illegal_guard_replacement",
48 &template_func_illegal_guard_replacement,
49 &template_func_illegal_guard_replacement_end },
50 { "illegal_call_target",
51 &template_func_illegal_call_target,
52 &template_func_illegal_call_target_end },
53 { "illegal_constant_replacement",
54 &template_func_illegal_constant_replacement,
55 &template_func_illegal_constant_replacement_end },
56 };
57
58 uint8_t *next_addr = NULL;
59
allocate_code_space(int pages)60 uint8_t *allocate_code_space(int pages) {
61 uint8_t *addr;
62 if (next_addr == NULL) {
63 next_addr = (uint8_t *) DYNAMIC_CODE_SEGMENT_START;
64 }
65 addr = next_addr;
66 next_addr += 0x10000 * pages;
67 assert(next_addr < (uint8_t *) DYNAMIC_CODE_SEGMENT_END);
68 return addr;
69 }
70
fill_int32(uint8_t * data,size_t size,int32_t value)71 void fill_int32(uint8_t *data, size_t size, int32_t value) {
72 int i;
73 assert(size % 4 == 0);
74 /*
75 * All the archs we target supported unaligned word read/write, but
76 * check that the pointer is aligned anyway.
77 */
78 assert(((uintptr_t) data) % 4 == 0);
79 for (i = 0; i < size / 4; i++)
80 ((uint32_t *) data)[i] = value;
81 }
82
fill_nops(uint8_t * data,size_t size)83 void fill_nops(uint8_t *data, size_t size) {
84 #if defined(__i386__) || defined(__x86_64__)
85 memset(data, 0x90, size); /* NOPs */
86 #elif defined(__arm__)
87 fill_int32(data, size, 0xe1a00000); /* NOP (MOV r0, r0) */
88 #else
89 # error "Unknown arch"
90 #endif
91 }
92
93 /*
94 * Getting the assembler to pad our code fragments in templates.S is
95 * awkward because we have to output them in data mode, in which the
96 * assembler wants to output zeroes instead of NOPs for padding.
97 */
copy_and_pad_fragment(void * dest,int dest_size,const char * fragment_start,const char * fragment_end)98 void copy_and_pad_fragment(void *dest,
99 int dest_size,
100 const char *fragment_start,
101 const char *fragment_end) {
102 int fragment_size = fragment_end - fragment_start;
103 assert(dest_size % 32 == 0);
104 assert(fragment_size <= dest_size);
105 fill_nops(dest, dest_size);
106 memcpy(dest, fragment_start, fragment_size);
107 }
108
109 /* Check that we can dynamically rewrite code. */
test_replacing_code(void)110 void test_replacing_code(void) {
111 uint8_t *load_area = allocate_code_space(1);
112 uint8_t buf[BUF_SIZE];
113 int rc;
114 int (*func)(void);
115
116 copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end);
117 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
118 assert(rc == 0);
119 func = (int (*)(void)) (uintptr_t) load_area;
120 rc = func();
121 assert(rc == MARKER_OLD);
122
123 /* write replacement to the same location */
124 copy_and_pad_fragment(buf, sizeof(buf), &template_func_replacement,
125 &template_func_replacement_end);
126 rc = nacl_dyncode_modify(load_area, buf, sizeof(buf));
127 assert(rc == 0);
128 func = (int (*)(void)) (uintptr_t) load_area;
129 rc = func();
130 assert(rc == MARKER_NEW);
131 }
132
133
134 /* Check that we can dynamically rewrite code. */
test_replacing_code_unaligned(void)135 void test_replacing_code_unaligned(void) {
136 uint8_t *load_area = allocate_code_space(1);
137 uint8_t buf[BUF_SIZE];
138 int first_diff = 0;
139 int rc;
140 int (*func)(void);
141
142 copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end);
143 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
144 assert(rc == 0);
145 func = (int (*)(void)) (uintptr_t) load_area;
146 rc = func();
147 assert(rc == MARKER_OLD);
148
149 /* write replacement to the same location, unaligned */
150 copy_and_pad_fragment(buf, sizeof(buf), &template_func_replacement,
151 &template_func_replacement_end);
152 /* we find first byte where old and new code differs */
153 while (buf[first_diff] == load_area[first_diff] && first_diff < sizeof buf) {
154 first_diff++;
155 }
156 /* and check, that there is some data in common, and some different */
157 assert(first_diff > 0 && first_diff < sizeof(buf));
158 rc = nacl_dyncode_modify(load_area+first_diff, buf+first_diff,
159 sizeof(buf)-first_diff);
160 assert(rc == 0);
161 func = (int (*)(void)) (uintptr_t) load_area;
162 rc = func();
163 assert(rc == MARKER_NEW);
164 }
165
166 #if defined(__i386__) || defined(__x86_64__)
167 /* Check that we can rewrite instruction that crosses align boundaries. */
test_replacing_code_slowpaths(void)168 void test_replacing_code_slowpaths(void) {
169 uint8_t *load_area = allocate_code_space(1);
170 uint8_t buf[NACL_BUNDLE_SIZE];
171 size_t size;
172 int rc;
173 /* Offsets to copy an instruction to. */
174 int off1, off2, off3;
175
176 fill_nops(buf, sizeof(buf));
177 size = (size_t) (&template_instr_end - &template_instr);
178 assert(size <= 5);
179 off1 = 4 - size + 1; /* Cross 4 byte boundary */
180 off2 = 24 - size + 1; /* Cross 8 byte boundary */
181 off3 = 16 - size + 1; /* Cross 16 byte boundary */
182 memcpy(buf + off1, &template_instr, size);
183 memcpy(buf + off2, &template_instr, size);
184 memcpy(buf + off3, &template_instr, size);
185 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
186 assert(rc == 0);
187
188 memcpy(buf + off1, &template_instr_replace, size);
189 rc = nacl_dyncode_modify(load_area + off1, buf + off1, size);
190 assert(rc == 0);
191 assert(memcmp(buf + off1, load_area + off1, size) == 0);
192
193 memcpy(buf + off2, &template_instr_replace, size);
194 rc = nacl_dyncode_modify(load_area + off2, buf + off2, size);
195 assert(rc == 0);
196 assert(memcmp(buf + off2, load_area + off2, size) == 0);
197
198 memcpy(buf + off3, &template_instr_replace, size);
199 rc = nacl_dyncode_modify(load_area + off3, buf + off3, size);
200 assert(rc == 0);
201 assert(memcmp(buf + off3, load_area + off3, size) == 0);
202 }
203 #endif
204
205 /* Check code replacement constraints */
test_illegal_code_replacment(void)206 void test_illegal_code_replacment(void) {
207 uint8_t *load_area = allocate_code_space(1);
208 uint8_t buf[BUF_SIZE];
209 int rc;
210 int i;
211 int (*func)(void);
212
213 copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end);
214 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
215 assert(rc == 0);
216 func = (int (*)(void)) (uintptr_t) load_area;
217 rc = func();
218 assert(rc == MARKER_OLD);
219
220 for (i = 0;
221 i < (sizeof(illegal_code_sections) / sizeof(struct code_section));
222 i++) {
223 printf("\t%s\n", illegal_code_sections[i].name);
224
225 /* write illegal replacement to the same location */
226 copy_and_pad_fragment(buf, sizeof(buf), illegal_code_sections[i].start,
227 illegal_code_sections[i].end);
228 rc = nacl_dyncode_modify(load_area, buf, sizeof(buf));
229 assert(rc != 0);
230 func = (int (*)(void)) (uintptr_t) load_area;
231 rc = func();
232 assert(rc == MARKER_OLD);
233 }
234 }
235
test_external_jump_target_replacement(void)236 void test_external_jump_target_replacement(void) {
237 uint8_t *load_area = allocate_code_space(1);
238 /* BUF_SIZE * 2 because this function necessarily has an extra bundle. */
239 uint8_t buf[BUF_SIZE * 2];
240 int rc;
241 int (*func)(void);
242 const int kNaClBundleSize = NACL_BUNDLE_SIZE;
243
244 copy_and_pad_fragment(buf, sizeof(buf),
245 &template_func_external_jump_target,
246 &template_func_external_jump_target_end);
247
248 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
249 assert(rc == 0);
250 func = (int (*)(void)) (uintptr_t) load_area;
251 rc = func();
252 assert(rc == MARKER_OLD);
253
254 copy_and_pad_fragment(buf, sizeof(buf),
255 &template_func_external_jump_target_replace,
256 &template_func_external_jump_target_replace_end);
257 /* Only copy one bundle so we can test an unaligned external jump target */
258 rc = nacl_dyncode_modify(load_area, buf, kNaClBundleSize);
259 assert(rc == 0);
260 func = (int (*)(void)) (uintptr_t) load_area;
261 rc = func();
262 assert(rc == MARKER_NEW);
263 }
264
265 #if defined(__i386__) || defined(__x86_64__)
test_jump_into_super_inst_create(void)266 void test_jump_into_super_inst_create(void) {
267 uint8_t *load_area = allocate_code_space(1);
268 uint8_t buf[BUF_SIZE];
269 int rc;
270
271 /* A direct jump into a bundle is invalid. */
272 copy_and_pad_fragment(buf, sizeof(buf), &jump_into_super_inst_modified,
273 &jump_into_super_inst_modified_end);
274 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
275 assert(rc != 0);
276 assert(errno == EINVAL);
277 }
278
test_simle_replacement(const char * fragment1,const char * fragment1_end,const char * fragment2,const char * fragment2_end)279 int test_simle_replacement(const char *fragment1, const char *fragment1_end,
280 const char *fragment2, const char *fragment2_end) {
281 uint8_t *load_area = allocate_code_space(1);
282 uint8_t buf[BUF_SIZE];
283 int rc;
284
285 /* The original version is fine. */
286 copy_and_pad_fragment(buf, sizeof(buf), fragment1, fragment1_end);
287 rc = nacl_dyncode_create(load_area, buf, sizeof(buf));
288 assert(rc == 0);
289
290 copy_and_pad_fragment(buf, sizeof(buf), fragment2, fragment2_end);
291 rc = nacl_dyncode_modify(load_area, buf, sizeof(buf));
292 return rc;
293 }
294
test_start_with_super_inst_replace(void)295 void test_start_with_super_inst_replace(void) {
296 int rc;
297
298 /*
299 * Replace the code with itself. This makes sure that replacement code can
300 * start with a super instruction.
301 */
302 rc = test_simle_replacement(&jump_into_super_inst_original,
303 &jump_into_super_inst_original_end,
304 &jump_into_super_inst_original,
305 &jump_into_super_inst_original_end);
306 assert(rc == 0);
307 }
308
309 #if defined(__i386__)
test_delete_superinstruction(void)310 void test_delete_superinstruction(void) {
311 int rc;
312
313 /* Replace the superinstruction with normal instruction of the same size. */
314 rc = test_simle_replacement(&delete_superinstruction,
315 &delete_superinstruction_end,
316 &delete_superinstruction_replace,
317 &delete_superinstruction_replace_end);
318 assert(rc != 0);
319 assert(errno == EINVAL);
320 }
321
test_delete_superinstruction_split(void)322 void test_delete_superinstruction_split(void) {
323 int rc;
324
325 /*
326 * Replace the superinstruction with couple of normal instruction of the same
327 * size as atomic instructions of the superinstruction.
328 */
329 rc = test_simle_replacement(&delete_superinstruction_split,
330 &delete_superinstruction_split_end,
331 &delete_superinstruction_split_replace,
332 &delete_superinstruction_split_replace_end);
333 assert(rc != 0);
334 assert(errno == EINVAL);
335 }
336
test_create_superinstruction(void)337 void test_create_superinstruction(void) {
338 int rc;
339
340 /* Replace normal instruction with the superinstruction of the same size. */
341 rc = test_simle_replacement(&create_superinstruction_split,
342 &create_superinstruction_split_end,
343 &create_superinstruction_split_replace,
344 &create_superinstruction_split_replace_end);
345 assert(rc != 0);
346 assert(errno == EINVAL);
347 }
348
test_create_superinstruction_split(void)349 void test_create_superinstruction_split(void) {
350 int rc;
351
352 /*
353 * Replace couple of normal instruction which have sizes identical to the size
354 * of atomic instructions in the superinstruction with said superinstruction.
355 */
356 rc = test_simle_replacement(&create_superinstruction,
357 &create_superinstruction_end,
358 &create_superinstruction_replace,
359 &create_superinstruction_replace_end);
360 assert(rc != 0);
361 assert(errno == EINVAL);
362 }
363
test_change_boundaris_first_instructions(void)364 void test_change_boundaris_first_instructions(void) {
365 int rc;
366
367 /*
368 * Replace the code with the code which shifts boundary between first and
369 * second instruction.
370 */
371 rc = test_simle_replacement(
372 &change_boundaries_first_instructions,
373 &change_boundaries_first_instructions_end,
374 &change_boundaries_first_instructions_replace,
375 &change_boundaries_first_instructions_replace_end);
376 assert(rc != 0);
377 assert(errno == EINVAL);
378 }
379
test_change_boundaris_last_instructions(void)380 void test_change_boundaris_last_instructions(void) {
381 int rc;
382
383 /*
384 * Replace the code with the code which shifts boundary between last and next
385 * to last instructions.
386 */
387 rc = test_simle_replacement(&change_boundaries_last_instructions,
388 &change_boundaries_last_instructions_end,
389 &change_boundaries_last_instructions_replace,
390 &change_boundaries_last_instructions_replace_end);
391 assert(rc != 0);
392 assert(errno == EINVAL);
393 }
394 #endif
395
test_jump_into_super_inst_replace(void)396 void test_jump_into_super_inst_replace(void) {
397 int rc;
398
399 /*
400 * The modified version cannot be used as a replacement.
401 * See: http://code.google.com/p/nativeclient/issues/detail?id=2563
402 */
403 rc = test_simle_replacement(&jump_into_super_inst_original,
404 &jump_into_super_inst_original_end,
405 &jump_into_super_inst_modified,
406 &jump_into_super_inst_modified_end);
407 assert(rc != 0);
408 assert(errno == EINVAL);
409 }
410 #endif
411
run_test(const char * test_name,void (* test_func)(void))412 void run_test(const char *test_name, void (*test_func)(void)) {
413 printf("Running %s...\n", test_name);
414 test_func();
415 }
416
is_replacement_enabled(void)417 int is_replacement_enabled(void) {
418 char trash;
419 return (0 == nacl_dyncode_modify(allocate_code_space(1), &trash, 0));
420 }
421
422 #define RUN_TEST(test_func) (run_test(#test_func, test_func))
423
TestMain(void)424 int TestMain(void) {
425 /* Turn off stdout buffering to aid debugging in case of a crash. */
426 setvbuf(stdout, NULL, _IONBF, 0);
427
428 assert(is_replacement_enabled());
429
430 RUN_TEST(test_replacing_code);
431 RUN_TEST(test_replacing_code_unaligned);
432 #if defined(__i386__) || defined(__x86_64__)
433 RUN_TEST(test_replacing_code_slowpaths);
434 RUN_TEST(test_jump_into_super_inst_create);
435 RUN_TEST(test_start_with_super_inst_replace);
436 RUN_TEST(test_jump_into_super_inst_replace);
437 #endif
438 RUN_TEST(test_illegal_code_replacment);
439 RUN_TEST(test_external_jump_target_replacement);
440 #if defined(__i386__)
441 RUN_TEST(test_delete_superinstruction);
442 RUN_TEST(test_delete_superinstruction_split);
443 RUN_TEST(test_create_superinstruction);
444 RUN_TEST(test_create_superinstruction_split);
445 RUN_TEST(test_change_boundaris_first_instructions);
446 RUN_TEST(test_change_boundaris_last_instructions);
447 #endif
448
449 return 0;
450 }
451
main(void)452 int main(void) {
453 return RunTests(TestMain);
454 }
455