From 999781390062345a086c97faabf45547ab5fe7e5 Mon Sep 17 00:00:00 2001 From: Tony Coder <407243179@qq.com> Date: Mon, 31 Aug 2026 14:01:00 +0000 Subject: [PATCH] fix: leftover packing-tool CLI stoi packing-tool parses untrusted CLI integers (version-code, compress-level, atomic-service size limits, general/version-normalize params) with std::stoi. Overflow throws; partial/junk tokens can still be accepted when callers skip digit prechecks. Parse with std::from_chars via ParsePackingInt32 so valid in-range values stay unchanged and overflow/partial/empty/junk is rejected and logged. Signed-off-by: Tony Coder <407243179@qq.com> --- .../frameworks/include/parse_packing_int.h | 61 +++++++++ .../frameworks/src/general_normalize.cpp | 121 ++++++------------ packing_tool/frameworks/src/packager.cpp | 20 +-- packing_tool/frameworks/src/utils.cpp | 12 +- .../frameworks/src/version_normalize.cpp | 7 +- 5 files changed, 113 insertions(+), 108 deletions(-) create mode 100644 packing_tool/frameworks/include/parse_packing_int.h diff --git a/packing_tool/frameworks/include/parse_packing_int.h b/packing_tool/frameworks/include/parse_packing_int.h new file mode 100644 index 00000000..3a6f06da --- /dev/null +++ b/packing_tool/frameworks/include/parse_packing_int.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEVELOPTOOLS_PACKING_TOOL_APT_FRAMEWORKS_INCLUDE_PARSE_PACKING_INT_H +#define DEVELOPTOOLS_PACKING_TOOL_APT_FRAMEWORKS_INCLUDE_PARSE_PACKING_INT_H + +#include +#include +#include +#include +#include + +namespace OHOS { +namespace AppPackingTool { +/* + * Parse a whole-token decimal int32 from packing-tool CLI / config text. + * Reject empty, overflow, underflow, leading/trailing junk, '+', hex, and floats. + * Valid in-range values keep the same numeric result as std::stoi on digit-only input. + */ +inline bool ParsePackingInt32(std::string_view text, int32_t &out) +{ + if (text.empty()) { + return false; + } + int32_t value = 0; + auto result = std::from_chars(text.data(), text.data() + text.size(), value); + if (result.ec != std::errc() || result.ptr != text.data() + text.size()) { + return false; + } + out = value; + return true; +} + +inline bool ParsePackingInt32(const std::string &text, int32_t &out) +{ + return ParsePackingInt32(std::string_view(text), out); +} + +inline bool ParsePackingInt32(const char *text, int32_t &out) +{ + if (text == nullptr) { + return false; + } + return ParsePackingInt32(std::string_view(text), out); +} +} // namespace AppPackingTool +} // namespace OHOS + +#endif // DEVELOPTOOLS_PACKING_TOOL_APT_FRAMEWORKS_INCLUDE_PARSE_PACKING_INT_H diff --git a/packing_tool/frameworks/src/general_normalize.cpp b/packing_tool/frameworks/src/general_normalize.cpp index 530263da..72b4bfe0 100644 --- a/packing_tool/frameworks/src/general_normalize.cpp +++ b/packing_tool/frameworks/src/general_normalize.cpp @@ -26,6 +26,7 @@ #include "json/pack_info.h" #include "log.h" #include "utils.h" +#include "parse_packing_int.h" #include "zip_utils.h" #include "error/packing_tool_err_msg.h" @@ -96,16 +97,10 @@ int32_t GeneralNormalize::PreProcess() "--version-code is invalid.").c_str()); return ERR_INVALID_VALUE; } - try { - int32_t versionCode = std::stoi(it->second); - if (versionCode > Constants::MAX_VERSION_CODE) { - LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - "--version-code is invalid.").c_str()); - return ERR_INVALID_VALUE; - } - } catch (const std::exception& e) { + int32_t versionCode = 0; + if (!ParsePackingInt32(it->second, versionCode) || versionCode > Constants::MAX_VERSION_CODE) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "--version-code is invalid.").c_str()); return ERR_INVALID_VALUE; } } @@ -155,16 +150,10 @@ int32_t GeneralNormalize::PreProcess() "--min-compatible-version-code is invalid.").c_str()); return ERR_INVALID_VALUE; } - try { - int32_t minCompatibleVersionCode = std::stoi(it->second); - if (minCompatibleVersionCode > Constants::MAX_VERSION_CODE) { - LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - "--min-compatible-version-code is invalid.").c_str()); - return ERR_INVALID_VALUE; - } - } catch (const std::exception& e) { + int32_t minCompatibleVersionCode = 0; + if (!ParsePackingInt32(it->second, minCompatibleVersionCode) || minCompatibleVersionCode > Constants::MAX_VERSION_CODE) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "--min-compatible-version-code is invalid.").c_str()); return ERR_INVALID_VALUE; } } @@ -176,16 +165,10 @@ int32_t GeneralNormalize::PreProcess() "--min-api-version is invalid.").c_str()); return ERR_INVALID_VALUE; } - try { - int32_t minApiVersion = std::stoi(it->second); - if (minApiVersion > Constants::MAX_VERSION_CODE) { - LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - "--min-api-version is invalid.").c_str()); - return ERR_INVALID_VALUE; - } - } catch (const std::exception& e) { + int32_t minApiVersion = 0; + if (!ParsePackingInt32(it->second, minApiVersion) || minApiVersion > Constants::MAX_VERSION_CODE) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "--min-api-version is invalid.").c_str()); return ERR_INVALID_VALUE; } } @@ -197,16 +180,10 @@ int32_t GeneralNormalize::PreProcess() "--target-api-version is invalid.").c_str()); return ERR_INVALID_VALUE; } - try { - int32_t targetApiVersion = std::stoi(it->second); - if (targetApiVersion > Constants::MAX_VERSION_CODE) { - LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - "--target-api-version is invalid.").c_str()); - return ERR_INVALID_VALUE; - } - } catch (const std::exception& e) { + int32_t targetApiVersion = 0; + if (!ParsePackingInt32(it->second, targetApiVersion) || targetApiVersion > Constants::MAX_VERSION_CODE) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "--target-api-version is invalid.").c_str()); return ERR_INVALID_VALUE; } } @@ -270,11 +247,9 @@ bool GeneralNormalize::ModifyModuleJson(const std::string &moduleJsonPath, moduleJson.GetStageVersion(version); generalNormalizeVersion.originVersionCode = version.versionCode; int32_t versionCode = 0; - try { - versionCode = std::stoi(parameterMap_.at(Constants::PARAM_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_VERSION_CODE), versionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!moduleJson.SetVersionCode(versionCode, true)) { @@ -327,11 +302,9 @@ bool GeneralNormalize::ModifyModuleJson(const std::string &moduleJsonPath, moduleJson.GetStageVersion(version); generalNormalizeVersion.originMinCompatibleVersionCode = version.minCompatibleVersionCode; int32_t minCompatibleVersionCode = 0; - try { - minCompatibleVersionCode = std::stoi(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE), minCompatibleVersionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!moduleJson.SetMinCompatibleVersionCode(minCompatibleVersionCode, true)) { @@ -347,11 +320,9 @@ bool GeneralNormalize::ModifyModuleJson(const std::string &moduleJsonPath, moduleJson.GetMinApiVersion(originMinAPIVersion); generalNormalizeVersion.originMinAPIVersion = originMinAPIVersion; int32_t minAPIVersion = 0; - try { - minAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_MIN_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_API_VERSION), minAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!moduleJson.SetMinAPIVersion(minAPIVersion, true)) { @@ -367,11 +338,9 @@ bool GeneralNormalize::ModifyModuleJson(const std::string &moduleJsonPath, moduleJson.GetTargetApiVersion(originTargetAPIVersion); generalNormalizeVersion.originTargetAPIVersion = originTargetAPIVersion; int32_t targetAPIVersion = 0; - try { - targetAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION), targetAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!moduleJson.SetTargetAPIVersion(targetAPIVersion, true)) { @@ -473,11 +442,9 @@ bool GeneralNormalize::ModifyConfigJson(const std::string &configJsonPath, configJson.GetFaVersion(version); generalNormalizeVersion.originVersionCode = version.versionCode; int32_t versionCode = 0; - try { - versionCode = std::stoi(parameterMap_.at(Constants::PARAM_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_VERSION_CODE), versionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!configJson.SetVersionCode(versionCode, false)) { @@ -517,11 +484,9 @@ bool GeneralNormalize::ModifyConfigJson(const std::string &configJsonPath, configJson.GetFaVersion(version); generalNormalizeVersion.originMinCompatibleVersionCode = version.minCompatibleVersionCode; int32_t minCompatibleVersionCode = 0; - try { - minCompatibleVersionCode = std::stoi(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE), minCompatibleVersionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!configJson.SetMinCompatibleVersionCode(minCompatibleVersionCode, false)) { @@ -537,11 +502,9 @@ bool GeneralNormalize::ModifyConfigJson(const std::string &configJsonPath, configJson.GetFaModuleApiVersion(moduleApiVersion); generalNormalizeVersion.originMinAPIVersion = moduleApiVersion.compatibleApiVersion; int32_t minAPIVersion = 0; - try { - minAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_MIN_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_API_VERSION), minAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!configJson.SetMinAPIVersion(minAPIVersion, false)) { @@ -557,11 +520,9 @@ bool GeneralNormalize::ModifyConfigJson(const std::string &configJsonPath, configJson.GetFaModuleApiVersion(moduleApiVersion); generalNormalizeVersion.originTargetAPIVersion = moduleApiVersion.targetApiVersion; int32_t targetAPIVersion = 0; - try { - targetAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION), targetAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!configJson.SetTargetAPIVersion(targetAPIVersion, false)) { @@ -660,11 +621,9 @@ bool GeneralNormalize::ModifyPackInfo(const std::string &packInfoPath) auto it = parameterMap_.find(Constants::PARAM_VERSION_CODE); if (it != parameterMap_.end()) { int32_t versionCode = 0; - try { - versionCode = std::stoi(parameterMap_.at(Constants::PARAM_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_VERSION_CODE), versionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!packInfo.SetVersionCode(versionCode)) { @@ -707,11 +666,9 @@ bool GeneralNormalize::ModifyPackInfo(const std::string &packInfoPath) it = parameterMap_.find(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE); if (it != parameterMap_.end()) { int32_t minCompatibleVersionCode = 0; - try { - minCompatibleVersionCode = std::stoi(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_COMPATIBLE_VERSION_CODE), minCompatibleVersionCode)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!packInfo.SetMinCompatibleVersionCode(minCompatibleVersionCode)) { @@ -724,11 +681,9 @@ bool GeneralNormalize::ModifyPackInfo(const std::string &packInfoPath) it = parameterMap_.find(Constants::PARAM_MIN_API_VERSION); if (it != parameterMap_.end()) { int32_t minAPIVersion = 0; - try { - minAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_MIN_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_MIN_API_VERSION), minAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!packInfo.SetMinAPIVersion(minAPIVersion)) { @@ -741,11 +696,9 @@ bool GeneralNormalize::ModifyPackInfo(const std::string &packInfoPath) it = parameterMap_.find(Constants::PARAM_TARGET_API_VERSION); if (it != parameterMap_.end()) { int32_t targetAPIVersion = 0; - try { - targetAPIVersion = std::stoi(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION)); - } catch (const std::exception& e) { + if (!ParsePackingInt32(parameterMap_.at(Constants::PARAM_TARGET_API_VERSION), targetAPIVersion)) { LOGE("%s", PackingToolErrMsg::GENERAL_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "CLI integer parameter is invalid.").c_str()); return false; } if (!packInfo.SetTargetAPIVersion(targetAPIVersion)) { diff --git a/packing_tool/frameworks/src/packager.cpp b/packing_tool/frameworks/src/packager.cpp index f4454ed4..a9c3fa95 100644 --- a/packing_tool/frameworks/src/packager.cpp +++ b/packing_tool/frameworks/src/packager.cpp @@ -26,6 +26,7 @@ #include "log.h" #include "scan_statdulpicate.h" #include "utils.h" +#include "parse_packing_int.h" #include "zip_utils.h" #include "error/packing_tool_err_msg.h" using packing_tool::error::PackingToolErrMsg; @@ -176,14 +177,9 @@ bool Packager::IsCompressLevelValid() "--compress-level value not number between 1-9.").c_str()); return false; } - try { - int level = std::stoi(levelStr); - if (level < Constants::MIN_COMPRESS_LEVEL || level > Constants::MAX_COMPRESS_LEVEL) { - LOGE("%s", PackingToolErrMsg::COMMAND_PARSER_FAILED.toStringWithArgs( - "--compress-level value not number between 1-9.").c_str()); - return false; - } - } catch (const std::exception& e) { + int32_t level = 0; + if (!ParsePackingInt32(levelStr, level) || + level < Constants::MIN_COMPRESS_LEVEL || level > Constants::MAX_COMPRESS_LEVEL) { LOGE("%s", PackingToolErrMsg::COMMAND_PARSER_FAILED.toStringWithArgs( "--compress-level value not number between 1-9.").c_str()); return false; @@ -613,9 +609,7 @@ bool Packager::ParseAtomicServiceEntrySizeLimitParameter() auto it = parameterMap_.find(Constants::PARAM_ATOMIC_SERVICE_ENTRY_SIZE_LIMIT); int32_t entrySizeLimit = Constants::ATOMIC_SERVICE_ENTRY_SIZE_LIMIT_DEFAULT; if (it != parameterMap_.end()) { - try { - entrySizeLimit = std::stoi(it->second); - } catch (const std::exception& e) { + if (!ParsePackingInt32(it->second, entrySizeLimit)) { LOGE("%s", PackingToolErrMsg::PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toStringWithArgs( std::vector{ "parseAtomicServiceEntrySizeLimitParameter failed, " @@ -643,9 +637,7 @@ bool Packager::ParseAtomicServiceNonEntrySizeLimitParameter() auto it = parameterMap_.find(Constants::PARAM_ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT); int32_t nonEntrySizeLimit = Constants::ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT_DEFAULT; if (it != parameterMap_.end()) { - try { - nonEntrySizeLimit = std::stoi(it->second); - } catch (const std::exception& e) { + if (!ParsePackingInt32(it->second, nonEntrySizeLimit)) { LOGE("%s", PackingToolErrMsg::PARSE_ATOMIC_SERVICE_SIZE_LIMIT_FAILED.toStringWithArgs( std::vector{ "parseAtomicServiceSizeLimit failed, " diff --git a/packing_tool/frameworks/src/utils.cpp b/packing_tool/frameworks/src/utils.cpp index 0791348f..03811161 100644 --- a/packing_tool/frameworks/src/utils.cpp +++ b/packing_tool/frameworks/src/utils.cpp @@ -15,6 +15,8 @@ #include "utils.h" +#include "parse_packing_int.h" + #include #include #include @@ -361,19 +363,17 @@ bool Utils::IsPositiveInteger(const std::string& str, int min, int max) return false; } for (char c : str) { - if (!std::isdigit(c)) { + if (!std::isdigit(static_cast(c))) { return false; } } - try { - int number = std::stoi(str); - return number >= min && number <= max; - } catch (const std::out_of_range& e) { + int32_t number = 0; + if (!ParsePackingInt32(str, number)) { LOGE("%s", PackingToolErrMsg::COMMAND_PARSER_FAILED.toStringWithArgs( ("Number " + str + " is Out of Range!").c_str()).c_str()); return false; } - return true; + return number >= min && number <= max; } bool Utils::StringToBool(const std::string& str) diff --git a/packing_tool/frameworks/src/version_normalize.cpp b/packing_tool/frameworks/src/version_normalize.cpp index 2db5a644..f9c9d6ec 100644 --- a/packing_tool/frameworks/src/version_normalize.cpp +++ b/packing_tool/frameworks/src/version_normalize.cpp @@ -23,6 +23,7 @@ #include "json/pack_info.h" #include "log.h" #include "utils.h" +#include "parse_packing_int.h" #include "zip_utils.h" #include "error/packing_tool_err_msg.h" @@ -295,11 +296,9 @@ int32_t VersionNormalize::Process() int32_t versionCode = 0; auto it = parameterMap_.find(Constants::PARAM_VERSION_CODE); if (it != parameterMap_.end()) { - try { - versionCode = std::stoi(it->second); - } catch (const std::exception& e) { + if (!ParsePackingInt32(it->second, versionCode)) { LOGE("%s", PackingToolErrMsg::VERSION_NORMALIZE_MODE_ARGS_INVALID.toStringWithArgs( - ("Exception: " + std::string(e.what())).c_str()).c_str()); + "Input version-code is invalid.").c_str()); return ERR_INVALID_VALUE; } } else {