fuzz: stability of parallel fuzz testing

The fuzz targets that write a temp file currently use the same filename
every time. One of the users identified that if the tests are running
in parallel mode, many processes are accessing the same file.
This results in unstable input to the API being tested, where the file
may be overwritten as the function is being tested.

This commit fixes it by putting the fuzz process PID in the filenames
for the scanfile and dbload fuzz targets.

Resolves: https://github.com/Cisco-Talos/clamav/issues/432

Also fixed a CMake bug that built an extra fuzz target file that doesn't
serve any purpose.

Resolves: https://github.com/Cisco-Talos/clamav/issues/431
pull/523/head
Micah Snyder 3 years ago committed by Micah Snyder
parent 0d96061e2f
commit 2d99b49797
  1. 4
      fuzz/CMakeLists.txt
  2. 99
      fuzz/clamav_dbload_fuzzer.cpp
  3. 29
      fuzz/clamav_scanfile_fuzzer.cpp

@ -43,7 +43,7 @@ target_sources(clamav_scanfile_fuzzer
target_link_libraries(clamav_scanfile_fuzzer ClamAV::libclamav)
set_target_properties(clamav_scanfile_fuzzer PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
foreach(TARGET in ${SCAN_TARGETS})
foreach(TARGET ${SCAN_TARGETS})
add_executable(clamav_scanmap_${TARGET}_fuzzer)
target_sources(clamav_scanmap_${TARGET}_fuzzer
PRIVATE clamav_scanmap_fuzzer.cpp)
@ -59,7 +59,7 @@ foreach(TARGET in ${SCAN_TARGETS})
set_target_properties(clamav_scanfile_${TARGET}_fuzzer PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
endforeach()
foreach(TARGET in ${DBLOAD_TARGETS})
foreach(TARGET ${DBLOAD_TARGETS})
add_executable(clamav_dbload_${TARGET}_fuzzer)
target_sources(clamav_dbload_${TARGET}_fuzzer
PRIVATE clamav_dbload_fuzzer.cpp)

@ -51,73 +51,71 @@ class ClamAVState
cl_set_clcb_msg(clamav_message_callback);
cl_init(CL_INIT_DEFAULT);
}
~ClamAVState()
{
}
};
// Global with static initializer to setup an engine so we don't need to do
// that on each execution.
ClamAVState kClamAVState;
dboptions =
CL_DB_PHISHING | CL_DB_PHISHING_URLS |
CL_DB_BYTECODE | CL_DB_PUA | CL_DB_ENHANCED;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
cl_error_t ret;
char tmp_file_name[200] = {0};
unsigned int sigs = 0;
FILE* fuzzdb = NULL;
struct cl_engine* engine = NULL;
unsigned int dboptions;
__pid_t pid = getpid();
dboptions =
CL_DB_PHISHING | CL_DB_PHISHING_URLS |
CL_DB_BYTECODE | CL_DB_PUA | CL_DB_ENHANCED;
#if defined(CLAMAV_FUZZ_CDB)
tmp_db_name = "dbload_tmp_fuzz.cdb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.cdb", pid);
#elif defined(CLAMAV_FUZZ_CFG)
tmp_db_name = "dbload_tmp_fuzz.cfg";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.cfg", pid);
#elif defined(CLAMAV_FUZZ_CRB)
tmp_db_name = "dbload_tmp_fuzz.crb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.crb", pid);
#elif defined(CLAMAV_FUZZ_FP)
tmp_db_name = "dbload_tmp_fuzz.fp";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.fp", pid);
#elif defined(CLAMAV_FUZZ_FTM)
tmp_db_name = "dbload_tmp_fuzz.ftm";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.ftm", pid);
#elif defined(CLAMAV_FUZZ_HDB)
tmp_db_name = "dbload_tmp_fuzz.hdb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.hdb", pid);
#elif defined(CLAMAV_FUZZ_HSB)
tmp_db_name = "dbload_tmp_fuzz.hsb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.hsb", pid);
#elif defined(CLAMAV_FUZZ_IDB)
tmp_db_name = "dbload_tmp_fuzz.idb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.idb", pid);
#elif defined(CLAMAV_FUZZ_IGN)
tmp_db_name = "dbload_tmp_fuzz.ign";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.ign", pid);
#elif defined(CLAMAV_FUZZ_IGN2)
tmp_db_name = "dbload_tmp_fuzz.ign2";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.ign2", pid);
#elif defined(CLAMAV_FUZZ_LDB)
tmp_db_name = "dbload_tmp_fuzz.ldb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.ldb", pid);
#elif defined(CLAMAV_FUZZ_MDB)
tmp_db_name = "dbload_tmp_fuzz.mdb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.mdb", pid);
#elif defined(CLAMAV_FUZZ_MSB)
tmp_db_name = "dbload_tmp_fuzz.msb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.msb", pid);
#elif defined(CLAMAV_FUZZ_NDB)
tmp_db_name = "dbload_tmp_fuzz.ndb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.ndb", pid);
#elif defined(CLAMAV_FUZZ_PDB)
tmp_db_name = "dbload_tmp_fuzz.pdb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.pdb", pid);
#elif defined(CLAMAV_FUZZ_WDB)
tmp_db_name = "dbload_tmp_fuzz.wdb";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.wdb", pid);
#elif defined(CLAMAV_FUZZ_YARA)
tmp_db_name = "dbload_tmp_fuzz.yara";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d.yara", pid);
#else
tmp_db_name = "dbload_tmp_fuzz";
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.dbload.%d", pid);
#endif
}
~ClamAVState()
{
if (NULL != tmp_db_name) {
unlink(tmp_db_name);
}
}
const char* tmp_db_name;
unsigned int dboptions;
};
// Global with static initializer to setup an engine so we don't need to do
// that on each execution.
ClamAVState kClamAVState;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
cl_error_t ret;
unsigned int sigs = 0;
FILE* fuzzdb = NULL;
struct cl_engine* engine = NULL;
fuzzdb = fopen(kClamAVState.tmp_db_name, "w");
fuzzdb = fopen(tmp_file_name, "w");
fwrite(data, size, 1, fuzzdb);
fclose(fuzzdb);
@ -125,11 +123,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
engine = cl_engine_new();
/* load the fuzzer-generated sig db */
if (CL_SUCCESS != (ret = cl_load(
kClamAVState.tmp_db_name,
engine,
&sigs,
kClamAVState.dboptions))) {
if (CL_SUCCESS != (ret = cl_load(tmp_file_name,
engine,
&sigs,
dboptions))) {
printf("cl_load: %s\n", cl_strerror(ret));
goto done;
}
@ -147,5 +144,7 @@ done:
cl_engine_free(engine);
}
unlink(tmp_file_name);
return 0;
}

