已开启
fix: WantParams ToString/Serialize crash on invalid UTF-8 bytes #875
SkyQAQ创建于 8 天前
fix: WantParams ToString/Serialize crash on invalid UTF-8 bytes #875
已开启
共 10 个文件变更+153-7
| @@ -238,7 +238,7 @@ const std::string Configuration::GetName() const | |||
| 238 | json configArray(configParameter_); | 238 | json configArray(configParameter_); |
| 239 | std::string configArrayStr = ""; | 239 | std::string configArrayStr = ""; |
| 240 | try { | 240 | try { |
| 241 | - configArrayStr = configArray.dump(); | 241 | + configArrayStr = configArray.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore); |
| 242 | } catch (...) { | 242 | } catch (...) { |
| 243 | ABILITYBASE_LOGE("failed to dump GetName"); | 243 | ABILITYBASE_LOGE("failed to dump GetName"); |
| 244 | } | 244 | } |
| @@ -98,7 +98,7 @@ std::string PageNodeInfo::ToJsonString() const | |||
| 98 | {PAGE_NODE_INFO_RECT, rect.ToJsonString()}, | 98 | {PAGE_NODE_INFO_RECT, rect.ToJsonString()}, |
| 99 | {PAGE_NODE_INFO_IS_FOCUS, isFocus} | 99 | {PAGE_NODE_INFO_IS_FOCUS, isFocus} |
| 100 | }; | 100 | }; |
| 101 | - return jsonObject.dump(); | 101 | + return jsonObject.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore); |
| 102 | } | 102 | } |
| 103 | } // namespace AbilityBase | 103 | } // namespace AbilityBase |
| 104 | } // namespace OHOS | 104 | } // namespace OHOS |
| @@ -89,7 +89,7 @@ std::string ViewData::ToJsonString() const | |||
| 89 | {VIEW_DATA_OTHER_ACCOUNT, isOtherAccount}, | 89 | {VIEW_DATA_OTHER_ACCOUNT, isOtherAccount}, |
| 90 | {VIEW_DATA_PAGE_RECT, pageRect.ToJsonString()} | 90 | {VIEW_DATA_PAGE_RECT, pageRect.ToJsonString()} |
| 91 | }; | 91 | }; |
| 92 | - return jsonObject.dump(); | 92 | + return jsonObject.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore); |
| 93 | } | 93 | } |
| 94 | } // namespace AbilityBase | 94 | } // namespace AbilityBase |
| 95 | } // namespace OHOS | 95 | } // namespace OHOS |
| @@ -235,7 +235,9 @@ void ParseWantParamsFromJsonString(const std::string &jsonString, WantParams &wa | |||
| 235 | template<typename JsonType> | 235 | template<typename JsonType> |
| 236 | void from_json(const JsonType &jsonObject, WantParams &wantParams) | 236 | void from_json(const JsonType &jsonObject, WantParams &wantParams) |
| 237 | { | 237 | { |
| 238 | - ParseWantParamsFromJsonString(jsonObject.dump(), wantParams); | 238 | + using ErrorHandler = typename JsonType::error_handler_t; |
| 239 | + ParseWantParamsFromJsonString( | ||
| 240 | + jsonObject.dump(-1, ' ', false, ErrorHandler::ignore), wantParams); | ||
| 239 | } | 241 | } |
| 240 | 242 | ||
| 241 | template<typename JsonType> | 243 | template<typename JsonType> |
| @@ -2371,7 +2371,7 @@ std::string WantParams::ToString() const | |||
| 2371 | { | 2371 | { |
| 2372 | nlohmann::json jsonObject; | 2372 | nlohmann::json jsonObject; |
| 2373 | WantParamsToJson(jsonObject, *this); | 2373 | WantParamsToJson(jsonObject, *this); |
| 2374 | - return jsonObject.dump(); | 2374 | + return jsonObject.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore); |
| 2375 | } | 2375 | } |
| 2376 | 2376 | ||
| 2377 | void ParseWantParamsFromJsonString(const std::string &jsonString, WantParams &wantParams) | 2377 | void ParseWantParamsFromJsonString(const std::string &jsonString, WantParams &wantParams) |
| @@ -2386,7 +2386,8 @@ void ParseWantParamsFromJsonString(const std::string &jsonString, WantParams &wa | |||
| 2386 | 2386 | ||
| 2387 | void from_json(const nlohmann::json &jsonObject, WantParams &wantParams) | 2387 | void from_json(const nlohmann::json &jsonObject, WantParams &wantParams) |
| 2388 | { | 2388 | { |
| 2389 | - ParseWantParamsFromJsonString(jsonObject.dump(), wantParams); | 2389 | + ParseWantParamsFromJsonString( |
| 2390 | + jsonObject.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore), wantParams); | ||
| 2390 | } | 2391 | } |
| 2391 | 2392 | ||
| 2392 | void to_json(nlohmann::json &jsonObject, const WantParams &wantParams) | 2393 | void to_json(nlohmann::json &jsonObject, const WantParams &wantParams) |
| @@ -253,7 +253,7 @@ bool Serialize(const WantParams &wp, std::string &out) | |||
| 253 | 253 | ||
| 254 | Json envelope = Json::object(); | 254 | Json envelope = Json::object(); |
| 255 | envelope[ENVELOPE_KEY] = std::move(params); | 255 | envelope[ENVELOPE_KEY] = std::move(params); |
| 256 | - out = envelope.dump(); | 256 | + out = envelope.dump(-1, ' ', false, Json::error_handler_t::ignore); |
| 257 | return true; | 257 | return true; |
| 258 | } catch (const Json::exception &e) { | 258 | } catch (const Json::exception &e) { |
| 259 | ABILITYBASE_LOGE("serialize failed, json exception id=%{public}d", e.id); | 259 | ABILITYBASE_LOGE("serialize failed, json exception id=%{public}d", e.id); |
| @@ -1186,5 +1186,27 @@ HWTEST_F(PageNodeInfoTest, ToJsonString_004, TestSize.Level1) | |||
| 1186 | nlohmann::json jsonResult = nlohmann::json::parse(result); | 1186 | nlohmann::json jsonResult = nlohmann::json::parse(result); |
| 1187 | EXPECT_FALSE(jsonResult.is_null()); | 1187 | EXPECT_FALSE(jsonResult.is_null()); |
| 1188 | } | 1188 | } |
| 1189 | + | ||
| 1190 | +/** | ||
| 1191 | + * @tc.name: ToJsonString_InvalidUtf8_0100 | ||
| 1192 | + * @tc.desc: Verify ToJsonString survives invalid UTF-8 bytes in string fields. | ||
| 1193 | + * @tc.type: FUNC | ||
| 1194 | + */ | ||
| 1195 | +HWTEST_F(PageNodeInfoTest, ToJsonString_InvalidUtf8_0100, TestSize.Level1) | ||
| 1196 | +{ | ||
| 1197 | + PageNodeInfo testNode; | ||
| 1198 | + std::string invalidUtf8; | ||
| 1199 | + invalidUtf8.push_back(static_cast<char>(0xFF)); | ||
| 1200 | + invalidUtf8.push_back(static_cast<char>(0xFE)); | ||
| 1201 | + invalidUtf8.push_back(static_cast<char>(0x80)); | ||
| 1202 | + testNode.value = invalidUtf8; | ||
| 1203 | + testNode.tag = "valid"; | ||
| 1204 | + | ||
| 1205 | + std::string result = testNode.ToJsonString(); | ||
| 1206 | + nlohmann::json jsonResult = nlohmann::json::parse(result); | ||
| 1207 | + ASSERT_FALSE(jsonResult.is_discarded()); | ||
| 1208 | + ASSERT_TRUE(jsonResult.contains(PAGE_NODE_INFO_TAG)); | ||
| 1209 | + EXPECT_EQ(jsonResult[PAGE_NODE_INFO_TAG], "valid"); | ||
| 1210 | +} | ||
| 1189 | } // namespace AbilityBase | 1211 | } // namespace AbilityBase |
| 1190 | } // namespace OHOS | 1212 | } // namespace OHOS |
| @@ -785,5 +785,27 @@ HWTEST_F(ViewDataTest, ToJsonString_1010, TestSize.Level1) | |||
| 785 | EXPECT_NE(jsonResult[VIEW_DATA_ABILITY_NAME], "Ability1\twith\ttabs"); | 785 | EXPECT_NE(jsonResult[VIEW_DATA_ABILITY_NAME], "Ability1\twith\ttabs"); |
| 786 | EXPECT_EQ(jsonResult[VIEW_DATA_PAGE_URL], "pages/\\escaped"); | 786 | EXPECT_EQ(jsonResult[VIEW_DATA_PAGE_URL], "pages/\\escaped"); |
| 787 | } | 787 | } |
| 788 | + | ||
| 789 | +/** | ||
| 790 | + * @tc.name: ToJsonString_InvalidUtf8_0100 | ||
| 791 | + * @tc.desc: Verify ToJsonString survives invalid UTF-8 bytes in string fields. | ||
| 792 | + * @tc.type: FUNC | ||
| 793 | + */ | ||
| 794 | +HWTEST_F(ViewDataTest, ToJsonString_InvalidUtf8_0100, TestSize.Level1) | ||
| 795 | +{ | ||
| 796 | + ViewData viewData; | ||
| 797 | + std::string invalidUtf8; | ||
| 798 | + invalidUtf8.push_back(static_cast<char>(0xFF)); | ||
| 799 | + invalidUtf8.push_back(static_cast<char>(0xFE)); | ||
| 800 | + invalidUtf8.push_back(static_cast<char>(0x80)); | ||
| 801 | + viewData.bundleName = invalidUtf8; | ||
| 802 | + viewData.pageUrl = "valid"; | ||
| 803 | + | ||
| 804 | + std::string result = viewData.ToJsonString(); | ||
| 805 | + nlohmann::json jsonResult = nlohmann::json::parse(result); | ||
| 806 | + ASSERT_FALSE(jsonResult.is_discarded()); | ||
| 807 | + ASSERT_TRUE(jsonResult.contains(VIEW_DATA_PAGE_URL)); | ||
| 808 | + EXPECT_EQ(jsonResult[VIEW_DATA_PAGE_URL], "valid"); | ||
| 809 | +} | ||
| 788 | } // namespace AbilityBase | 810 | } // namespace AbilityBase |
| 789 | } // namespace OHOS | 811 | } // namespace OHOS |
| @@ -1446,6 +1446,56 @@ HWTEST_F(WantParamsBaseTest, AaFwk_WantParams_ToString_Nested_0100, Function | M | |||
| 1446 | EXPECT_EQ(jsonObj["nested"]["innerKey"], "innerValue"); | 1446 | EXPECT_EQ(jsonObj["nested"]["innerKey"], "innerValue"); |
| 1447 | } | 1447 | } |
| 1448 | 1448 | ||
| 1449 | +/** | ||
| 1450 | + * @tc.number: AaFwk_WantParams_ToString_InvalidUtf8_0100 | ||
| 1451 | + * @tc.name: ToString | ||
| 1452 | + * @tc.desc: Verify ToString survives param values that are not valid UTF-8. The value | ||
| 1453 | + * may come from an untrusted Parcel across processes, so ToString must not | ||
| 1454 | + * throw on invalid UTF-8 bytes. In the original code, dump() uses the strict | ||
| 1455 | + * error handler and throws nlohmann::json::type_error (id 316) which, under | ||
| 1456 | + * -fno-exceptions, terminates the process. After the fix (error_handler_t:: | ||
| 1457 | + * replace), ToString returns parseable JSON with the bad bytes replaced. | ||
| 1458 | + */ | ||
| 1459 | +HWTEST_F(WantParamsBaseTest, AaFwk_WantParams_ToString_InvalidUtf8_0100, Function | MediumTest | Level1) | ||
| 1460 | +{ | ||
| 1461 | + WantParams params; | ||
| 1462 | + std::string invalidUtf8; | ||
| 1463 | + invalidUtf8.push_back(static_cast<char>(0xFF)); | ||
| 1464 | + invalidUtf8.push_back(static_cast<char>(0xFE)); | ||
| 1465 | + invalidUtf8.push_back(static_cast<char>(0x80)); | ||
| 1466 | + params.SetParam("badKey", OHOS::AAFwk::String::Box(invalidUtf8)); | ||
| 1467 | + | ||
| 1468 | + std::string jsonString = params.ToString(); | ||
| 1469 | + auto jsonObj = nlohmann::json::parse(jsonString, nullptr, false); | ||
| 1470 | + ASSERT_FALSE(jsonObj.is_discarded()); | ||
| 1471 | + ASSERT_TRUE(jsonObj.contains("badKey")); | ||
| 1472 | + EXPECT_TRUE(jsonObj["badKey"].is_string()); | ||
| 1473 | +} | ||
| 1474 | + | ||
| 1475 | +/** | ||
| 1476 | + * @tc.number: AaFwk_WantParams_ToString_InvalidUtf8_0200 | ||
| 1477 | + * @tc.name: ToString | ||
| 1478 | + * @tc.desc: Verify ToString survives invalid UTF-8 bytes embedded inside a longer | ||
| 1479 | + * string (mixed with valid multi-byte UTF-8). Guards against the same | ||
| 1480 | + * strict-error-handler throw on dump(). | ||
| 1481 | + */ | ||
| 1482 | +HWTEST_F(WantParamsBaseTest, AaFwk_WantParams_ToString_InvalidUtf8_0200, Function | MediumTest | Level1) | ||
| 1483 | +{ | ||
| 1484 | + WantParams params; | ||
| 1485 | + std::string mixed = "hello"; | ||
| 1486 | + mixed.push_back(static_cast<char>(0xE4)); // lead byte of a 3-byte seq | ||
| 1487 | + mixed.push_back(static_cast<char>(0xBD)); // continuation, but sequence truncated | ||
| 1488 | + mixed.push_back(static_cast<char>(0xFF)); | ||
| 1489 | + mixed += "world"; | ||
| 1490 | + params.SetParam("mixedKey", OHOS::AAFwk::String::Box(mixed)); | ||
| 1491 | + | ||
| 1492 | + std::string jsonString = params.ToString(); | ||
| 1493 | + auto jsonObj = nlohmann::json::parse(jsonString, nullptr, false); | ||
| 1494 | + ASSERT_FALSE(jsonObj.is_discarded()); | ||
| 1495 | + ASSERT_TRUE(jsonObj.contains("mixedKey")); | ||
| 1496 | + EXPECT_TRUE(jsonObj["mixedKey"].is_string()); | ||
| 1497 | +} | ||
| 1498 | + | ||
| 1449 | /** | 1499 | /** |
| 1450 | * @tc.number: AaFwk_WantParams_PublicReadFromParcel_0100 | 1500 | * @tc.number: AaFwk_WantParams_PublicReadFromParcel_0100 |
| 1451 | * @tc.name: PublicReadFromParcel | 1501 | * @tc.name: PublicReadFromParcel |
| @@ -1265,3 +1265,52 @@ HWTEST_F(WantParamWrapperJsonTest, Want_Param_Wrapper_Json_3990, TestSize.Level1 | |||
| 1265 | EXPECT_FALSE(WantParamWrapperJson::Serialize(overLimit, serialized)); | 1265 | EXPECT_FALSE(WantParamWrapperJson::Serialize(overLimit, serialized)); |
| 1266 | EXPECT_EQ(serialized, "unchanged"); | 1266 | EXPECT_EQ(serialized, "unchanged"); |
| 1267 | } | 1267 | } |
| 1268 | + | ||
| 1269 | +/** | ||
| 1270 | + * @tc.number: Want_Param_Wrapper_Json_4000 | ||
| 1271 | + * @tc.name: Serialize survives invalid UTF-8 bytes | ||
| 1272 | + * @tc.desc: Verify WantParamWrapperJson::Serialize does not throw when a string | ||
| 1273 | + * param contains bytes that are not valid UTF-8. The original code calls | ||
| 1274 | + * envelope.dump() with the strict error handler, which throws | ||
| 1275 | + * nlohmann::json::type_error 316 and terminates. After the fix | ||
| 1276 | + * (error_handler_t::ignore), Serialize succeeds and returns valid JSON. | ||
| 1277 | + */ | ||
| 1278 | +HWTEST_F(WantParamWrapperJsonTest, Want_Param_Wrapper_Json_4000, TestSize.Level1) | ||
| 1279 | +{ | ||
| 1280 | + WantParams wp; | ||
| 1281 | + std::string invalidUtf8; | ||
| 1282 | + invalidUtf8.push_back(static_cast<char>(0xFF)); | ||
| 1283 | + invalidUtf8.push_back(static_cast<char>(0xFE)); | ||
| 1284 | + invalidUtf8.push_back(static_cast<char>(0x80)); | ||
| 1285 | + wp.SetParam("k", String::Box(invalidUtf8)); | ||
| 1286 | + | ||
| 1287 | + std::string serialized; | ||
| 1288 | + EXPECT_TRUE(WantParamWrapperJson::Serialize(wp, serialized)); | ||
| 1289 | + | ||
| 1290 | + WantParams out; | ||
| 1291 | + EXPECT_TRUE(WantParamWrapperJson::Parse(serialized, out)); | ||
| 1292 | +} | ||
| 1293 | + | ||
| 1294 | +/** | ||
| 1295 | + * @tc.number: Want_Param_Wrapper_Json_4010 | ||
| 1296 | + * @tc.name: Serialize survives invalid UTF-8 in nested WantParams | ||
| 1297 | + * @tc.desc: Same as 4000 but with invalid UTF-8 inside a nested WantParams, | ||
| 1298 | + * which exercises the recursive BuildParamsJson path. | ||
| 1299 | + */ | ||
| 1300 | +HWTEST_F(WantParamWrapperJsonTest, Want_Param_Wrapper_Json_4010, TestSize.Level1) | ||
| 1301 | +{ | ||
| 1302 | + WantParams inner; | ||
| 1303 | + std::string invalidUtf8; | ||
| 1304 | + invalidUtf8.push_back(static_cast<char>(0xFF)); | ||
| 1305 | + invalidUtf8.push_back(static_cast<char>(0xFE)); | ||
| 1306 | + inner.SetParam("inner", String::Box(invalidUtf8)); | ||
| 1307 | + | ||
| 1308 | + WantParams wp; | ||
| 1309 | + wp.SetParam("nested", WantParamWrapper::Box(std::move(inner))); | ||
| 1310 | + | ||
| 1311 | + std::string serialized; | ||
| 1312 | + EXPECT_TRUE(WantParamWrapperJson::Serialize(wp, serialized)); | ||
| 1313 | + | ||
| 1314 | + WantParams out; | ||
| 1315 | + EXPECT_TRUE(WantParamWrapperJson::Parse(serialized, out)); | ||
| 1316 | +} | ||