From 14e9b843a427937ba0e75b7048eca59efd4faa4d Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Tue, 1 Nov 2022 21:39:21 -0700 Subject: [PATCH 1/2] Test: Add clamdscan --allmatch stickiness regression test Test that clamdscan --allmatch does not cause future clamdscans to run in allmatch mode. --- unit_tests/clamd_test.py | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/unit_tests/clamd_test.py b/unit_tests/clamd_test.py index 0bee5d5619..76cb5eb674 100644 --- a/unit_tests/clamd_test.py +++ b/unit_tests/clamd_test.py @@ -512,3 +512,48 @@ def test_clamd_09_clamdscan_ExcludePath(self): self.run_clamdscan('{}'.format(TC.path_tmp), expected_ec=1, expected_out=expected_out, unexpected_out=unexpected_out, use_valgrind=True) + + def test_clamd_10_allmatch_not_sticky(self): + ''' + Verify that a scanning without allmatch does not use allmatch mode, after scanning with allmatch. + This is a regression test for an issue where the allmatch scan option is sticky and any scans after an allmatch scan become an allmatch scan. + ''' + self.step_name('Testing clamdscan --allmatch is not sticky') + + # Get a list of Path()'s of each of signature file + test_path = TC.path_source / 'unit_tests' / 'input' / 'pe_allmatch' + database_files = list(test_path.glob('alert-sigs/*')) + + test_exe = test_path / 'test.exe' + + # Copy them to the database directory before starting ClamD + for db in database_files: + shutil.copy(str(db), str(TC.path_db)) + + # + # Start ClamD + # + self.start_clamd() + + poll = self.proc.poll() + assert poll == None # subprocess is alive if poll() returns None + + # Try first without --allmatch + output = self.execute_command('{clamdscan} -c {clamd_config} --wait --ping 10 {test_exe}'.format( + clamdscan=TC.clamdscan, clamd_config=TC.clamd_config, test_exe=test_exe)) + assert output.ec == 1 + assert output.out.count('FOUND') == 1 + + + # Next, try WITH --allmatch + output = self.execute_command('{clamdscan} -c {clamd_config} --allmatch {test_exe}'.format( + clamdscan=TC.clamdscan, clamd_config=TC.clamd_config, test_exe=test_exe)) + assert output.ec == 1 + assert output.out.count('FOUND') > 1 + + + # Try again without --allmatch + output = self.execute_command('{clamdscan} -c {clamd_config} {test_exe}'.format( + clamdscan=TC.clamdscan, clamd_config=TC.clamd_config, test_exe=test_exe)) + assert output.ec == 1 + assert output.out.count('FOUND') == 1 From 1ed59894087ae9fe32a2cbafc5dbfb92f814c4dc Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Tue, 1 Nov 2022 21:42:11 -0700 Subject: [PATCH 2/2] Fix clamdscan --allmatch stickiness bug If you run clamdscan with the --allmatch option, it will cause all subsequent clamdscan scans to have all-match mode enabled. This bug is specific to clamd / clamdscan and does not affect clamscan. The problem was introduced when we converted the scan options from a single integer bitfield to a struct. The scan options set by the clamdscan parameters should be saved in a local copy of the scan options, but instead it is saving a copy of the pointer to the scan options struct, and so any changes to the scan options affect future scans. --- clamd/session.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clamd/session.c b/clamd/session.c index a14109c223..92a7a8fb7e 100644 --- a/clamd/session.c +++ b/clamd/session.c @@ -191,13 +191,15 @@ int command(client_conn_t *conn, int *virus) { int desc = conn->sd; struct cl_engine *engine = conn->engine; - struct cl_scan_options *options = conn->options; + struct cl_scan_options options; const struct optstruct *opts = conn->opts; enum scan_type type = TYPE_INIT; int maxdirrec; int ret = 0; int flags = CLI_FTW_STD; + memcpy(&options, conn->options, sizeof(struct cl_scan_options)); + struct scan_cb_data scandata; struct cli_ftw_cbdata data; unsigned ok, error, total; @@ -218,7 +220,7 @@ int command(client_conn_t *conn, int *virus) scandata.group = conn->group; scandata.odesc = desc; scandata.conn = conn; - scandata.options = options; + scandata.options = &options; scandata.engine = engine; scandata.opts = opts; scandata.thr_pool = conn->thrpool; @@ -296,7 +298,7 @@ int command(client_conn_t *conn, int *virus) conn_reply_error(conn, "FILDES: didn't receive file descriptor."); return 1; } else { - ret = scanfd(conn, NULL, engine, options, opts, desc, 0); + ret = scanfd(conn, NULL, engine, &options, opts, desc, 0); if (ret == CL_VIRUS) { *virus = 1; ret = 0; @@ -327,7 +329,7 @@ int command(client_conn_t *conn, int *virus) return 0; case COMMAND_INSTREAMSCAN: thrmgr_setactivetask(NULL, "INSTREAM"); - ret = scanfd(conn, NULL, engine, options, opts, desc, 1); + ret = scanfd(conn, NULL, engine, &options, opts, desc, 1); if (ret == CL_VIRUS) { *virus = 1; ret = 0;