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 "native_client/src/include/concurrency_ops.h"
8 : #include "native_client/src/shared/platform/nacl_log.h"
9 : #include "native_client/src/shared/utils/types.h"
10 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
11 : #include "native_client/src/trusted/validator/ncvalidate.h"
12 :
13 : const size_t kMinimumCachedCodeSize = 40000;
14 :
15 : /* Translate validation status to values wanted by sel_ldr. */
16 1465 : static int NaClValidateStatus(NaClValidationStatus status) {
17 1465 : switch (status) {
18 : case NaClValidationSucceeded:
19 1379 : return LOAD_OK;
20 : case NaClValidationFailedOutOfMemory:
21 : /* Note: this is confusing, but is what sel_ldr is expecting. */
22 43 : return LOAD_BAD_FILE;
23 : case NaClValidationFailed:
24 : case NaClValidationFailedNotImplemented:
25 : case NaClValidationFailedCpuNotSupported:
26 : case NaClValidationFailedSegmentationIssue:
27 : default:
28 43 : return LOAD_VALIDATION_FAILED;
29 : }
30 1422 : }
31 :
32 1402 : int NaClValidateCode(struct NaClApp *nap, uintptr_t guest_addr,
33 1402 : uint8_t *data, size_t size,
34 1402 : const struct NaClValidationMetadata *metadata) {
35 1402 : NaClValidationStatus status = NaClValidationSucceeded;
36 1402 : struct NaClValidationCache *cache = nap->validation_cache;
37 1402 : const struct NaClValidatorInterface *validator = nap->validator;
38 :
39 1402 : if (size < kMinimumCachedCodeSize) {
40 : /*
41 : * Don't cache the validation of small code chunks for three reasons:
42 : * 1) The size of the validation cache will be bounded. Cache entries are
43 : * better used for bigger code.
44 : * 2) The per-transaction overhead of validation caching is more noticeable
45 : * for small code.
46 : * 3) JITs tend to generate a lot of small code chunks, and JITed code may
47 : * never be seen again. Currently code size is the best mechanism we
48 : * have for heuristically distinguishing between JIT and static code.
49 : * (In practice most Mono JIT blocks are less than 1k, and a quick look
50 : * didn't show any above 35k.)
51 : * The choice of what constitutes "small" is arbitrary, and should be
52 : * empirically tuned.
53 : * TODO(ncbray) let the syscall specify if the code is cached or not.
54 : */
55 1058 : metadata = NULL;
56 1058 : cache = NULL;
57 1058 : }
58 :
59 : /* As fixed feature mode implies the text should be readonly, and
60 : * stubout mode implies updating the text, disallow their use together.
61 : */
62 1402 : if (nap->validator_stub_out_mode && nap->fixed_feature_cpu_mode) {
63 0 : NaClLog(LOG_FATAL,
64 : "stub_out_mode and fixed_feature_cpu_mode are incompatible\n");
65 0 : return LOAD_VALIDATION_FAILED;
66 : }
67 1402 : if (nap->validator_stub_out_mode) {
68 : /* Validation caching is currently incompatible with stubout. */
69 0 : metadata = NULL;
70 0 : cache = NULL;
71 : /* In stub out mode, we do two passes. The second pass acts as a
72 : sanity check that bad instructions were indeed overwritten with
73 : allowable HLTs. */
74 0 : status = validator->Validate(guest_addr, data, size,
75 : TRUE, /* stub out */
76 : FALSE, /* text is not read-only */
77 : nap->cpu_features,
78 : metadata,
79 : cache);
80 0 : }
81 1402 : if (status == NaClValidationSucceeded) {
82 : /* Fixed feature CPU mode implies read-only. */
83 1402 : int readonly_text = nap->fixed_feature_cpu_mode;
84 1402 : status = validator->Validate(guest_addr, data, size,
85 : FALSE, /* do not stub out */
86 : readonly_text,
87 : nap->cpu_features,
88 : metadata,
89 : cache);
90 1402 : }
91 1402 : return NaClValidateStatus(status);
92 1402 : }
93 :
94 13 : int NaClValidateCodeReplacement(struct NaClApp *nap, uintptr_t guest_addr,
95 13 : uint8_t *data_old, uint8_t *data_new,
96 13 : size_t size) {
97 13 : if (nap->validator_stub_out_mode) return LOAD_BAD_FILE;
98 13 : if (nap->fixed_feature_cpu_mode) return LOAD_BAD_FILE;
99 :
100 26 : if ((guest_addr % nap->bundle_size) != 0 ||
101 : (size % nap->bundle_size) != 0) {
102 0 : return LOAD_BAD_FILE;
103 : }
104 :
105 13 : return NaClValidateStatus(nap->validator->ValidateCodeReplacement(
106 : guest_addr, data_old, data_new, size, nap->cpu_features));
107 13 : }
108 :
109 7 : int NaClCopyCode(struct NaClApp *nap, uintptr_t guest_addr,
110 7 : uint8_t *data_old, uint8_t *data_new,
111 7 : size_t size) {
112 7 : int status;
113 : /* Fixed-feature mode disables any code copying for now. Currently
114 : * the only use of NaClCodeCopy() seems to be for dynamic code
115 : * modification, which should fail in NaClValidateCodeReplacement()
116 : * before reaching this.
117 : */
118 7 : if (nap->fixed_feature_cpu_mode) {
119 0 : return LOAD_BAD_FILE;
120 : }
121 7 : status = NaClValidateStatus(nap->validator->CopyCode(
122 : guest_addr, data_old, data_new, size,
123 : nap->cpu_features,
124 : NaClCopyInstruction));
125 : /*
126 : * Flush the processor's instruction cache. This is not necessary
127 : * for security, because any old cached instructions will just be
128 : * safe halt instructions. It is only necessary to ensure that
129 : * untrusted code runs correctly when it tries to execute the
130 : * dynamically-loaded code.
131 : */
132 7 : NaClFlushCacheForDoublyMappedCode(data_old,
133 : (uint8_t *) guest_addr,
134 : size);
135 7 : return status;
136 7 : }
137 :
138 263 : NaClErrorCode NaClValidateImage(struct NaClApp *nap) {
139 263 : uintptr_t memp;
140 263 : uintptr_t endp;
141 263 : size_t regionsize;
142 263 : NaClErrorCode rcode;
143 :
144 263 : memp = nap->mem_start + NACL_TRAMPOLINE_END;
145 263 : endp = nap->mem_start + nap->static_text_end;
146 263 : regionsize = endp - memp;
147 263 : if (endp < memp) {
148 0 : return LOAD_NO_MEMORY;
149 : }
150 :
151 263 : if (nap->skip_validator) {
152 0 : NaClLog(LOG_ERROR, "VALIDATION SKIPPED.\n");
153 0 : return LOAD_OK;
154 : } else {
155 : /* TODO(ncbray) metadata for the main image. */
156 263 : rcode = NaClValidateCode(nap, NACL_TRAMPOLINE_END,
157 : (uint8_t *) memp, regionsize, NULL);
158 263 : if (LOAD_OK != rcode) {
159 1 : if (nap->ignore_validator_result) {
160 0 : NaClLog(LOG_ERROR, "VALIDATION FAILED: continuing anyway...\n");
161 0 : rcode = LOAD_OK;
162 0 : } else {
163 1 : NaClLog(LOG_ERROR, "VALIDATION FAILED.\n");
164 1 : NaClLog(LOG_ERROR,
165 : "Run sel_ldr in debug mode to ignore validation failure.\n");
166 1 : NaClLog(LOG_ERROR,
167 : "Run ncval <module-name> for validation error details.\n");
168 1 : rcode = LOAD_VALIDATION_FAILED;
169 : }
170 1 : }
171 : }
172 263 : return rcode;
173 263 : }
|