| 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 |