@ -58,37 +58,26 @@ class ClamAVState
memset(&scanopts, 0, sizeof(struct cl_scan_options));
#if defined(CLAMAV_FUZZ_ARCHIVE)
tmp_file_name = "tmp.scanfile.archive";
scanopts.parse |= CL_SCAN_PARSE_ARCHIVE;
#elif defined(CLAMAV_FUZZ_MAIL)
tmp_file_name = "tmp.scanfile.eml";
scanopts.parse |= CL_SCAN_PARSE_MAIL;
#elif defined(CLAMAV_FUZZ_OLE2)
tmp_file_name = "tmp.scanfile.ole2";
scanopts.parse |= CL_SCAN_PARSE_OLE2;
#elif defined(CLAMAV_FUZZ_PDF)
tmp_file_name = "tmp.scanfile.pdf";
scanopts.parse |= CL_SCAN_PARSE_PDF;
#elif defined(CLAMAV_FUZZ_HTML)
tmp_file_name = "tmp.scanfile.html";
scanopts.parse |= CL_SCAN_PARSE_HTML;
#elif defined(CLAMAV_FUZZ_PE)
tmp_file_name = "tmp.scanfile.pe";
scanopts.parse |= CL_SCAN_PARSE_PE;
#elif defined(CLAMAV_FUZZ_ELF)
tmp_file_name = "tmp.scanfile.elf";
scanopts.parse |= CL_SCAN_PARSE_ELF;
#elif defined(CLAMAV_FUZZ_SWF)
tmp_file_name = "tmp.scanfile.swf";
scanopts.parse |= CL_SCAN_PARSE_SWF;
#elif defined(CLAMAV_FUZZ_XMLDOCS)
tmp_file_name = "tmp.scanfile.docx";
scanopts.parse |= CL_SCAN_PARSE_XMLDOCS;
#elif defined(CLAMAV_FUZZ_HWP3)
tmp_file_name = "tmp.scanfile.hwp";
scanopts.parse |= CL_SCAN_PARSE_HWP3;
#else
tmp_file_name = "tmp.scanfile";
scanopts.parse |= ~(0);
#endif
scanopts.general |= CL_SCAN_GENERAL_HEURISTICS;
@ -100,14 +89,9 @@ class ClamAVState
~ClamAVState()
{
cl_engine_free(engine);
if (NULL != tmp_file_name) {
unlink(tmp_file_name);
}
}
struct cl_engine* engine;
const char* tmp_file_name;
struct cl_scan_options scanopts;
};
@ -117,9 +101,14 @@ ClamAVState kClamAVState;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
FILE* fuzzfile = NULL;
FILE* fuzzfile = NULL;
char tmp_file_name[200] = {0};
__pid_t pid = getpid();
fuzzfile = fopen(kClamAVState.tmp_file_name, "w");
snprintf(tmp_file_name, sizeof(tmp_file_name), "tmp.scanfile.%d", pid);
fuzzfile = fopen(tmp_file_name, "w");
fwrite(data, size, 1, fuzzfile);
fclose(fuzzfile);
@ -127,11 +116,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
unsigned long scanned = 0;
cl_scanfile(
kClamAVState.tmp_file_name,
tmp_file_name,
&virus_name,
&scanned,
kClamAVState.engine,
&kClamAVState.scanopts);
unlink(tmp_file_name);
return 0;
}

Loading…
Cancel
Save