Skip to content

Commit e45d67f

Browse files
Initial C++ fuzz tests
This changelist adds initial C++ fuzz tests to the XmlIo module of the MaterialX test suite. In these new tests, pseudo-random edits are applied to a set of source documents before import, in order to uncover edge cases that aren't yet handled in MaterialXCore and MaterialXFormat. Also: - Add missing type casts to invalidNameChar and hasWindowDriveSpecifier. - Make random numbers deterministic in shaderGenPerformanceTest.
1 parent 60c8a59 commit e45d67f

4 files changed

Lines changed: 49 additions & 6 deletions

File tree

source/MaterialXCore/Util.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const std::tuple<int, int, int> LIBRARY_VERSION_TUPLE(MATERIALX_MAJOR_VERSION,
2424

2525
bool invalidNameChar(char c)
2626
{
27-
return !isalnum(c) && c != '_' && c != ':';
27+
return !isalnum((unsigned char) c) && c != '_' && c != ':';
2828
}
2929

3030
} // anonymous namespace

source/MaterialXFormat/File.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const string MATERIALX_SEARCH_PATH_ENV_VAR = "MATERIALX_SEARCH_PATH";
5151

5252
inline bool hasWindowsDriveSpecifier(const string& val)
5353
{
54-
return (val.length() > 1 && std::isalpha(val[0]) && (val[1] == ':'));
54+
return (val.length() > 1 && std::isalpha((unsigned char) val[0]) && (val[1] == ':'));
5555
}
5656

5757
//

source/MaterialXTest/MaterialXFormat/XmlIo.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <MaterialXTest/External/Catch/catch.hpp>
77

88
#include <MaterialXFormat/Environ.h>
9-
#include <MaterialXFormat/File.h>
109
#include <MaterialXFormat/Util.h>
1110
#include <MaterialXFormat/XmlIo.h>
1211

@@ -262,6 +261,51 @@ TEST_CASE("Comments and newlines", "[xmlio]")
262261
REQUIRE(origXml == newXml);
263262
}
264263

264+
TEST_CASE("Fuzz testing", "[xmlio]")
265+
{
266+
mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();
267+
mx::FilePath examplesPath = searchPath.find("resources/Materials/Examples/StandardSurface");
268+
269+
std::mt19937 rng(0);
270+
std::uniform_int_distribution<size_t> randChar(0, 255);
271+
272+
for (const mx::FilePath& filename : examplesPath.getFilesInDirectory(mx::MTLX_EXTENSION))
273+
{
274+
// Read the example file into an XML string buffer.
275+
const std::string origString = mx::readFile(examplesPath / filename);
276+
REQUIRE(origString.size() > 0);
277+
std::uniform_int_distribution<size_t> randPos(0, origString.size() - 1);
278+
279+
// Iterate over test runs.
280+
for (size_t testRun = 0; testRun < 256; testRun++)
281+
{
282+
std::string editString = origString;
283+
284+
// Iterate over string edits.
285+
for (size_t editIndex = 0; editIndex < 32; editIndex++)
286+
{
287+
// Randomly alter one character in the document.
288+
size_t charIndex = randPos(rng);
289+
size_t newChar = randChar(rng);
290+
editString[charIndex] = (char) newChar;
291+
292+
// Attempt to interpret the edited string as a document, allowing only MaterialX exceptions.
293+
mx::DocumentPtr doc = mx::createDocument();
294+
try
295+
{
296+
mx::readFromXmlString(doc, editString, searchPath);
297+
doc->validate();
298+
}
299+
catch (const mx::Exception&)
300+
{
301+
// On a MaterialX exception, proceed to the next test run.
302+
break;
303+
}
304+
}
305+
}
306+
}
307+
}
308+
265309
TEST_CASE("Locale region testing", "[xmlio]")
266310
{
267311
// In the United States, the thousands separator is a comma, while in Germany it is a period.

source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,8 @@ void shaderGenPerformanceTest(mx::GenContext& context)
370370
REQUIRE(loadedDocuments.size() == documentsPaths.size());
371371

372372
// Shuffle the order of documents and perform document library import validatation and shadergen
373-
std::random_device random_dev;
374-
std::mt19937 generator(random_dev());
375-
std::shuffle(loadedDocuments.begin(), loadedDocuments.end(), generator);
373+
std::mt19937 rng(0);
374+
std::shuffle(loadedDocuments.begin(), loadedDocuments.end(), rng);
376375
for (const auto& doc : loadedDocuments)
377376
{
378377
doc->importLibrary(nodeLibrary);

0 commit comments

Comments
 (0)