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