| OLD | NEW |
| (Empty) |
| 1 /* | |
| 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 | |
| 4 * found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h" | |
| 10 | |
| 11 #include <stdlib.h> | |
| 12 | |
| 13 #include "native_client/src/include/nacl_base.h" | |
| 14 #include "native_client/src/include/nacl_macros.h" | |
| 15 #include "native_client/src/include/nacl_string.h" | |
| 16 #include "native_client/src/include/portability.h" | |
| 17 #include "native_client/src/shared/platform/nacl_check.h" | |
| 18 #include "ppapi/c/private/ppb_nacl_private.h" | |
| 19 #include "ppapi/cpp/dev/url_util_dev.h" | |
| 20 #include "ppapi/cpp/var.h" | |
| 21 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" | |
| 22 #include "ppapi/native_client/src/trusted/plugin/utility.h" | |
| 23 #include "third_party/jsoncpp/source/include/json/reader.h" | |
| 24 | |
| 25 namespace plugin { | |
| 26 | |
| 27 namespace { | |
| 28 // Top-level section name keys | |
| 29 const char* const kProgramKey = "program"; | |
| 30 const char* const kInterpreterKey = "interpreter"; | |
| 31 const char* const kFilesKey = "files"; | |
| 32 | |
| 33 // ISA Dictionary keys | |
| 34 const char* const kX8632Key = "x86-32"; | |
| 35 const char* const kX8632NonSFIKey = "x86-32-nonsfi"; | |
| 36 const char* const kX8664Key = "x86-64"; | |
| 37 const char* const kX8664NonSFIKey = "x86-64-nonsfi"; | |
| 38 const char* const kArmKey = "arm"; | |
| 39 const char* const kArmNonSFIKey = "arm-nonsfi"; | |
| 40 const char* const kPortableKey = "portable"; | |
| 41 | |
| 42 // Url Resolution keys | |
| 43 const char* const kPnaclDebugKey = "pnacl-debug"; | |
| 44 const char* const kPnaclTranslateKey = "pnacl-translate"; | |
| 45 const char* const kUrlKey = "url"; | |
| 46 | |
| 47 // PNaCl keys | |
| 48 const char* const kOptLevelKey = "optlevel"; | |
| 49 | |
| 50 // Sample NaCl manifest file: | |
| 51 // { | |
| 52 // "program": { | |
| 53 // "x86-32": {"url": "myprogram_x86-32.nexe"}, | |
| 54 // "x86-64": {"url": "myprogram_x86-64.nexe"}, | |
| 55 // "arm": {"url": "myprogram_arm.nexe"} | |
| 56 // }, | |
| 57 // "interpreter": { | |
| 58 // "x86-32": {"url": "interpreter_x86-32.nexe"}, | |
| 59 // "x86-64": {"url": "interpreter_x86-64.nexe"}, | |
| 60 // "arm": {"url": "interpreter_arm.nexe"} | |
| 61 // }, | |
| 62 // "files": { | |
| 63 // "foo.txt": { | |
| 64 // "portable": {"url": "foo.txt"} | |
| 65 // }, | |
| 66 // "bar.txt": { | |
| 67 // "x86-32": {"url": "x86-32/bar.txt"}, | |
| 68 // "portable": {"url": "bar.txt"} | |
| 69 // }, | |
| 70 // "libfoo.so": { | |
| 71 // "x86-64" : { "url": "..." } | |
| 72 // } | |
| 73 // } | |
| 74 // } | |
| 75 | |
| 76 // Sample PNaCl manifest file: | |
| 77 // { | |
| 78 // "program": { | |
| 79 // "portable": { | |
| 80 // "pnacl-translate": { | |
| 81 // "url": "myprogram.pexe" | |
| 82 // }, | |
| 83 // "pnacl-debug": { | |
| 84 // "url": "myprogram.debug.pexe", | |
| 85 // "opt_level": 0 | |
| 86 // } | |
| 87 // } | |
| 88 // }, | |
| 89 // "files": { | |
| 90 // "foo.txt": { | |
| 91 // "portable": {"url": "foo.txt"} | |
| 92 // }, | |
| 93 // "bar.txt": { | |
| 94 // "portable": {"url": "bar.txt"} | |
| 95 // } | |
| 96 // } | |
| 97 // } | |
| 98 | |
| 99 // Returns the key for the architecture in non-SFI mode. | |
| 100 nacl::string GetNonSFIKey(const nacl::string& sandbox_isa) { | |
| 101 return sandbox_isa + "-nonsfi"; | |
| 102 } | |
| 103 | |
| 104 // Looks up |property_name| in the vector |valid_names| with length | |
| 105 // |valid_name_count|. Returns true if |property_name| is found. | |
| 106 bool FindMatchingProperty(const nacl::string& property_name, | |
| 107 const char** valid_names, | |
| 108 size_t valid_name_count) { | |
| 109 for (size_t i = 0; i < valid_name_count; ++i) { | |
| 110 if (property_name == valid_names[i]) { | |
| 111 return true; | |
| 112 } | |
| 113 } | |
| 114 return false; | |
| 115 } | |
| 116 | |
| 117 // Return true if this is a valid dictionary. Having only keys present in | |
| 118 // |valid_keys| and having at least the keys in |required_keys|. | |
| 119 // Error messages will be placed in |error_string|, given that the dictionary | |
| 120 // was the property value of |container_key|. | |
| 121 // E.g., "container_key" : dictionary | |
| 122 bool IsValidDictionary(const Json::Value& dictionary, | |
| 123 const nacl::string& container_key, | |
| 124 const nacl::string& parent_key, | |
| 125 const char** valid_keys, | |
| 126 size_t valid_key_count, | |
| 127 const char** required_keys, | |
| 128 size_t required_key_count, | |
| 129 nacl::string* error_string) { | |
| 130 if (!dictionary.isObject()) { | |
| 131 nacl::stringstream error_stream; | |
| 132 error_stream << parent_key << " property '" << container_key | |
| 133 << "' is non-dictionary value '" | |
| 134 << dictionary.toStyledString() << "'."; | |
| 135 *error_string = error_stream.str(); | |
| 136 return false; | |
| 137 } | |
| 138 // Check for unknown dictionary members. | |
| 139 Json::Value::Members members = dictionary.getMemberNames(); | |
| 140 for (size_t i = 0; i < members.size(); ++i) { | |
| 141 nacl::string property_name = members[i]; | |
| 142 if (!FindMatchingProperty(property_name, | |
| 143 valid_keys, | |
| 144 valid_key_count)) { | |
| 145 // For forward compatibility, we do not prohibit other keys being in | |
| 146 // the dictionary. | |
| 147 PLUGIN_PRINTF(("WARNING: '%s' property '%s' has unknown key '%s'.\n", | |
| 148 parent_key.c_str(), | |
| 149 container_key.c_str(), property_name.c_str())); | |
| 150 } | |
| 151 } | |
| 152 // Check for required members. | |
| 153 for (size_t i = 0; i < required_key_count; ++i) { | |
| 154 if (!dictionary.isMember(required_keys[i])) { | |
| 155 nacl::stringstream error_stream; | |
| 156 error_stream << parent_key << " property '" << container_key | |
| 157 << "' does not have required key: '" | |
| 158 << required_keys[i] << "'."; | |
| 159 *error_string = error_stream.str(); | |
| 160 return false; | |
| 161 } | |
| 162 } | |
| 163 return true; | |
| 164 } | |
| 165 | |
| 166 // Validate a "url" dictionary assuming it was resolved from container_key. | |
| 167 // E.g., "container_key" : { "url": "foo.txt" } | |
| 168 bool IsValidUrlSpec(const Json::Value& url_spec, | |
| 169 const nacl::string& container_key, | |
| 170 const nacl::string& parent_key, | |
| 171 const nacl::string& sandbox_isa, | |
| 172 nacl::string* error_string) { | |
| 173 static const char* kManifestUrlSpecRequired[] = { | |
| 174 kUrlKey | |
| 175 }; | |
| 176 const char** urlSpecPlusOptional; | |
| 177 size_t urlSpecPlusOptionalLength; | |
| 178 if (sandbox_isa == kPortableKey) { | |
| 179 static const char* kPnaclUrlSpecPlusOptional[] = { | |
| 180 kUrlKey, | |
| 181 kOptLevelKey, | |
| 182 }; | |
| 183 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional; | |
| 184 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kPnaclUrlSpecPlusOptional); | |
| 185 } else { | |
| 186 // URL specifications must not contain "pnacl-translate" keys. | |
| 187 // This prohibits NaCl clients from invoking PNaCl. | |
| 188 if (url_spec.isMember(kPnaclTranslateKey)) { | |
| 189 nacl::stringstream error_stream; | |
| 190 error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead " | |
| 191 << "of x-pnacl mimetype (has " << kPnaclTranslateKey << ")."; | |
| 192 *error_string = error_stream.str(); | |
| 193 return false; | |
| 194 } | |
| 195 urlSpecPlusOptional = kManifestUrlSpecRequired; | |
| 196 urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kManifestUrlSpecRequired); | |
| 197 } | |
| 198 if (!IsValidDictionary(url_spec, container_key, parent_key, | |
| 199 urlSpecPlusOptional, | |
| 200 urlSpecPlusOptionalLength, | |
| 201 kManifestUrlSpecRequired, | |
| 202 NACL_ARRAY_SIZE(kManifestUrlSpecRequired), | |
| 203 error_string)) { | |
| 204 return false; | |
| 205 } | |
| 206 // Verify the correct types of the fields if they exist. | |
| 207 Json::Value url = url_spec[kUrlKey]; | |
| 208 if (!url.isString()) { | |
| 209 nacl::stringstream error_stream; | |
| 210 error_stream << parent_key << " property '" << container_key << | |
| 211 "' has non-string value '" << url.toStyledString() << | |
| 212 "' for key '" << kUrlKey << "'."; | |
| 213 *error_string = error_stream.str(); | |
| 214 return false; | |
| 215 } | |
| 216 Json::Value opt_level = url_spec[kOptLevelKey]; | |
| 217 if (!opt_level.empty() && !opt_level.isNumeric()) { | |
| 218 nacl::stringstream error_stream; | |
| 219 error_stream << parent_key << " property '" << container_key << | |
| 220 "' has non-numeric value '" << opt_level.toStyledString() << | |
| 221 "' for key '" << kOptLevelKey << "'."; | |
| 222 *error_string = error_stream.str(); | |
| 223 return false; | |
| 224 } | |
| 225 return true; | |
| 226 } | |
| 227 | |
| 228 // Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming | |
| 229 // it was resolved from container_key. | |
| 230 // E.g., "container_key" : { "pnacl-translate" : URLSpec } | |
| 231 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec, | |
| 232 const nacl::string& container_key, | |
| 233 const nacl::string& parent_key, | |
| 234 const nacl::string& sandbox_isa, | |
| 235 nacl::string* error_string) { | |
| 236 static const char* kManifestPnaclSpecValid[] = { | |
| 237 kPnaclDebugKey, | |
| 238 kPnaclTranslateKey | |
| 239 }; | |
| 240 static const char* kManifestPnaclSpecRequired[] = { | |
| 241 kPnaclTranslateKey | |
| 242 }; | |
| 243 if (!IsValidDictionary(pnacl_spec, container_key, parent_key, | |
| 244 kManifestPnaclSpecValid, | |
| 245 NACL_ARRAY_SIZE(kManifestPnaclSpecValid), | |
| 246 kManifestPnaclSpecRequired, | |
| 247 NACL_ARRAY_SIZE(kManifestPnaclSpecRequired), | |
| 248 error_string)) { | |
| 249 return false; | |
| 250 } | |
| 251 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey]; | |
| 252 if (!IsValidUrlSpec(url_spec, kPnaclTranslateKey, | |
| 253 container_key, sandbox_isa, error_string)) { | |
| 254 return false; | |
| 255 } | |
| 256 return true; | |
| 257 } | |
| 258 | |
| 259 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary | |
| 260 // is validated to have keys from within the set of recognized ISAs. Unknown | |
| 261 // ISAs are allowed, but ignored and warnings are produced. It is also validated | |
| 262 // that it must have an entry to match the ISA specified in |sandbox_isa| or | |
| 263 // have a fallback 'portable' entry if there is no match. Returns true if | |
| 264 // |dictionary| is an ISA to URL map. Sets |error_info| to something | |
| 265 // descriptive if it fails. | |
| 266 bool IsValidISADictionary(const Json::Value& dictionary, | |
| 267 const nacl::string& parent_key, | |
| 268 const nacl::string& sandbox_isa, | |
| 269 bool must_find_matching_entry, | |
| 270 bool nonsfi_enabled, | |
| 271 ErrorInfo* error_info) { | |
| 272 if (error_info == NULL) return false; | |
| 273 | |
| 274 // An ISA to URL dictionary has to be an object. | |
| 275 if (!dictionary.isObject()) { | |
| 276 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 277 nacl::string("manifest: ") + parent_key + | |
| 278 " property is not an ISA to URL dictionary"); | |
| 279 return false; | |
| 280 } | |
| 281 // Build the set of reserved ISA dictionary keys. | |
| 282 const char** isaProperties; | |
| 283 size_t isaPropertiesLength; | |
| 284 if (sandbox_isa == kPortableKey) { | |
| 285 // The known values for PNaCl ISA dictionaries in the manifest. | |
| 286 static const char* kPnaclManifestISAProperties[] = { | |
| 287 kPortableKey | |
| 288 }; | |
| 289 isaProperties = kPnaclManifestISAProperties; | |
| 290 isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties); | |
| 291 } else { | |
| 292 // The known values for NaCl ISA dictionaries in the manifest. | |
| 293 static const char* kNaClManifestISAProperties[] = { | |
| 294 kX8632Key, | |
| 295 kX8632NonSFIKey, | |
| 296 kX8664Key, | |
| 297 kX8664NonSFIKey, | |
| 298 kArmKey, | |
| 299 kArmNonSFIKey, | |
| 300 // "portable" is here to allow checking that, if present, it can | |
| 301 // only refer to an URL, such as for a data file, and not to | |
| 302 // "pnacl-translate", which would cause the creation of a nexe. | |
| 303 kPortableKey | |
| 304 }; | |
| 305 isaProperties = kNaClManifestISAProperties; | |
| 306 isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties); | |
| 307 } | |
| 308 // Check that entries in the dictionary are structurally correct. | |
| 309 Json::Value::Members members = dictionary.getMemberNames(); | |
| 310 for (size_t i = 0; i < members.size(); ++i) { | |
| 311 nacl::string property_name = members[i]; | |
| 312 Json::Value property_value = dictionary[property_name]; | |
| 313 nacl::string error_string; | |
| 314 if (FindMatchingProperty(property_name, | |
| 315 isaProperties, | |
| 316 isaPropertiesLength)) { | |
| 317 // For NaCl, arch entries can only be | |
| 318 // "arch/portable" : URLSpec | |
| 319 // For PNaCl arch in "program" dictionary entries can be | |
| 320 // "portable" : { "pnacl-translate": URLSpec } | |
| 321 // or "portable" : { "pnacl-debug": URLSpec } | |
| 322 // For PNaCl arch elsewhere, dictionary entries can only be | |
| 323 // "portable" : URLSpec | |
| 324 if ((sandbox_isa != kPortableKey && | |
| 325 !IsValidUrlSpec(property_value, property_name, parent_key, | |
| 326 sandbox_isa, &error_string)) || | |
| 327 (sandbox_isa == kPortableKey && | |
| 328 parent_key == kProgramKey && | |
| 329 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key, | |
| 330 sandbox_isa, &error_string)) || | |
| 331 (sandbox_isa == kPortableKey && | |
| 332 parent_key != kProgramKey && | |
| 333 !IsValidUrlSpec(property_value, property_name, parent_key, | |
| 334 sandbox_isa, &error_string))) { | |
| 335 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 336 nacl::string("manifest: ") + error_string); | |
| 337 return false; | |
| 338 } | |
| 339 } else { | |
| 340 // For forward compatibility, we do not prohibit other keys being in | |
| 341 // the dictionary, as they may be architectures supported in later | |
| 342 // versions. However, the value of these entries must be an URLSpec. | |
| 343 PLUGIN_PRINTF(("IsValidISADictionary: unrecognized key '%s'.\n", | |
| 344 property_name.c_str())); | |
| 345 if (!IsValidUrlSpec(property_value, property_name, parent_key, | |
| 346 sandbox_isa, &error_string)) { | |
| 347 error_info->SetReport(PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 348 nacl::string("manifest: ") + error_string); | |
| 349 return false; | |
| 350 } | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 if (sandbox_isa == kPortableKey) { | |
| 355 bool has_portable = dictionary.isMember(kPortableKey); | |
| 356 | |
| 357 if (!has_portable) { | |
| 358 error_info->SetReport( | |
| 359 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, | |
| 360 nacl::string("manifest: no version of ") + parent_key + | |
| 361 " given for portable."); | |
| 362 return false; | |
| 363 } | |
| 364 } else if (must_find_matching_entry) { | |
| 365 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include | |
| 366 // micro-architectures that can resolve to multiple valid sandboxes. | |
| 367 bool has_isa = dictionary.isMember(sandbox_isa); | |
| 368 bool has_nonsfi_isa = | |
| 369 nonsfi_enabled && dictionary.isMember(GetNonSFIKey(sandbox_isa)); | |
| 370 bool has_portable = dictionary.isMember(kPortableKey); | |
| 371 | |
| 372 if (!has_isa && !has_nonsfi_isa && !has_portable) { | |
| 373 error_info->SetReport( | |
| 374 PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH, | |
| 375 nacl::string("manifest: no version of ") + parent_key + | |
| 376 " given for current arch and no portable version found."); | |
| 377 return false; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 return true; | |
| 382 } | |
| 383 | |
| 384 void GrabUrlAndPNaClOptions(const Json::Value& url_spec, | |
| 385 nacl::string* url, | |
| 386 PP_PNaClOptions* pnacl_options) { | |
| 387 *url = url_spec[kUrlKey].asString(); | |
| 388 pnacl_options->translate = PP_TRUE; | |
| 389 if (url_spec.isMember(kOptLevelKey)) { | |
| 390 int32_t opt_raw = url_spec[kOptLevelKey].asInt(); | |
| 391 int32_t opt_level; | |
| 392 // Currently only allow 0 or 2, since that is what we test. | |
| 393 if (opt_raw <= 0) | |
| 394 opt_level = 0; | |
| 395 else | |
| 396 opt_level = 2; | |
| 397 pnacl_options->opt_level = opt_level; | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 } // namespace | |
| 402 | |
| 403 bool JsonManifest::Init(const nacl::string& manifest_json, | |
| 404 ErrorInfo* error_info) { | |
| 405 if (error_info == NULL) { | |
| 406 return false; | |
| 407 } | |
| 408 Json::Reader reader; | |
| 409 if (!reader.parse(manifest_json, dictionary_)) { | |
| 410 std::string json_error = reader.getFormatedErrorMessages(); | |
| 411 error_info->SetReport(PP_NACL_ERROR_MANIFEST_PARSING, | |
| 412 "manifest JSON parsing failed: " + json_error); | |
| 413 return false; | |
| 414 } | |
| 415 // Parse has ensured the string was valid JSON. Check that it matches the | |
| 416 // manifest schema. | |
| 417 return MatchesSchema(error_info); | |
| 418 } | |
| 419 | |
| 420 bool JsonManifest::MatchesSchema(ErrorInfo* error_info) { | |
| 421 pp::Var exception; | |
| 422 if (error_info == NULL) { | |
| 423 return false; | |
| 424 } | |
| 425 if (!dictionary_.isObject()) { | |
| 426 error_info->SetReport( | |
| 427 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 428 "manifest: is not a json dictionary."); | |
| 429 return false; | |
| 430 } | |
| 431 Json::Value::Members members = dictionary_.getMemberNames(); | |
| 432 for (size_t i = 0; i < members.size(); ++i) { | |
| 433 // The top level dictionary entries valid in the manifest file. | |
| 434 static const char* kManifestTopLevelProperties[] = { kProgramKey, | |
| 435 kInterpreterKey, | |
| 436 kFilesKey }; | |
| 437 nacl::string property_name = members[i]; | |
| 438 if (!FindMatchingProperty(property_name, | |
| 439 kManifestTopLevelProperties, | |
| 440 NACL_ARRAY_SIZE(kManifestTopLevelProperties))) { | |
| 441 PLUGIN_PRINTF(("JsonManifest::MatchesSchema: WARNING: unknown top-level " | |
| 442 "section '%s' in manifest.\n", property_name.c_str())); | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 // A manifest file must have a program section. | |
| 447 if (!dictionary_.isMember(kProgramKey)) { | |
| 448 error_info->SetReport( | |
| 449 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 450 nacl::string("manifest: missing '") + kProgramKey + "' section."); | |
| 451 return false; | |
| 452 } | |
| 453 | |
| 454 // Validate the program section. | |
| 455 // There must be a matching (portable or sandbox_isa_) entry for program for | |
| 456 // NaCl. | |
| 457 if (!IsValidISADictionary(dictionary_[kProgramKey], | |
| 458 kProgramKey, | |
| 459 sandbox_isa_, | |
| 460 true, | |
| 461 nonsfi_enabled_, | |
| 462 error_info)) { | |
| 463 return false; | |
| 464 } | |
| 465 | |
| 466 // Validate the interpreter section (if given). | |
| 467 // There must be a matching (portable or sandbox_isa_) entry for interpreter | |
| 468 // for NaCl. | |
| 469 if (dictionary_.isMember(kInterpreterKey)) { | |
| 470 if (!IsValidISADictionary(dictionary_[kInterpreterKey], | |
| 471 kInterpreterKey, | |
| 472 sandbox_isa_, | |
| 473 true, | |
| 474 nonsfi_enabled_, | |
| 475 error_info)) { | |
| 476 return false; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 // Validate the file dictionary (if given). | |
| 481 // The "files" key does not require a matching (portable or sandbox_isa_) | |
| 482 // entry at schema validation time for NaCl. This allows manifests to specify | |
| 483 // resources that are only loaded for a particular sandbox_isa. | |
| 484 if (dictionary_.isMember(kFilesKey)) { | |
| 485 const Json::Value& files = dictionary_[kFilesKey]; | |
| 486 if (!files.isObject()) { | |
| 487 error_info->SetReport( | |
| 488 PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE, | |
| 489 nacl::string("manifest: '") + kFilesKey + "' is not a dictionary."); | |
| 490 } | |
| 491 Json::Value::Members members = files.getMemberNames(); | |
| 492 for (size_t i = 0; i < members.size(); ++i) { | |
| 493 nacl::string file_name = members[i]; | |
| 494 if (!IsValidISADictionary(files[file_name], | |
| 495 file_name, | |
| 496 sandbox_isa_, | |
| 497 false, | |
| 498 nonsfi_enabled_, | |
| 499 error_info)) { | |
| 500 return false; | |
| 501 } | |
| 502 } | |
| 503 } | |
| 504 | |
| 505 return true; | |
| 506 } | |
| 507 | |
| 508 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary, | |
| 509 const nacl::string& parent_key, | |
| 510 nacl::string* url, | |
| 511 PP_PNaClOptions* pnacl_options, | |
| 512 bool* uses_nonsfi_mode, | |
| 513 ErrorInfo* error_info) const { | |
| 514 DCHECK(url != NULL && pnacl_options != NULL && error_info != NULL); | |
| 515 | |
| 516 // When the application actually requests a resolved URL, we must have | |
| 517 // a matching entry (sandbox_isa_ or portable) for NaCl. | |
| 518 ErrorInfo ignored_error_info; | |
| 519 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true, | |
| 520 nonsfi_enabled_, &ignored_error_info)) { | |
| 521 error_info->SetReport(PP_NACL_ERROR_MANIFEST_RESOLVE_URL, | |
| 522 "architecture " + sandbox_isa_ + | |
| 523 " is not found for file " + parent_key); | |
| 524 return false; | |
| 525 } | |
| 526 | |
| 527 // The call to IsValidISADictionary() above guarantees that either | |
| 528 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the | |
| 529 // dictionary. | |
| 530 *uses_nonsfi_mode = false; | |
| 531 nacl::string chosen_isa; | |
| 532 if (sandbox_isa_ == kPortableKey) { | |
| 533 chosen_isa = kPortableKey; | |
| 534 } else { | |
| 535 nacl::string nonsfi_isa = GetNonSFIKey(sandbox_isa_); | |
| 536 if (nonsfi_enabled_ && dictionary.isMember(nonsfi_isa)) { | |
| 537 chosen_isa = nonsfi_isa; | |
| 538 *uses_nonsfi_mode = true; | |
| 539 } else if (dictionary.isMember(sandbox_isa_)) { | |
| 540 chosen_isa = sandbox_isa_; | |
| 541 } else if (dictionary.isMember(kPortableKey)) { | |
| 542 chosen_isa = kPortableKey; | |
| 543 } else { | |
| 544 // Should not reach here, because the earlier IsValidISADictionary() | |
| 545 // call checked that the manifest covers the current architecture. | |
| 546 DCHECK(false); | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 const Json::Value& isa_spec = dictionary[chosen_isa]; | |
| 551 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first. | |
| 552 // If found, mark that it is a debug URL. Otherwise, fall back to | |
| 553 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL. | |
| 554 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) { | |
| 555 GrabUrlAndPNaClOptions(isa_spec[kPnaclDebugKey], url, pnacl_options); | |
| 556 pnacl_options->is_debug = PP_TRUE; | |
| 557 } else if (isa_spec.isMember(kPnaclTranslateKey)) { | |
| 558 GrabUrlAndPNaClOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); | |
| 559 } else { | |
| 560 // NaCl | |
| 561 *url = isa_spec[kUrlKey].asString(); | |
| 562 pnacl_options->translate = PP_FALSE; | |
| 563 } | |
| 564 | |
| 565 return true; | |
| 566 } | |
| 567 | |
| 568 bool JsonManifest::GetKeyUrl(const Json::Value& dictionary, | |
| 569 const nacl::string& key, | |
| 570 nacl::string* full_url, | |
| 571 PP_PNaClOptions* pnacl_options) const { | |
| 572 DCHECK(full_url != NULL && pnacl_options != NULL); | |
| 573 if (!dictionary.isMember(key)) { | |
| 574 PLUGIN_PRINTF(("file key not found in manifest")); | |
| 575 return false; | |
| 576 } | |
| 577 const Json::Value& isa_dict = dictionary[key]; | |
| 578 nacl::string relative_url; | |
| 579 bool uses_nonsfi_mode; | |
| 580 | |
| 581 // We ignore the error_info we receive here but it's needed for the calls | |
| 582 // below. | |
| 583 ErrorInfo error_info; | |
| 584 | |
| 585 if (!GetURLFromISADictionary(isa_dict, key, &relative_url, | |
| 586 pnacl_options, &uses_nonsfi_mode, &error_info)) { | |
| 587 return false; | |
| 588 } | |
| 589 return ResolveURL(relative_url, full_url, &error_info); | |
| 590 } | |
| 591 | |
| 592 bool JsonManifest::GetProgramURL(nacl::string* full_url, | |
| 593 PP_PNaClOptions* pnacl_options, | |
| 594 bool* uses_nonsfi_mode, | |
| 595 ErrorInfo* error_info) const { | |
| 596 if (full_url == NULL || pnacl_options == NULL || error_info == NULL) | |
| 597 return false; | |
| 598 | |
| 599 const Json::Value& program = dictionary_[kProgramKey]; | |
| 600 nacl::string nexe_url; | |
| 601 if (!GetURLFromISADictionary(program, | |
| 602 kProgramKey, | |
| 603 &nexe_url, | |
| 604 pnacl_options, | |
| 605 uses_nonsfi_mode, | |
| 606 error_info)) { | |
| 607 return false; | |
| 608 } | |
| 609 return ResolveURL(nexe_url, full_url, error_info); | |
| 610 } | |
| 611 | |
| 612 bool JsonManifest::ResolveKey(const nacl::string& key, | |
| 613 nacl::string* full_url, | |
| 614 PP_PNaClOptions* pnacl_options) const { | |
| 615 NaClLog(3, "JsonManifest::ResolveKey(%s)\n", key.c_str()); | |
| 616 // key must be one of kProgramKey or kFileKey '/' file-section-key | |
| 617 | |
| 618 if (full_url == NULL || pnacl_options == NULL) | |
| 619 return false; | |
| 620 | |
| 621 if (key == kProgramKey) | |
| 622 return GetKeyUrl(dictionary_, key, full_url, pnacl_options); | |
| 623 nacl::string::const_iterator p = find(key.begin(), key.end(), '/'); | |
| 624 if (p == key.end()) { | |
| 625 std::string err = "ResolveKey: invalid key, no slash: " + key; | |
| 626 PLUGIN_PRINTF((err.c_str())); | |
| 627 return false; | |
| 628 } | |
| 629 | |
| 630 // generalize to permit other sections? | |
| 631 nacl::string prefix(key.begin(), p); | |
| 632 if (prefix != kFilesKey) { | |
| 633 std::string err = "ResolveKey: invalid key: no \"files\" prefix: " + key; | |
| 634 PLUGIN_PRINTF((err.c_str())); | |
| 635 return false; | |
| 636 } | |
| 637 | |
| 638 nacl::string rest(p + 1, key.end()); | |
| 639 | |
| 640 const Json::Value& files = dictionary_[kFilesKey]; | |
| 641 if (!files.isObject()) { | |
| 642 std::string err = "ResolveKey: no \"files\" dictionary"; | |
| 643 PLUGIN_PRINTF((err.c_str())); | |
| 644 return false; | |
| 645 } | |
| 646 if (!files.isMember(rest)) { | |
| 647 std::string err = "ResolveKey: no such \"files\" entry: " + key; | |
| 648 PLUGIN_PRINTF((err.c_str())); | |
| 649 return false; | |
| 650 } | |
| 651 return GetKeyUrl(files, rest, full_url, pnacl_options); | |
| 652 } | |
| 653 | |
| 654 bool JsonManifest::ResolveURL(const nacl::string& relative_url, | |
| 655 nacl::string* full_url, | |
| 656 ErrorInfo* error_info) const { | |
| 657 // The contents of the manifest are resolved relative to the manifest URL. | |
| 658 CHECK(url_util_ != NULL); | |
| 659 pp::Var resolved_url = | |
| 660 url_util_->ResolveRelativeToURL(pp::Var(manifest_base_url_), | |
| 661 relative_url); | |
| 662 if (!resolved_url.is_string()) { | |
| 663 error_info->SetReport( | |
| 664 PP_NACL_ERROR_MANIFEST_RESOLVE_URL, | |
| 665 "could not resolve url '" + relative_url + | |
| 666 "' relative to manifest base url '" + manifest_base_url_.c_str() + | |
| 667 "'."); | |
| 668 return false; | |
| 669 } | |
| 670 *full_url = resolved_url.AsString(); | |
| 671 return true; | |
| 672 } | |
| 673 | |
| 674 } // namespace plugin | |
| OLD | NEW |