mirror of
https://github.com/opencv/opencv.git
synced 2026-09-25 04:09:57 +03:00
Merge pull request #29945 from Rishiii57:fix/filestorage-recursion-depth-limit-29939
core(persistence): add recursion depth limit to XML/YAML/JSON parsers
This commit is contained in:
@@ -27,6 +27,7 @@ typedef void* gzFile;
|
||||
//=====================================================================================
|
||||
|
||||
static const size_t PARSER_BASE64_BUFFER_SIZE = 1024U * 1024U / 8U;
|
||||
static const int CV_PERSISTENCE_MAX_DEPTH = 1024;
|
||||
|
||||
namespace base64 {
|
||||
|
||||
|
||||
@@ -673,11 +673,14 @@ public:
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char* parseSeq( char* ptr, FileNode& node )
|
||||
char* parseSeq( char* ptr, FileNode& node, int depth = 0 )
|
||||
{
|
||||
if (!ptr)
|
||||
CV_PARSE_ERROR_CPP( "ptr is NULL" );
|
||||
|
||||
if( depth > CV_PERSISTENCE_MAX_DEPTH )
|
||||
CV_PARSE_ERROR_CPP("Too many nested collections");
|
||||
|
||||
if ( *ptr != '[' )
|
||||
CV_PARSE_ERROR_CPP( "'[' - left-brace of seq is missing" );
|
||||
else
|
||||
@@ -696,9 +699,9 @@ public:
|
||||
FileNode child = fs->addNode(node, std::string(), FileNode::NONE );
|
||||
|
||||
if ( *ptr == '[' )
|
||||
ptr = parseSeq( ptr, child );
|
||||
ptr = parseSeq( ptr, child, depth + 1 );
|
||||
else if ( *ptr == '{' )
|
||||
ptr = parseMap( ptr, child );
|
||||
ptr = parseMap( ptr, child, depth + 1 );
|
||||
else
|
||||
ptr = parseValue( ptr, child );
|
||||
}
|
||||
@@ -727,11 +730,14 @@ public:
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char* parseMap( char* ptr, FileNode& node )
|
||||
char* parseMap( char* ptr, FileNode& node, int depth = 0 )
|
||||
{
|
||||
if (!ptr)
|
||||
CV_PARSE_ERROR_CPP("ptr is NULL");
|
||||
|
||||
if( depth > CV_PERSISTENCE_MAX_DEPTH )
|
||||
CV_PARSE_ERROR_CPP("Too many nested collections");
|
||||
|
||||
if ( *ptr != '{' )
|
||||
CV_PARSE_ERROR_CPP( "'{' - left-brace of map is missing" );
|
||||
else
|
||||
@@ -756,9 +762,9 @@ public:
|
||||
break;
|
||||
|
||||
if ( *ptr == '[' )
|
||||
ptr = parseSeq( ptr, child );
|
||||
ptr = parseSeq( ptr, child, depth + 1 );
|
||||
else if ( *ptr == '{' )
|
||||
ptr = parseMap( ptr, child );
|
||||
ptr = parseMap( ptr, child, depth + 1 );
|
||||
else
|
||||
ptr = parseValue( ptr, child );
|
||||
}
|
||||
|
||||
@@ -458,11 +458,14 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
char* parseValue( char* ptr, FileNode& node )
|
||||
char* parseValue( char* ptr, FileNode& node, int depth = 0 )
|
||||
{
|
||||
if (!ptr)
|
||||
CV_PARSE_ERROR_CPP("Invalid input");
|
||||
|
||||
if( depth > CV_PERSISTENCE_MAX_DEPTH )
|
||||
CV_PARSE_ERROR_CPP("Too many nested collections");
|
||||
|
||||
FileNode new_elem;
|
||||
bool have_space = true;
|
||||
int value_type = node.type();
|
||||
@@ -521,7 +524,7 @@ public:
|
||||
|
||||
new_elem = fs->addNode(node, key, elem_type, 0);
|
||||
if (!binary_string)
|
||||
ptr = parseValue(ptr, new_elem);
|
||||
ptr = parseValue(ptr, new_elem, depth + 1);
|
||||
else
|
||||
{
|
||||
ptr = fs->parseBase64( ptr, 0, new_elem);
|
||||
|
||||
@@ -446,11 +446,14 @@ public:
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char* parseValue( char* ptr, FileNode& node, int min_indent, bool is_parent_flow )
|
||||
char* parseValue( char* ptr, FileNode& node, int min_indent, bool is_parent_flow, int depth = 0 )
|
||||
{
|
||||
if (!ptr)
|
||||
CV_PARSE_ERROR_CPP("Invalid input");
|
||||
|
||||
if( depth > CV_PERSISTENCE_MAX_DEPTH )
|
||||
CV_PARSE_ERROR_CPP("Too many nested collections");
|
||||
|
||||
char* endptr = 0;
|
||||
char c = ptr[0], d = ptr[1];
|
||||
int value_type = FileNode::NONE;
|
||||
@@ -695,7 +698,7 @@ public:
|
||||
break;
|
||||
elem = fs->addNode(node, std::string(), FileNode::NONE);
|
||||
}
|
||||
ptr = parseValue( ptr, elem, new_min_indent, true );
|
||||
ptr = parseValue( ptr, elem, new_min_indent, true, depth + 1 );
|
||||
}
|
||||
fs->finalizeCollection(node);
|
||||
}
|
||||
@@ -781,7 +784,7 @@ public:
|
||||
elem = fs->addNode(node, std::string(), FileNode::NONE);
|
||||
}
|
||||
ptr = skipSpaces( ptr, indent + 1, INT_MAX );
|
||||
ptr = parseValue( ptr, elem, indent + 1, false );
|
||||
ptr = parseValue( ptr, elem, indent + 1, false, depth + 1 );
|
||||
ptr = skipSpaces( ptr, 0, INT_MAX );
|
||||
if( ptr - fs->bufferStart() != indent )
|
||||
{
|
||||
|
||||
@@ -2143,6 +2143,36 @@ TEST(Core_InputOutput, FileStorage_invalid_path_regression_21448_JSON)
|
||||
fs.release();
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_recursion_depth_limit_29939_XML)
|
||||
{
|
||||
const int N = 2000; // well past CV_PERSISTENCE_MAX_DEPTH, small enough to run fast
|
||||
std::string open_tags, close_tags;
|
||||
for (int i = 0; i < N; i++) { open_tags += "<a>"; close_tags += "</a>"; }
|
||||
std::string content = "<?xml version=\"1.0\"?>\n<opencv_storage>\n"
|
||||
+ open_tags + "1" + close_tags + "\n</opencv_storage>\n";
|
||||
|
||||
EXPECT_THROW(FileStorage(content, FileStorage::READ | FileStorage::MEMORY), cv::Exception);
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_recursion_depth_limit_29939_YAML)
|
||||
{
|
||||
const int N = 2000;
|
||||
std::string content = "%YAML:1.0\n---\n" + std::string(N, '[') + std::string(N, ']') + "\n";
|
||||
|
||||
EXPECT_THROW(FileStorage(content, FileStorage::READ | FileStorage::MEMORY), cv::Exception);
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_recursion_depth_limit_29939_JSON)
|
||||
{
|
||||
const int N = 2000;
|
||||
std::string content;
|
||||
for (int i = 0; i < N; i++) content += "{\"a\":";
|
||||
content += "1";
|
||||
for (int i = 0; i < N; i++) content += "}";
|
||||
content += "\n";
|
||||
|
||||
EXPECT_THROW(FileStorage(content, FileStorage::READ | FileStorage::MEMORY), cv::Exception);
|
||||
}
|
||||
// see https://github.com/opencv/opencv/issues/25073
|
||||
typedef testing::TestWithParam< std::string > Core_InputOutput_regression_25073;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user