• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 "base/json/json_reader.h"
6 
7 #include <string_view>
8 #include <utility>
9 
10 #include "base/features.h"
11 #include "base/json/json_parser.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "build/build_config.h"
15 
16 #if !BUILDFLAG(IS_NACL)
17 #include "base/strings/string_view_rust.h"
18 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/functions.h"
19 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs.h"
20 #endif
21 
22 namespace base {
23 
24 // TODO(crbug.com/40811643): Move the C++ parser into components/nacl to just
25 // run in-process there. Don't compile base::JSONReader on NaCL at all.
26 #if !BUILDFLAG(IS_NACL)
27 
28 namespace {
29 using serde_json_lenient::ContextPointer;
30 
31 const char kSecurityJsonParsingTime[] = "Security.JSONParser.ParsingTime";
32 
ListAppendList(ContextPointer & ctx)33 ContextPointer& ListAppendList(ContextPointer& ctx) {
34   auto& value = reinterpret_cast<base::Value&>(ctx);
35   value.GetList().Append(base::Value::List());
36   return reinterpret_cast<ContextPointer&>(value.GetList().back());
37 }
38 
ListAppendDict(ContextPointer & ctx)39 ContextPointer& ListAppendDict(ContextPointer& ctx) {
40   auto& value = reinterpret_cast<base::Value&>(ctx);
41   value.GetList().Append(base::Value::Dict());
42   return reinterpret_cast<ContextPointer&>(value.GetList().back());
43 }
44 
ListAppendNone(ContextPointer & ctx)45 void ListAppendNone(ContextPointer& ctx) {
46   auto& value = reinterpret_cast<base::Value&>(ctx);
47   value.GetList().Append(base::Value());
48 }
49 
50 template <class T, class As = T>
ListAppendValue(ContextPointer & ctx,T v)51 void ListAppendValue(ContextPointer& ctx, T v) {
52   auto& value = reinterpret_cast<base::Value&>(ctx);
53   value.GetList().Append(As{v});
54 }
55 
DictSetList(ContextPointer & ctx,rust::Str key)56 ContextPointer& DictSetList(ContextPointer& ctx, rust::Str key) {
57   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
58   base::Value* value =
59       dict.Set(base::RustStrToStringView(key), base::Value::List());
60   return reinterpret_cast<ContextPointer&>(*value);
61 }
62 
DictSetDict(ContextPointer & ctx,rust::Str key)63 ContextPointer& DictSetDict(ContextPointer& ctx, rust::Str key) {
64   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
65   base::Value* value =
66       dict.Set(base::RustStrToStringView(key), base::Value::Dict());
67   return reinterpret_cast<ContextPointer&>(*value);
68 }
69 
DictSetNone(ContextPointer & ctx,rust::Str key)70 void DictSetNone(ContextPointer& ctx, rust::Str key) {
71   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
72   dict.Set(base::RustStrToStringView(key), base::Value());
73 }
74 
75 template <class T, class As = T>
DictSetValue(ContextPointer & ctx,rust::Str key,T v)76 void DictSetValue(ContextPointer& ctx, rust::Str key, T v) {
77   auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
78   dict.Set(base::RustStrToStringView(key), base::Value(As{v}));
79 }
80 
DecodeJSONInRust(std::string_view json,int options,size_t max_depth)81 JSONReader::Result DecodeJSONInRust(std::string_view json,
82                                     int options,
83                                     size_t max_depth) {
84   const serde_json_lenient::JsonOptions rust_options = {
85       .allow_trailing_commas =
86           (options & base::JSON_ALLOW_TRAILING_COMMAS) != 0,
87       .replace_invalid_characters =
88           (options & base::JSON_REPLACE_INVALID_CHARACTERS) != 0,
89       .allow_comments = (options & base::JSON_ALLOW_COMMENTS) != 0,
90       .allow_newlines = (options & base::JSON_ALLOW_NEWLINES_IN_STRINGS) != 0,
91       .allow_control_chars = (options & base::JSON_ALLOW_CONTROL_CHARS) != 0,
92       .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0,
93       .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0,
94       .max_depth = max_depth,
95   };
96   static constexpr serde_json_lenient::Functions functions = {
97       .list_append_none_fn = ListAppendNone,
98       .list_append_bool_fn = ListAppendValue<bool>,
99       .list_append_i32_fn = ListAppendValue<int32_t>,
100       .list_append_f64_fn = ListAppendValue<double>,
101       .list_append_str_fn = ListAppendValue<rust::Str, std::string>,
102       .list_append_list_fn = ListAppendList,
103       .list_append_dict_fn = ListAppendDict,
104       .dict_set_none_fn = DictSetNone,
105       .dict_set_bool_fn = DictSetValue<bool>,
106       .dict_set_i32_fn = DictSetValue<int32_t>,
107       .dict_set_f64_fn = DictSetValue<double>,
108       .dict_set_str_fn = DictSetValue<rust::Str, std::string>,
109       .dict_set_list_fn = DictSetList,
110       .dict_set_dict_fn = DictSetDict,
111   };
112 
113   base::Value value(base::Value::Type::LIST);
114   auto& ctx = reinterpret_cast<ContextPointer&>(value);
115   serde_json_lenient::DecodeError error;
116   bool ok = serde_json_lenient::decode_json(
117       base::StringViewToRustSlice(json), rust_options, functions, ctx, error);
118 
119   if (!ok) {
120     return base::unexpected(base::JSONReader::Error{
121         .message = std::string(error.message),
122         .line = error.line,
123         .column = error.column,
124     });
125   }
126 
127   return std::move(std::move(value.GetList()).back());
128 }
129 
130 }  // anonymous namespace
131 
132 #endif  // !BUILDFLAG(IS_NACL)
133 
134 // static
Read(std::string_view json,int options,size_t max_depth)135 std::optional<Value> JSONReader::Read(std::string_view json,
136                                       int options,
137                                       size_t max_depth) {
138 #if BUILDFLAG(IS_NACL)
139   internal::JSONParser parser(options, max_depth);
140   return parser.Parse(json);
141 #else   // BUILDFLAG(IS_NACL)
142   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
143   if (UsingRust()) {
144     JSONReader::Result result = DecodeJSONInRust(json, options, max_depth);
145     if (!result.has_value()) {
146       return std::nullopt;
147     }
148     return std::move(*result);
149   } else {
150     internal::JSONParser parser(options, max_depth);
151     return parser.Parse(json);
152   }
153 #endif  // BUILDFLAG(IS_NACL)
154 }
155 
156 // static
ReadDict(std::string_view json,int options,size_t max_depth)157 std::optional<Value::Dict> JSONReader::ReadDict(std::string_view json,
158                                                 int options,
159                                                 size_t max_depth) {
160   std::optional<Value> value = Read(json, options, max_depth);
161   if (!value || !value->is_dict()) {
162     return std::nullopt;
163   }
164   return std::move(*value).TakeDict();
165 }
166 
167 // static
ReadAndReturnValueWithError(std::string_view json,int options)168 JSONReader::Result JSONReader::ReadAndReturnValueWithError(
169     std::string_view json,
170     int options) {
171 #if BUILDFLAG(IS_NACL)
172   internal::JSONParser parser(options);
173   auto value = parser.Parse(json);
174   if (!value) {
175     Error error;
176     error.message = parser.GetErrorMessage();
177     error.line = parser.error_line();
178     error.column = parser.error_column();
179     return base::unexpected(std::move(error));
180   }
181 
182   return std::move(*value);
183 #else   // BUILDFLAG(IS_NACL)
184   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
185   if (UsingRust()) {
186     return DecodeJSONInRust(json, options, internal::kAbsoluteMaxDepth);
187   } else {
188     internal::JSONParser parser(options);
189     auto value = parser.Parse(json);
190     if (!value) {
191       Error error;
192       error.message = parser.GetErrorMessage();
193       error.line = parser.error_line();
194       error.column = parser.error_column();
195       return base::unexpected(std::move(error));
196     }
197 
198     return std::move(*value);
199   }
200 #endif  // BUILDFLAG(IS_NACL)
201 }
202 
203 // static
UsingRust()204 bool JSONReader::UsingRust() {
205   // If features have not yet been enabled, we cannot check the feature, so fall
206   // back to the C++ parser. In practice, this seems to apply to
207   // `ReadPrefsFromDisk()`, which is parsing trusted JSON.
208   if (!base::FeatureList::GetInstance()) {
209     return false;
210   }
211 #if BUILDFLAG(IS_NACL)
212   return false;
213 #else
214   return base::FeatureList::IsEnabled(base::features::kUseRustJsonParser);
215 #endif
216 }
217 
218 }  // namespace base
219