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