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