OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "native_client/src/trusted/plugin/json_manifest.h" | 9 #include "native_client/src/trusted/plugin/json_manifest.h" |
10 | 10 |
(...skipping 27 matching lines...) Expand all Loading... |
38 | 38 |
39 // Url Resolution keys | 39 // Url Resolution keys |
40 const char* const kPnaclTranslateKey = "pnacl-translate"; | 40 const char* const kPnaclTranslateKey = "pnacl-translate"; |
41 const char* const kUrlKey = "url"; | 41 const char* const kUrlKey = "url"; |
42 | 42 |
43 // Pnacl keys | 43 // Pnacl keys |
44 const char* const kCacheIdentityKey = "sha256"; | 44 const char* const kCacheIdentityKey = "sha256"; |
45 const char* const kOptLevelKey = "-O"; | 45 const char* const kOptLevelKey = "-O"; |
46 const char* const kPnaclExperimentalFlags = "experimental_flags"; | 46 const char* const kPnaclExperimentalFlags = "experimental_flags"; |
47 | 47 |
48 // Sample manifest file: | 48 // Sample NaCL manifest file: |
49 // { | 49 // { |
50 // "program": { | 50 // "program": { |
51 // "x86-32": {"url": "myprogram_x86-32.nexe"}, | 51 // "x86-32": {"url": "myprogram_x86-32.nexe"}, |
52 // "x86-64": {"url": "myprogram_x86-64.nexe"}, | 52 // "x86-64": {"url": "myprogram_x86-64.nexe"}, |
53 // "arm": {"url": "myprogram_arm.nexe"}, | 53 // "arm": {"url": "myprogram_arm.nexe"} |
54 // "portable": { | |
55 // "pnacl-translate": { | |
56 // "url": "myprogram.pexe", | |
57 // "sha256": "...", | |
58 // "-O": 0 | |
59 // } | |
60 // } | |
61 // }, | 54 // }, |
62 // "interpreter": { | 55 // "interpreter": { |
63 // "x86-32": {"url": "interpreter_x86-32.nexe"}, | 56 // "x86-32": {"url": "interpreter_x86-32.nexe"}, |
64 // "x86-64": {"url": "interpreter_x86-64.nexe"}, | 57 // "x86-64": {"url": "interpreter_x86-64.nexe"}, |
65 // "arm": {"url": "interpreter_arm.nexe"} | 58 // "arm": {"url": "interpreter_arm.nexe"} |
66 // }, | 59 // }, |
67 // "files": { | 60 // "files": { |
68 // "foo.txt": { | 61 // "foo.txt": { |
69 // "portable": {"url": "foo.txt"} | 62 // "portable": {"url": "foo.txt"} |
70 // }, | 63 // }, |
71 // "bar.txt": { | 64 // "bar.txt": { |
72 // "x86-32": {"url": "x86-32/bar.txt"}, | 65 // "x86-32": {"url": "x86-32/bar.txt"}, |
73 // "portable": {"url": "bar.txt"} | 66 // "portable": {"url": "bar.txt"} |
74 // }, | 67 // }, |
75 // "libfoo.so": { | 68 // "libfoo.so": { |
76 // "x86-64-ivybridge-foo": { "url": "..."}, | 69 // "x86-64" : { "url": "..." } |
77 // "x86-64-ivybridge" : { "pnacl-translate": { "url": "..."}}, | |
78 // "x86-64" : { "url": "..." }, | |
79 // "portable": {"pnacl-translate": {"url": "..."}} | |
80 // } | 70 // } |
81 // } | 71 // } |
82 // } | 72 // } |
| 73 |
| 74 // Sample PNaCl manifest file: |
| 75 // { |
| 76 // "program": { |
| 77 // "portable": { |
| 78 // "pnacl-translate": { |
| 79 // "url": "myprogram.pexe", |
| 80 // "sha256": "...", |
| 81 // "-O": 0 |
| 82 // } |
| 83 // } |
| 84 // }, |
| 85 // "files": { |
| 86 // "foo.txt": { |
| 87 // "portable": {"url": "foo.txt"} |
| 88 // }, |
| 89 // "bar.txt": { |
| 90 // "portable": {"url": "bar.txt"} |
| 91 // } |
| 92 // } |
| 93 // } |
83 | 94 |
84 // Looks up |property_name| in the vector |valid_names| with length | 95 // Looks up |property_name| in the vector |valid_names| with length |
85 // |valid_name_count|. Returns true if |property_name| is found. | 96 // |valid_name_count|. Returns true if |property_name| is found. |
86 bool FindMatchingProperty(const nacl::string& property_name, | 97 bool FindMatchingProperty(const nacl::string& property_name, |
87 const char** valid_names, | 98 const char** valid_names, |
88 size_t valid_name_count) { | 99 size_t valid_name_count) { |
89 for (size_t i = 0; i < valid_name_count; ++i) { | 100 for (size_t i = 0; i < valid_name_count; ++i) { |
90 if (property_name == valid_names[i]) { | 101 if (property_name == valid_names[i]) { |
91 return true; | 102 return true; |
92 } | 103 } |
(...skipping 22 matching lines...) Expand all Loading... |
115 *error_string = error_stream.str(); | 126 *error_string = error_stream.str(); |
116 return false; | 127 return false; |
117 } | 128 } |
118 // Check for unknown dictionary members. | 129 // Check for unknown dictionary members. |
119 Json::Value::Members members = dictionary.getMemberNames(); | 130 Json::Value::Members members = dictionary.getMemberNames(); |
120 for (size_t i = 0; i < members.size(); ++i) { | 131 for (size_t i = 0; i < members.size(); ++i) { |
121 nacl::string property_name = members[i]; | 132 nacl::string property_name = members[i]; |
122 if (!FindMatchingProperty(property_name, | 133 if (!FindMatchingProperty(property_name, |
123 valid_keys, | 134 valid_keys, |
124 valid_key_count)) { | 135 valid_key_count)) { |
125 // TODO(jvoung): Should this set error_string and return false? | 136 // For forward compatibility, we do not prohibit other keys being in |
| 137 // the dictionary. |
126 PLUGIN_PRINTF(("WARNING: '%s' property '%s' has unknown key '%s'.\n", | 138 PLUGIN_PRINTF(("WARNING: '%s' property '%s' has unknown key '%s'.\n", |
127 parent_key.c_str(), | 139 parent_key.c_str(), |
128 container_key.c_str(), property_name.c_str())); | 140 container_key.c_str(), property_name.c_str())); |
129 } | 141 } |
130 } | 142 } |
131 // Check for required members. | 143 // Check for required members. |
132 for (size_t i = 0; i < required_key_count; ++i) { | 144 for (size_t i = 0; i < required_key_count; ++i) { |
133 if (!dictionary.isMember(required_keys[i])) { | 145 if (!dictionary.isMember(required_keys[i])) { |
134 nacl::stringstream error_stream; | 146 nacl::stringstream error_stream; |
135 error_stream << parent_key << " property '" << container_key | 147 error_stream << parent_key << " property '" << container_key |
136 << "' does not have required key: '" | 148 << "' does not have required key: '" |
137 << required_keys[i] << "'."; | 149 << required_keys[i] << "'."; |
138 *error_string = error_stream.str(); | 150 *error_string = error_stream.str(); |
139 return false; | 151 return false; |
140 } | 152 } |
141 } | 153 } |
142 return true; | 154 return true; |
143 } | 155 } |
144 | 156 |
145 // Validate a "url" dictionary assuming it was resolved from container_key. | 157 // Validate a "url" dictionary assuming it was resolved from container_key. |
146 // E.g., "container_key" : { "url": "foo.txt" } | 158 // E.g., "container_key" : { "url": "foo.txt" } |
147 bool IsValidUrlSpec(const Json::Value& url_spec, | 159 bool IsValidUrlSpec(const Json::Value& url_spec, |
148 const nacl::string& container_key, | 160 const nacl::string& container_key, |
149 const nacl::string& parent_key, | 161 const nacl::string& parent_key, |
| 162 const nacl::string& sandbox_isa, |
150 nacl::string* error_string) { | 163 nacl::string* error_string) { |
151 static const char* kManifestUrlSpecRequired[] = { | 164 static const char* kManifestUrlSpecRequired[] = { |
152 kUrlKey | 165 kUrlKey |
153 }; | 166 }; |
154 static const char* kManifestUrlSpecPlusOptional[] = { | 167 const char** urlSpecPlusOptional; |
155 kUrlKey, | 168 size_t urlSpecPlusOptionalLength; |
156 kCacheIdentityKey | 169 if (sandbox_isa == kPortableKey) { |
157 }; | 170 static const char* kPnaclUrlSpecPlusOptional[] = { |
| 171 kUrlKey, |
| 172 kCacheIdentityKey, |
| 173 kOptLevelKey, |
| 174 }; |
| 175 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional; |
| 176 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kPnaclUrlSpecPlusOptional); |
| 177 } else { |
| 178 urlSpecPlusOptional = kManifestUrlSpecRequired; |
| 179 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kManifestUrlSpecRequired); |
| 180 } |
158 if (!IsValidDictionary(url_spec, container_key, parent_key, | 181 if (!IsValidDictionary(url_spec, container_key, parent_key, |
159 kManifestUrlSpecPlusOptional, | 182 urlSpecPlusOptional, |
160 NACL_ARRAY_SIZE(kManifestUrlSpecPlusOptional), | 183 urlSpecPlusOptionalLength, |
161 kManifestUrlSpecRequired, | 184 kManifestUrlSpecRequired, |
162 NACL_ARRAY_SIZE(kManifestUrlSpecRequired), | 185 NACL_ARRAY_SIZE(kManifestUrlSpecRequired), |
163 error_string)) { | 186 error_string)) { |
164 return false; | 187 return false; |
165 } | 188 } |
| 189 // URL specifications must not contain "pnacl-translate" keys. |
| 190 // This prohibits NaCl clients from invoking PNaCl. |
| 191 Json::Value translate = url_spec[kPnaclTranslateKey]; |
| 192 if (!translate.empty()) { |
| 193 nacl::stringstream error_stream; |
| 194 error_stream << parent_key << " property '" << container_key << |
| 195 "' has '" << kPnaclTranslateKey << "' inside URL spec."; |
| 196 *error_string = error_stream.str(); |
| 197 return false; |
| 198 } |
| 199 // Verify the correct types of the fields if they exist. |
166 Json::Value url = url_spec[kUrlKey]; | 200 Json::Value url = url_spec[kUrlKey]; |
167 if (!url.isString()) { | 201 if (!url.isString()) { |
168 nacl::stringstream error_stream; | 202 nacl::stringstream error_stream; |
169 error_stream << parent_key << " property '" << container_key << | 203 error_stream << parent_key << " property '" << container_key << |
170 "' has non-string value '" << url.toStyledString() << | 204 "' has non-string value '" << url.toStyledString() << |
171 "' for key '" << kUrlKey << "'."; | 205 "' for key '" << kUrlKey << "'."; |
172 *error_string = error_stream.str(); | 206 *error_string = error_stream.str(); |
173 return false; | 207 return false; |
174 } | 208 } |
| 209 Json::Value cache_identity = url_spec[kCacheIdentityKey]; |
| 210 if (!cache_identity.empty() && !cache_identity.isString()) { |
| 211 nacl::stringstream error_stream; |
| 212 error_stream << parent_key << " property '" << container_key << |
| 213 "' has non-string value '" << cache_identity.toStyledString() << |
| 214 "' for key '" << kCacheIdentityKey << "'."; |
| 215 *error_string = error_stream.str(); |
| 216 return false; |
| 217 } |
| 218 Json::Value opt_level = url_spec[kOptLevelKey]; |
| 219 if (!opt_level.empty() && !opt_level.isNumeric()) { |
| 220 nacl::stringstream error_stream; |
| 221 error_stream << parent_key << " property '" << container_key << |
| 222 "' has non-numeric value '" << opt_level.toStyledString() << |
| 223 "' for key '" << kOptLevelKey << "'."; |
| 224 *error_string = error_stream.str(); |
| 225 return false; |
| 226 } |
175 return true; | 227 return true; |
176 } | 228 } |
177 | 229 |
178 // Validate a "pnacl-translate" dictionary, assuming it was resolved from | 230 // Validate a "pnacl-translate" dictionary, assuming it was resolved from |
179 // container_key. E.g., "container_key" : { "pnacl_translate" : URLSpec } | 231 // container_key. E.g., "container_key" : { "pnacl_translate" : URLSpec } |
180 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec, | 232 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec, |
181 const nacl::string& container_key, | 233 const nacl::string& container_key, |
182 const nacl::string& parent_key, | 234 const nacl::string& parent_key, |
| 235 const nacl::string& sandbox_isa, |
183 nacl::string* error_string) { | 236 nacl::string* error_string) { |
184 static const char* kManifestPnaclSpecProperties[] = { | 237 static const char* kManifestPnaclSpecProperties[] = { |
185 kPnaclTranslateKey | 238 kPnaclTranslateKey |
186 }; | 239 }; |
187 if (!IsValidDictionary(pnacl_spec, container_key, parent_key, | 240 if (!IsValidDictionary(pnacl_spec, container_key, parent_key, |
188 kManifestPnaclSpecProperties, | 241 kManifestPnaclSpecProperties, |
189 NACL_ARRAY_SIZE(kManifestPnaclSpecProperties), | 242 NACL_ARRAY_SIZE(kManifestPnaclSpecProperties), |
190 kManifestPnaclSpecProperties, | 243 kManifestPnaclSpecProperties, |
191 NACL_ARRAY_SIZE(kManifestPnaclSpecProperties), | 244 NACL_ARRAY_SIZE(kManifestPnaclSpecProperties), |
192 error_string)) { | 245 error_string)) { |
193 return false; | 246 return false; |
194 } | 247 } |
195 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey]; | 248 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey]; |
196 if (!IsValidUrlSpec(url_spec, kPnaclTranslateKey, | 249 if (!IsValidUrlSpec(url_spec, kPnaclTranslateKey, |
197 container_key, error_string)) { | 250 container_key, sandbox_isa, error_string)) { |
198 return false; | 251 return false; |
199 } | 252 } |
200 return true; | 253 return true; |
201 } | 254 } |
202 | 255 |
203 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary | 256 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary |
204 // is validated to have keys from within the set of recognized ISAs. Unknown | 257 // is validated to have keys from within the set of recognized ISAs. Unknown |
205 // ISAs are allowed, but ignored and warnings are produced. It is also validated | 258 // ISAs are allowed, but ignored and warnings are produced. It is also validated |
206 // that it must have an entry to match the ISA specified in |sandbox_isa| or | 259 // that it must have an entry to match the ISA specified in |sandbox_isa| or |
207 // have a fallback 'portable' entry if there is no match. Returns true if | 260 // have a fallback 'portable' entry if there is no match. Returns true if |
208 // |dictionary| is an ISA to URL map. Sets |error_info| to something | 261 // |dictionary| is an ISA to URL map. Sets |error_info| to something |
209 // descriptive if it fails. | 262 // descriptive if it fails. |
210 bool IsValidISADictionary(const Json::Value& dictionary, | 263 bool IsValidISADictionary(const Json::Value& dictionary, |
211 const nacl::string& parent_key, | 264 const nacl::string& parent_key, |
212 const nacl::string& sandbox_isa, | 265 const nacl::string& sandbox_isa, |
| 266 bool must_find_matching_entry, |
213 ErrorInfo* error_info) { | 267 ErrorInfo* error_info) { |
214 if (error_info == NULL) return false; | 268 if (error_info == NULL) return false; |
215 | 269 |
216 // An ISA to URL dictionary has to be an object. | 270 // An ISA to URL dictionary has to be an object. |
217 if (!dictionary.isObject()) { | 271 if (!dictionary.isObject()) { |
218 error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE, | 272 error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE, |
219 nacl::string("manifest: ") + parent_key + | 273 nacl::string("manifest: ") + parent_key + |
220 " property is not an ISA to URL dictionary"); | 274 " property is not an ISA to URL dictionary"); |
221 return false; | 275 return false; |
222 } | 276 } |
223 // The keys to the dictionary have to be valid ISA names. | 277 // Build the set of reserved ISA dictionary keys. |
224 Json::Value::Members members = dictionary.getMemberNames(); | 278 const char** isaProperties; |
225 for (size_t i = 0; i < members.size(); ++i) { | 279 size_t isaPropertiesLength; |
226 // The known ISA values for ISA dictionaries in the manifest. | 280 if (sandbox_isa == kPortableKey) { |
227 static const char* kManifestISAProperties[] = { | 281 // The known values for PNaCl ISA dictionaries in the manifest. |
| 282 static const char* kPnaclManifestISAProperties[] = { |
| 283 kPortableKey |
| 284 }; |
| 285 isaProperties = kPnaclManifestISAProperties; |
| 286 isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties); |
| 287 } else { |
| 288 // The known values for NaCl ISA dictionaries in the manifest. |
| 289 static const char* kNaClManifestISAProperties[] = { |
228 kX8632Key, | 290 kX8632Key, |
229 kX8664Key, | 291 kX8664Key, |
230 kArmKey, | 292 kArmKey, |
| 293 // "portable" is here to allow checking that, if present, it can |
| 294 // only refer to an URL, such as for a data file, and not to |
| 295 // "pnacl-translate", which would cause the creation of a nexe. |
231 kPortableKey | 296 kPortableKey |
232 }; | 297 }; |
| 298 isaProperties = kNaClManifestISAProperties; |
| 299 isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties); |
| 300 } |
| 301 // Check that entries in the dictionary are structurally correct. |
| 302 Json::Value::Members members = dictionary.getMemberNames(); |
| 303 for (size_t i = 0; i < members.size(); ++i) { |
233 nacl::string property_name = members[i]; | 304 nacl::string property_name = members[i]; |
234 if (!FindMatchingProperty(property_name, | |
235 kManifestISAProperties, | |
236 NACL_ARRAY_SIZE(kManifestISAProperties))) { | |
237 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized ISA '%s'.\n", | |
238 property_name.c_str())); | |
239 } | |
240 // Could be "arch/portable" : URLSpec, or | |
241 // it could be "arch/portable" : { "pnacl-translate": URLSpec } | |
242 // for executables that need to be translated. | |
243 Json::Value property_value = dictionary[property_name]; | 305 Json::Value property_value = dictionary[property_name]; |
244 nacl::string error_string; | 306 nacl::string error_string; |
245 if (!IsValidUrlSpec(property_value, property_name, parent_key, | 307 if (FindMatchingProperty(property_name, |
246 &error_string) && | 308 isaProperties, |
247 !IsValidPnaclTranslateSpec(property_value, property_name, | 309 isaPropertiesLength)) { |
248 parent_key, &error_string)) { | 310 // For NaCl, arch entries can only be |
249 error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE, | 311 // "arch/portable" : URLSpec |
250 nacl::string("manifest: ") + error_string); | 312 // For PNaCl arch in "program" dictionary entries can only be |
251 return false; | 313 // "portable" : { "pnacl-translate": URLSpec } |
| 314 // For PNaCl arch elsewhere, dictionary entries can only be |
| 315 // "portable" : URLSpec |
| 316 if ((sandbox_isa != kPortableKey && |
| 317 !IsValidUrlSpec(property_value, property_name, parent_key, |
| 318 sandbox_isa, &error_string)) || |
| 319 (sandbox_isa == kPortableKey && |
| 320 parent_key == kProgramKey && |
| 321 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key, |
| 322 sandbox_isa, &error_string)) || |
| 323 (sandbox_isa == kPortableKey && |
| 324 parent_key != kProgramKey && |
| 325 !IsValidUrlSpec(property_value, property_name, parent_key, |
| 326 sandbox_isa, &error_string))) { |
| 327 error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE, |
| 328 nacl::string("manifest: ") + error_string); |
| 329 return false; |
| 330 } |
| 331 } else { |
| 332 // For forward compatibility, we do not prohibit other keys being in |
| 333 // the dictionary, as they may be architectures supported in later |
| 334 // versions. However, the value of these entries must be an URLSpec. |
| 335 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized key '%s'.\n", |
| 336 property_name.c_str())); |
| 337 if (!IsValidUrlSpec(property_value, property_name, parent_key, |
| 338 sandbox_isa, &error_string)) { |
| 339 error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE, |
| 340 nacl::string("manifest: ") + error_string); |
| 341 return false; |
| 342 } |
252 } | 343 } |
253 } | 344 } |
254 | 345 |
255 if (!sandbox_isa.empty()) { | 346 if (sandbox_isa == kPortableKey) { |
| 347 bool has_portable = dictionary.isMember(kPortableKey); |
| 348 |
| 349 if (!has_portable) { |
| 350 error_info->SetReport( |
| 351 ERROR_MANIFEST_PROGRAM_MISSING_ARCH, |
| 352 nacl::string("manifest: no version of ") + parent_key + |
| 353 " given for portable."); |
| 354 return false; |
| 355 } |
| 356 } else if (must_find_matching_entry) { |
256 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include | 357 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include |
257 // micro-architectures that can resolve to multiple valid sandboxes. | 358 // micro-architectures that can resolve to multiple valid sandboxes. |
258 bool has_isa = dictionary.isMember(sandbox_isa); | 359 bool has_isa = dictionary.isMember(sandbox_isa); |
259 bool has_portable = dictionary.isMember(kPortableKey); | 360 bool has_portable = dictionary.isMember(kPortableKey); |
260 | 361 |
261 if (!has_isa && !has_portable) { | 362 if (!has_isa && !has_portable) { |
262 error_info->SetReport( | 363 error_info->SetReport( |
263 ERROR_MANIFEST_PROGRAM_MISSING_ARCH, | 364 ERROR_MANIFEST_PROGRAM_MISSING_ARCH, |
264 nacl::string("manifest: no version of ") + parent_key + | 365 nacl::string("manifest: no version of ") + parent_key + |
265 " given for current arch and no portable version found."); | 366 " given for current arch and no portable version found."); |
(...skipping 20 matching lines...) Expand all Loading... |
286 } | 387 } |
287 if (url_spec.isMember(kPnaclExperimentalFlags)) { | 388 if (url_spec.isMember(kPnaclExperimentalFlags)) { |
288 pnacl_options->set_experimental_flags( | 389 pnacl_options->set_experimental_flags( |
289 url_spec[kPnaclExperimentalFlags].asString()); | 390 url_spec[kPnaclExperimentalFlags].asString()); |
290 } | 391 } |
291 } | 392 } |
292 | 393 |
293 bool GetURLFromISADictionary(const Json::Value& dictionary, | 394 bool GetURLFromISADictionary(const Json::Value& dictionary, |
294 const nacl::string& parent_key, | 395 const nacl::string& parent_key, |
295 const nacl::string& sandbox_isa, | 396 const nacl::string& sandbox_isa, |
296 bool prefer_portable, | |
297 nacl::string* url, | 397 nacl::string* url, |
298 PnaclOptions* pnacl_options, | 398 PnaclOptions* pnacl_options, |
299 ErrorInfo* error_info) { | 399 ErrorInfo* error_info) { |
300 if (url == NULL || pnacl_options == NULL || error_info == NULL) | 400 if (url == NULL || pnacl_options == NULL || error_info == NULL) |
301 return false; | 401 return false; |
302 | 402 |
303 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa, error_info)) { | 403 // When the application actually requests a resolved URL, we must have |
| 404 // a matching entry (sandbox_isa or portable) for NaCl. |
| 405 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa, true, |
| 406 error_info)) { |
304 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, | 407 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, |
305 "architecture " + sandbox_isa + | 408 "architecture " + sandbox_isa + |
306 " is not found for file " + parent_key); | 409 " is not found for file " + parent_key); |
307 return false; | 410 return false; |
308 } | 411 } |
309 | 412 |
310 *url = ""; | 413 *url = ""; |
311 | 414 |
312 // The call to IsValidISADictionary() above guarantees that either | 415 // The call to IsValidISADictionary() above guarantees that either |
313 // sandbox_isa or kPortableKey is present in the dictionary. | 416 // sandbox_isa or kPortableKey is present in the dictionary. |
314 bool has_portable = dictionary.isMember(kPortableKey); | 417 bool has_portable = dictionary.isMember(kPortableKey); |
315 bool has_isa = dictionary.isMember(sandbox_isa); | 418 bool has_isa = dictionary.isMember(sandbox_isa); |
316 nacl::string chosen_isa; | 419 nacl::string chosen_isa; |
317 if ((has_portable && prefer_portable) || !has_isa) { | 420 if ((sandbox_isa == kPortableKey) || (has_portable && !has_isa)) { |
318 chosen_isa = kPortableKey; | 421 chosen_isa = kPortableKey; |
319 } else { | 422 } else { |
320 chosen_isa = sandbox_isa; | 423 chosen_isa = sandbox_isa; |
321 } | 424 } |
322 const Json::Value& isa_spec = dictionary[chosen_isa]; | 425 const Json::Value& isa_spec = dictionary[chosen_isa]; |
323 // Check if this requires a pnacl-translate, otherwise just grab the URL. | 426 // Check if this requires a pnacl-translate, otherwise just grab the URL. |
324 // We may have pnacl-translate for isa-specific bitcode for CPU tuning. | 427 // We may have pnacl-translate for isa-specific bitcode for CPU tuning. |
325 if (isa_spec.isMember(kPnaclTranslateKey)) { | 428 if (isa_spec.isMember(kPnaclTranslateKey)) { |
| 429 // PNaCl |
326 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); | 430 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); |
327 pnacl_options->set_translate(true); | 431 pnacl_options->set_translate(true); |
328 } else { | 432 } else { |
| 433 // NaCl |
329 *url = isa_spec[kUrlKey].asString(); | 434 *url = isa_spec[kUrlKey].asString(); |
330 pnacl_options->set_translate(false); | 435 pnacl_options->set_translate(false); |
331 } | 436 } |
332 | 437 |
333 return true; | 438 return true; |
334 } | 439 } |
335 | 440 |
336 bool GetKeyUrl(const Json::Value& dictionary, | 441 bool GetKeyUrl(const Json::Value& dictionary, |
337 const nacl::string& key, | 442 const nacl::string& key, |
338 const nacl::string& sandbox_isa, | 443 const nacl::string& sandbox_isa, |
339 const Manifest* manifest, | 444 const Manifest* manifest, |
340 bool prefer_portable, | |
341 nacl::string* full_url, | 445 nacl::string* full_url, |
342 PnaclOptions* pnacl_options, | 446 PnaclOptions* pnacl_options, |
343 ErrorInfo* error_info) { | 447 ErrorInfo* error_info) { |
344 CHECK(full_url != NULL && error_info != NULL); | 448 CHECK(full_url != NULL && error_info != NULL); |
345 if (!dictionary.isMember(key)) { | 449 if (!dictionary.isMember(key)) { |
346 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, | 450 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, |
347 "file key not found in manifest"); | 451 "file key not found in manifest"); |
348 return false; | 452 return false; |
349 } | 453 } |
350 const Json::Value& isa_dict = dictionary[key]; | 454 const Json::Value& isa_dict = dictionary[key]; |
351 nacl::string relative_url; | 455 nacl::string relative_url; |
352 if (!GetURLFromISADictionary(isa_dict, key, sandbox_isa, prefer_portable, | 456 if (!GetURLFromISADictionary(isa_dict, key, sandbox_isa, &relative_url, |
353 &relative_url, pnacl_options, error_info)) { | 457 pnacl_options, error_info)) { |
354 return false; | 458 return false; |
355 } | 459 } |
356 return manifest->ResolveURL(relative_url, full_url, error_info); | 460 return manifest->ResolveURL(relative_url, full_url, error_info); |
357 } | 461 } |
358 | 462 |
359 } // namespace | 463 } // namespace |
360 | 464 |
361 bool JsonManifest::Init(const nacl::string& manifest_json, | 465 bool JsonManifest::Init(const nacl::string& manifest_json, |
362 ErrorInfo* error_info) { | 466 ErrorInfo* error_info) { |
363 if (error_info == NULL) { | 467 if (error_info == NULL) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
403 | 507 |
404 // A manifest file must have a program section. | 508 // A manifest file must have a program section. |
405 if (!dictionary_.isMember(kProgramKey)) { | 509 if (!dictionary_.isMember(kProgramKey)) { |
406 error_info->SetReport( | 510 error_info->SetReport( |
407 ERROR_MANIFEST_SCHEMA_VALIDATE, | 511 ERROR_MANIFEST_SCHEMA_VALIDATE, |
408 nacl::string("manifest: missing '") + kProgramKey + "' section."); | 512 nacl::string("manifest: missing '") + kProgramKey + "' section."); |
409 return false; | 513 return false; |
410 } | 514 } |
411 | 515 |
412 // Validate the program section. | 516 // Validate the program section. |
| 517 // There must be a matching (portable or sandbox_isa_) entry for program for |
| 518 // NaCl. |
413 if (!IsValidISADictionary(dictionary_[kProgramKey], | 519 if (!IsValidISADictionary(dictionary_[kProgramKey], |
414 kProgramKey, | 520 kProgramKey, |
415 sandbox_isa_, | 521 sandbox_isa_, |
| 522 true, |
416 error_info)) { | 523 error_info)) { |
417 return false; | 524 return false; |
418 } | 525 } |
419 | 526 |
420 // Validate the interpreter section (if given). | 527 // Validate the interpreter section (if given). |
| 528 // There must be a matching (portable or sandbox_isa_) entry for interpreter |
| 529 // for NaCl. |
421 if (dictionary_.isMember(kInterpreterKey)) { | 530 if (dictionary_.isMember(kInterpreterKey)) { |
422 if (!IsValidISADictionary(dictionary_[kInterpreterKey], | 531 if (!IsValidISADictionary(dictionary_[kInterpreterKey], |
423 kInterpreterKey, | 532 kInterpreterKey, |
424 sandbox_isa_, | 533 sandbox_isa_, |
| 534 true, |
425 error_info)) { | 535 error_info)) { |
426 return false; | 536 return false; |
427 } | 537 } |
428 } | 538 } |
429 | 539 |
430 // Validate the file dictionary (if given). | 540 // Validate the file dictionary (if given). |
| 541 // The "files" key does not require a matching (portable or sandbox_isa_) |
| 542 // entry at schema validation time for NaCl. This allows manifests to specify |
| 543 // resources that are only loaded for a particular sandbox_isa. |
431 if (dictionary_.isMember(kFilesKey)) { | 544 if (dictionary_.isMember(kFilesKey)) { |
432 const Json::Value& files = dictionary_[kFilesKey]; | 545 const Json::Value& files = dictionary_[kFilesKey]; |
433 if (!files.isObject()) { | 546 if (!files.isObject()) { |
434 error_info->SetReport( | 547 error_info->SetReport( |
435 ERROR_MANIFEST_SCHEMA_VALIDATE, | 548 ERROR_MANIFEST_SCHEMA_VALIDATE, |
436 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary."); | 549 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary."); |
437 } | 550 } |
438 Json::Value::Members members = files.getMemberNames(); | 551 Json::Value::Members members = files.getMemberNames(); |
439 for (size_t i = 0; i < members.size(); ++i) { | 552 for (size_t i = 0; i < members.size(); ++i) { |
440 nacl::string file_name = members[i]; | 553 nacl::string file_name = members[i]; |
441 if (!IsValidISADictionary(files[file_name], | 554 if (!IsValidISADictionary(files[file_name], |
442 file_name, | 555 file_name, |
443 nacl::string(), | 556 sandbox_isa_, |
| 557 false, |
444 error_info)) { | 558 error_info)) { |
445 return false; | 559 return false; |
446 } | 560 } |
447 } | 561 } |
448 } | 562 } |
449 | 563 |
450 return true; | 564 return true; |
451 } | 565 } |
452 | 566 |
453 bool JsonManifest::ResolveURL(const nacl::string& relative_url, | 567 bool JsonManifest::ResolveURL(const nacl::string& relative_url, |
(...skipping 23 matching lines...) Expand all Loading... |
477 return false; | 591 return false; |
478 | 592 |
479 Json::Value program = dictionary_[kProgramKey]; | 593 Json::Value program = dictionary_[kProgramKey]; |
480 | 594 |
481 nacl::string nexe_url; | 595 nacl::string nexe_url; |
482 nacl::string error_string; | 596 nacl::string error_string; |
483 | 597 |
484 if (!GetURLFromISADictionary(program, | 598 if (!GetURLFromISADictionary(program, |
485 kProgramKey, | 599 kProgramKey, |
486 sandbox_isa_, | 600 sandbox_isa_, |
487 prefer_portable_, | |
488 &nexe_url, | 601 &nexe_url, |
489 pnacl_options, | 602 pnacl_options, |
490 error_info)) { | 603 error_info)) { |
491 return false; | 604 return false; |
492 } | 605 } |
493 | 606 |
494 return ResolveURL(nexe_url, full_url, error_info); | 607 return ResolveURL(nexe_url, full_url, error_info); |
495 } | 608 } |
496 | 609 |
497 bool JsonManifest::GetFileKeys(std::set<nacl::string>* keys) const { | 610 bool JsonManifest::GetFileKeys(std::set<nacl::string>* keys) const { |
(...skipping 14 matching lines...) Expand all Loading... |
512 nacl::string* full_url, | 625 nacl::string* full_url, |
513 PnaclOptions* pnacl_options, | 626 PnaclOptions* pnacl_options, |
514 ErrorInfo* error_info) const { | 627 ErrorInfo* error_info) const { |
515 NaClLog(3, "JsonManifest::ResolveKey(%s)\n", key.c_str()); | 628 NaClLog(3, "JsonManifest::ResolveKey(%s)\n", key.c_str()); |
516 // key must be one of kProgramKey or kFileKey '/' file-section-key | 629 // key must be one of kProgramKey or kFileKey '/' file-section-key |
517 | 630 |
518 if (full_url == NULL || pnacl_options == NULL || error_info == NULL) | 631 if (full_url == NULL || pnacl_options == NULL || error_info == NULL) |
519 return false; | 632 return false; |
520 | 633 |
521 if (key == kProgramKey) { | 634 if (key == kProgramKey) { |
522 return GetKeyUrl(dictionary_, key, sandbox_isa_, this, prefer_portable_, | 635 return GetKeyUrl(dictionary_, key, sandbox_isa_, this, full_url, |
523 full_url, pnacl_options, error_info); | 636 pnacl_options, error_info); |
524 } | 637 } |
525 nacl::string::const_iterator p = find(key.begin(), key.end(), '/'); | 638 nacl::string::const_iterator p = find(key.begin(), key.end(), '/'); |
526 if (p == key.end()) { | 639 if (p == key.end()) { |
527 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, | 640 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, |
528 nacl::string("ResolveKey: invalid key, no slash: ") | 641 nacl::string("ResolveKey: invalid key, no slash: ") |
529 + key); | 642 + key); |
530 return false; | 643 return false; |
531 } | 644 } |
532 | 645 |
533 // generalize to permit other sections? | 646 // generalize to permit other sections? |
(...skipping 13 matching lines...) Expand all Loading... |
547 ERROR_MANIFEST_RESOLVE_URL, | 660 ERROR_MANIFEST_RESOLVE_URL, |
548 nacl::string("ResolveKey: no \"files\" dictionary")); | 661 nacl::string("ResolveKey: no \"files\" dictionary")); |
549 return false; | 662 return false; |
550 } | 663 } |
551 if (!files.isMember(rest)) { | 664 if (!files.isMember(rest)) { |
552 error_info->SetReport( | 665 error_info->SetReport( |
553 ERROR_MANIFEST_RESOLVE_URL, | 666 ERROR_MANIFEST_RESOLVE_URL, |
554 nacl::string("ResolveKey: no such \"files\" entry: ") + key); | 667 nacl::string("ResolveKey: no such \"files\" entry: ") + key); |
555 return false; | 668 return false; |
556 } | 669 } |
557 return GetKeyUrl(files, rest, sandbox_isa_, this, prefer_portable_, | 670 return GetKeyUrl(files, rest, sandbox_isa_, this, full_url, pnacl_options, |
558 full_url, pnacl_options, error_info); | 671 error_info); |
559 } | 672 } |
560 | 673 |
561 } // namespace plugin | 674 } // namespace plugin |
OLD | NEW |