* 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.
*/
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fuzzer/FuzzedDataProvider.h>
#include "native_avcodec_videobase.h"
#include "native_avformat.h"
#define FUZZ_PROJECT_NAME "roistring_fuzzer"
constexpr int32_t CATEGORY_MAX = 5;
constexpr int32_t INVALID_TYPE_MAX = 6;
enum FuzzCategory : int32_t {
CAT_APPEND_ROI = 0,
CAT_APPEND_INVALID = 1,
CAT_APPEND_MISSING_KEYS = 2,
CAT_PARSE_RAW = 3,
CAT_PARSE_MIXED = 4,
CAT_PARSE_INVALID = 5,
};
enum InvalidType : int32_t {
INV_NON_NUMERIC = 0,
INV_BOTTOM_LESS_TOP = 1,
INV_RIGHT_LESS_LEFT = 2,
INV_DQP_NON_NUMERIC = 3,
INV_SLB_NON_NUMERIC = 4,
INV_OLD_FORMAT_OUT_OF_RANGE = 5,
INV_DQP_DUPLICATE = 6,
};
namespace OHOS {
namespace RoiStringFuzzer {
constexpr int32_t MAX_COORD = 10000;
constexpr int32_t MAX_PARSE_CAPACITY = 8;
constexpr int32_t INVALID_COORD_MIN = -10;
constexpr int32_t DEFAULT_VALID_COORD = 100;
constexpr int32_t DQP_FUZZ_MIN = -60;
constexpr int32_t DQP_FUZZ_MAX = 60;
constexpr int32_t DQP_MIN_VAL = -51;
constexpr int32_t DQP_MAX_VAL = 51;
constexpr int32_t SLB_FUZZ_MIN = -2;
constexpr int32_t SLB_FUZZ_MAX = 3;
constexpr int32_t SLB_MIN_VAL = 0;
constexpr int32_t SLB_MAX_VAL = 1;
constexpr int32_t MIN_SEGMENT_COUNT = 1;
constexpr int32_t MAX_SEGMENT_COUNT = 4;
constexpr int32_t MIXED_COORD_MAX = 50;
constexpr int32_t COORD_INCREMENT = 1;
static void DestroyFormats(OH_AVFormat **formats, uint32_t count)
{
for (uint32_t i = 0; i < count; i++) {
OH_AVFormat_Destroy(formats[i]);
}
}
static std::string BuildCoordStr(int32_t top, int32_t left,
int32_t bottom, int32_t right)
{
return std::to_string(top) + "," + std::to_string(left) + "-" +
std::to_string(bottom) + "," + std::to_string(right);
}
static std::string BuildKvStr(FuzzedDataProvider *provider)
{
std::string kvStr;
bool useNewFormat = provider->ConsumeBool();
if (useNewFormat) {
int32_t dqp = provider->ConsumeIntegralInRange<int32_t>(
DQP_MIN_VAL, DQP_MAX_VAL);
kvStr += "=dqp:" + std::to_string(dqp);
if (provider->ConsumeBool()) {
int32_t slb = provider->ConsumeIntegralInRange<int32_t>(
SLB_MIN_VAL, SLB_MAX_VAL);
kvStr += ",slb:" + std::to_string(slb);
}
} else {
int32_t dqp = provider->ConsumeIntegralInRange<int32_t>(
DQP_MIN_VAL, DQP_MAX_VAL);
kvStr += "=" + std::to_string(dqp);
}
return kvStr;
}
static void ParseAndDestroyRoiStr(const std::string &roiStr)
{
uint32_t count = 0;
OH_AVErrCode ret = OH_VideoMetadata_GetRoiCount(roiStr.c_str(), &count);
if (ret != AV_ERR_OK || count == 0) {
return;
}
OH_AVFormat *parsed[MAX_PARSE_CAPACITY] = {};
uint32_t parsedCount = 0;
OH_VideoMetadata_ParseRoiString(
roiStr.c_str(), parsed, MAX_PARSE_CAPACITY, &parsedCount);
DestroyFormats(parsed, parsedCount);
}
static void AppendSecondAndParse(char **roiStr, int32_t top, int32_t left,
int32_t bottom, int32_t right)
{
OH_AVFormat *fmt2 = OH_AVFormat_Create();
if (fmt2 == nullptr) {
return;
}
OH_AVFormat_SetIntValue(fmt2, OH_MD_KEY_VIDEO_METADATA_ROI_TOP,
top + COORD_INCREMENT);
OH_AVFormat_SetIntValue(fmt2, OH_MD_KEY_VIDEO_METADATA_ROI_LEFT,
left + COORD_INCREMENT);
OH_AVFormat_SetIntValue(fmt2, OH_MD_KEY_VIDEO_METADATA_ROI_BOTTOM,
bottom + COORD_INCREMENT);
OH_AVFormat_SetIntValue(fmt2, OH_MD_KEY_VIDEO_METADATA_ROI_RIGHT,
right + COORD_INCREMENT);
OH_VideoMetadata_AppendRoiString(roiStr, fmt2);
OH_AVFormat_Destroy(fmt2);
}
bool AppendRoiStringFuzzTest(FuzzedDataProvider *provider)
{
int32_t top = provider->ConsumeIntegralInRange<int32_t>(0, MAX_COORD);
int32_t left = provider->ConsumeIntegralInRange<int32_t>(0, MAX_COORD);
int32_t bottom = provider->ConsumeIntegralInRange<int32_t>(0, MAX_COORD);
int32_t right = provider->ConsumeIntegralInRange<int32_t>(0, MAX_COORD);
bool setDqp = provider->ConsumeBool();
bool setSlb = provider->ConsumeBool();
int32_t dqpVal = provider->ConsumeIntegralInRange<int32_t>(
DQP_FUZZ_MIN, DQP_FUZZ_MAX);
int32_t slbVal = provider->ConsumeIntegralInRange<int32_t>(
SLB_FUZZ_MIN, SLB_FUZZ_MAX);
bool appendSecond = provider->ConsumeBool();
OH_AVFormat *fmt = OH_AVFormat_Create();
if (fmt == nullptr) {
return false;
}
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_TOP, top);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_LEFT, left);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_BOTTOM, bottom);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_RIGHT, right);
if (setDqp) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_DELTA_QP, dqpVal);
}
if (setSlb) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_SEM_LABEL, slbVal);
}
char *roiStr = nullptr;
OH_AVErrCode ret = OH_VideoMetadata_AppendRoiString(&roiStr, fmt);
if (ret == AV_ERR_OK && roiStr != nullptr && appendSecond) {
AppendSecondAndParse(&roiStr, top, left, bottom, right);
if (roiStr != nullptr) {
std::string str(roiStr);
ParseAndDestroyRoiStr(str);
}
}
if (roiStr != nullptr) {
free(roiStr);
}
OH_AVFormat_Destroy(fmt);
return true;
}
bool AppendRoiStringInvalidFuzzTest(FuzzedDataProvider *provider)
{
int32_t top = provider->ConsumeIntegralInRange<int32_t>(
INVALID_COORD_MIN, MAX_COORD);
int32_t left = provider->ConsumeIntegralInRange<int32_t>(
INVALID_COORD_MIN, MAX_COORD);
int32_t bottom = provider->ConsumeIntegralInRange<int32_t>(
INVALID_COORD_MIN, MAX_COORD);
int32_t right = provider->ConsumeIntegralInRange<int32_t>(
INVALID_COORD_MIN, MAX_COORD);
OH_AVFormat *fmt = OH_AVFormat_Create();
if (fmt == nullptr) {
return false;
}
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_TOP, top);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_LEFT, left);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_BOTTOM, bottom);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_RIGHT, right);
char *roiStr = nullptr;
OH_VideoMetadata_AppendRoiString(&roiStr, fmt);
if (roiStr != nullptr) {
free(roiStr);
}
OH_AVFormat_Destroy(fmt);
return true;
}
bool AppendRoiStringMissingKeysFuzzTest(FuzzedDataProvider *provider)
{
bool skipTop = provider->ConsumeBool();
bool skipLeft = provider->ConsumeBool();
bool skipBottom = provider->ConsumeBool();
bool skipRight = provider->ConsumeBool();
OH_AVFormat *fmt = OH_AVFormat_Create();
if (fmt == nullptr) {
return false;
}
if (!skipTop) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_TOP, 0);
}
if (!skipLeft) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_LEFT, 0);
}
if (!skipBottom) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_BOTTOM,
DEFAULT_VALID_COORD);
}
if (!skipRight) {
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_VIDEO_METADATA_ROI_RIGHT,
DEFAULT_VALID_COORD);
}
char *roiStr = nullptr;
OH_VideoMetadata_AppendRoiString(&roiStr, fmt);
if (roiStr != nullptr) {
free(roiStr);
}
OH_AVFormat_Destroy(fmt);
return true;
}
bool ParseRoiStringFuzzTest(const uint8_t *data, size_t size)
{
std::string roiStr(reinterpret_cast<const char *>(data), size);
ParseAndDestroyRoiStr(roiStr);
return true;
}
bool ParseRoiStringMixedFuzzTest(FuzzedDataProvider *provider)
{
std::string baseStr;
int32_t segmentCount = provider->ConsumeIntegralInRange<int32_t>(
MIN_SEGMENT_COUNT, MAX_SEGMENT_COUNT);
for (int32_t i = 0; i < segmentCount; i++) {
int32_t top = provider->ConsumeIntegralInRange<int32_t>(
0, MIXED_COORD_MAX);
int32_t left = provider->ConsumeIntegralInRange<int32_t>(
0, MIXED_COORD_MAX);
int32_t bottom = provider->ConsumeIntegralInRange<int32_t>(
top + COORD_INCREMENT, DEFAULT_VALID_COORD);
int32_t right = provider->ConsumeIntegralInRange<int32_t>(
left + COORD_INCREMENT, DEFAULT_VALID_COORD);
baseStr += BuildCoordStr(top, left, bottom, right);
if (provider->ConsumeBool()) {
baseStr += BuildKvStr(provider);
}
baseStr += ";";
}
ParseAndDestroyRoiStr(baseStr);
return true;
}
static std::string GetInvalidStr(int32_t invalidType)
{
switch (invalidType) {
case INV_NON_NUMERIC:
return "abc";
case INV_BOTTOM_LESS_TOP:
return "10,20-5,30;";
case INV_RIGHT_LESS_LEFT:
return "10,50-30,20;";
case INV_DQP_NON_NUMERIC:
return "10,20-30,40=dqp:abc;";
case INV_SLB_NON_NUMERIC:
return "10,20-30,40=slb:1t;";
case INV_OLD_FORMAT_OUT_OF_RANGE:
return "10,20-30,40=60;";
case INV_DQP_DUPLICATE:
return "10,20-30,40=dqp:5,dqp:3;";
default:
return "";
}
}
bool ParseRoiStringInvalidFuzzTest(FuzzedDataProvider *provider)
{
int32_t invalidType = provider->ConsumeIntegralInRange<int32_t>(
0, INVALID_TYPE_MAX);
std::string invalidStr = GetInvalidStr(invalidType);
ParseAndDestroyRoiStr(invalidStr);
return true;
}
bool ParseRoiStringNullFuzzTest()
{
uint32_t count = 0;
OH_VideoMetadata_GetRoiCount(nullptr, &count);
OH_VideoMetadata_GetRoiCount("0,0-10,10;", nullptr);
OH_AVFormat *formats[MAX_PARSE_CAPACITY] = {};
uint32_t parsedCount = 0;
OH_VideoMetadata_ParseRoiString(nullptr, formats, MAX_PARSE_CAPACITY, &parsedCount);
OH_VideoMetadata_ParseRoiString("0,0-10,10;", nullptr, MAX_PARSE_CAPACITY, &parsedCount);
OH_VideoMetadata_ParseRoiString("0,0-10,10;", formats, MAX_PARSE_CAPACITY, nullptr);
return true;
}
bool AppendRoiStringNullFuzzTest()
{
OH_VideoMetadata_AppendRoiString(nullptr, nullptr);
char *roiStr = nullptr;
OH_VideoMetadata_AppendRoiString(&roiStr, nullptr);
OH_AVFormat *fmt = OH_AVFormat_Create();
if (fmt != nullptr) {
OH_VideoMetadata_AppendRoiString(nullptr, fmt);
OH_AVFormat_Destroy(fmt);
}
return true;
}
bool ParseRoiStringEmptyFuzzTest()
{
uint32_t count = 0;
OH_VideoMetadata_GetRoiCount("", &count);
OH_AVFormat *formats[MAX_PARSE_CAPACITY] = {};
uint32_t parsedCount = 0;
OH_VideoMetadata_ParseRoiString("", formats, MAX_PARSE_CAPACITY, &parsedCount);
return true;
}
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (data == nullptr || size < sizeof(int32_t)) {
return 0;
}
FuzzedDataProvider fdp(data, size);
static uint8_t nullTestFlag = 0;
if (nullTestFlag == 0) {
OHOS::RoiStringFuzzer::AppendRoiStringNullFuzzTest();
OHOS::RoiStringFuzzer::ParseRoiStringNullFuzzTest();
OHOS::RoiStringFuzzer::ParseRoiStringEmptyFuzzTest();
nullTestFlag = 1;
}
int32_t category = fdp.ConsumeIntegralInRange<int32_t>(0, CATEGORY_MAX);
switch (category) {
case CAT_APPEND_ROI:
OHOS::RoiStringFuzzer::AppendRoiStringFuzzTest(&fdp);
break;
case CAT_APPEND_INVALID:
OHOS::RoiStringFuzzer::AppendRoiStringInvalidFuzzTest(&fdp);
break;
case CAT_APPEND_MISSING_KEYS:
OHOS::RoiStringFuzzer::AppendRoiStringMissingKeysFuzzTest(&fdp);
break;
case CAT_PARSE_RAW:
OHOS::RoiStringFuzzer::ParseRoiStringFuzzTest(data, size);
break;
case CAT_PARSE_MIXED:
OHOS::RoiStringFuzzer::ParseRoiStringMixedFuzzTest(&fdp);
break;
case CAT_PARSE_INVALID:
OHOS::RoiStringFuzzer::ParseRoiStringInvalidFuzzTest(&fdp);
break;
default:
break;
}
return 0;
}