Browse code

Adds --max-scantime clamscan option and MaxScanTime clamd config option.

--max-scantime replaces the --timelimit clamscan option that had been experimental.
Default max-scantime set to 2 minutes (120000 milliseconds).

Micah Snyder authored on 2019/08/09 07:27:55
Showing 14 changed files
... ...
@@ -1,13 +1,39 @@
1 1
 # ClamAV News
2 2
 
3 3
 Note: This file refers to the source tarball. Things described here may differ
4
- slight
4
+ slightly from the binary packages.
5 5
 
6 6
 ## 0.101.4
7 7
 
8
-An out of bounds write was possible within ClamAV's NSIS bzip2 library when attempting decompression in cases where the number of selectors exceeded the max limit set by the library (CVE-2019-12900). The issue has been resolved by respecting that limit.
8
+ClamAV 0.101.4 is a security patch release that addresses the following issues.
9 9
 
10
-Thanks to Martin Simmons for reporting the issue [here](https://bugzilla.clamav.net/show_bug.cgi?id=12371)
10
+- An out of bounds write was possible within ClamAV's NSIS bzip2 library when
11
+  attempting decompression in cases where the number of selectors exceeded the
12
+  max limit set by the library (CVE-2019-12900). The issue has been resolved
13
+  by respecting that limit.
14
+
15
+  Thanks to Martin Simmons for reporting the issue [here](https://bugzilla.clamav.net/show_bug.cgi?id=12371)
16
+
17
+- A workaround for the zip-bomb vulnerability patch found in 0.101.3 was
18
+  identified. To remediate future denial of service conditions caused by
19
+  excessive scan times, a scan time limit has been introduced.
20
+
21
+  The default value is 2 minutes (120000 milliseconds).
22
+
23
+  To customize the time limit:
24
+
25
+  - use the `clamscan` `--max-scantime` option
26
+  - use the `clamd` `MaxScanTime` config option
27
+
28
+  Libclamav users may customize the time limit using the `cl_engine_set_num`
29
+  function. For example:
30
+
31
+  ```c
32
+      cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, time_limit_milliseconds)
33
+  ```
34
+
35
+  Thanks to David Fifield for reviewing the zip-bomb mitigation in 0.101.3
36
+  and reporting the issue.
11 37
 
12 38
 ## 0.101.3
13 39
 
... ...
@@ -88,7 +88,7 @@ static void scanner_thread(void *arg)
88 88
 #ifndef	_WIN32
89 89
     /* ignore all signals */
90 90
     sigfillset(&sigset);
91
-    /* The behavior of a process is undefined after it ignores a 
91
+    /* The behavior of a process is undefined after it ignores a
92 92
      * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
93 93
     sigdelset(&sigset, SIGFPE);
94 94
     sigdelset(&sigset, SIGILL);
... ...
@@ -552,7 +552,7 @@ static const char* parse_dispatch_cmd(client_conn_t *conn, struct fd_buf *buf, s
552 552
 		/* no more commands are accepted */
553 553
 		conn->mode = MODE_WAITREPLY;
554 554
 		/* Stop monitoring this FD, it will be closed either
555
-		 * by us, or by the scanner thread. 
555
+		 * by us, or by the scanner thread.
556 556
 		 * Never close a file descriptor that is being
557 557
 		 * monitored by poll()/select() from another thread,
558 558
 		 * because this can lead to subtle bugs such as:
... ...
@@ -631,7 +631,7 @@ static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct o
631 631
     int rc;
632 632
     size_t pos = *ppos;
633 633
     size_t cmdlen;
634
-    
634
+
635 635
     logg("$mode == MODE_STREAM\n");
636 636
     /* we received some data, set readtimeout */
637 637
     time(&buf->timeout_at);
... ...
@@ -754,12 +754,25 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
754 754
 	memset(&options, 0, sizeof(struct cl_scan_options));
755 755
 
756 756
     /* set up limits */
757
-    if((opt = optget(opts, "MaxScanSize"))->active) {
758
-	if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
759
-	    logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
760
-	    cl_engine_free(engine);
761
-	    return 1;
762
-	}
757
+    if ((opt = optget(opts, "MaxScanTime"))->active) {
758
+        if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
759
+            logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
760
+            cl_engine_free(engine);
761
+            return 1;
762
+        }
763
+    }
764
+    val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANTIME, NULL);
765
+    if (val)
766
+        logg("Limits: Global time limit set to %llu milliseconds.\n", val);
767
+    else
768
+        logg("^Limits: Global time limit protection disabled.\n");
769
+
770
+    if ((opt = optget(opts, "MaxScanSize"))->active) {
771
+        if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
772
+            logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
773
+            cl_engine_free(engine);
774
+            return 1;
775
+        }
763 776
     }
764 777
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL);
765 778
     if(val)
... ...
@@ -1016,7 +1029,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1016 1016
 
1017 1017
 	/* TODO: Remove deprecated option in a future feature release */
1018 1018
     if (optget(opts, "ScanPE")->enabled || optget(opts, "ScanELF")->enabled) {
1019
-        if ((optget(opts, "DetectBrokenExecutables")->enabled) || 
1019
+        if ((optget(opts, "DetectBrokenExecutables")->enabled) ||
1020 1020
 			(optget(opts, "AlertBrokenExecutables")->enabled)) {
1021 1021
             logg("Alerting on broken executables enabled.\n");
1022 1022
             options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
... ...
@@ -1039,7 +1052,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1039 1039
     if (optget(opts, "ScanOLE2")->enabled) {
1040 1040
         logg("OLE2 support enabled.\n");
1041 1041
         options.parse |= CL_SCAN_PARSE_OLE2;
1042
-		
1042
+
1043 1043
 		/* TODO: Remove deprecated option in a future feature release */
1044 1044
         if ((optget(opts, "OLE2BlockMacros")->enabled) ||
1045 1045
         	(optget(opts, "AlertOLE2Macros")->enabled)) {
... ...
@@ -1187,7 +1200,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1187 1187
 	int solaris_has_extended_stdio = 0;
1188 1188
 #endif
1189 1189
 	/* Condition to not run out of file descriptors:
1190
-	 * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE 
1190
+	 * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE
1191 1191
 	 * CLAMDFILES is 6: 3 standard FD + logfile + 2 FD for reloading the DB
1192 1192
 	 * */
1193 1193
 #ifdef C_SOLARIS
... ...
@@ -1314,12 +1327,12 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1314 1314
     sigdelset(&sigset, SIGHUP);
1315 1315
     sigdelset(&sigset, SIGPIPE);
1316 1316
     sigdelset(&sigset, SIGUSR2);
1317
-    /* The behavior of a process is undefined after it ignores a 
1317
+    /* The behavior of a process is undefined after it ignores a
1318 1318
      * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
1319 1319
     sigdelset(&sigset, SIGFPE);
1320 1320
     sigdelset(&sigset, SIGILL);
1321 1321
     sigdelset(&sigset, SIGSEGV);
1322
-#ifdef SIGBUS    
1322
+#ifdef SIGBUS
1323 1323
     sigdelset(&sigset, SIGBUS);
1324 1324
 #endif
1325 1325
     sigdelset(&sigset, SIGTSTP);
... ...
@@ -1663,4 +1676,4 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1663 1663
     logg("--- Stopped at %s", cli_ctime(&current_time, timestr, sizeof(timestr)));
1664 1664
 
1665 1665
     return ret;
1666
-} 
1666
+}
... ...
@@ -145,7 +145,7 @@ int main(int argc, char **argv)
145 145
 	    optfree(opts);
146 146
 	    return 2;
147 147
 	}
148
-    } else 
148
+    } else
149 149
 	logg_file = NULL;
150 150
 
151 151
     if(actsetup(opts)) {
... ...
@@ -277,6 +277,7 @@ void help(void)
277 277
     mprintf("    --nocerts                            Disable authenticode certificate chain verification in PE files\n");
278 278
     mprintf("    --dumpcerts                          Dump authenticode certificate chain in PE files\n");
279 279
     mprintf("\n");
280
+    mprintf("    --max-scantime=#n                    Scan time longer than this will be skipped and assumed clean\n");
280 281
     mprintf("    --max-filesize=#n                    Files larger than this will be skipped and assumed clean\n");
281 282
     mprintf("    --max-scansize=#n                    The maximum amount of data to scan for each container file (**)\n");
282 283
     mprintf("    --max-files=#n                       The maximum number of files to scan for each container file (**)\n");
... ...
@@ -340,7 +340,7 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
340 340
 
341 341
             return;
342 342
         }
343
-#endif    
343
+#endif
344 344
         if(!sb.st_size) {
345 345
             if(!printinfected)
346 346
                 logg("~%s: Empty file\n", filename);
... ...
@@ -674,7 +674,7 @@ int scanmanager(const struct optstruct *opts)
674 674
     }
675 675
 
676 676
     cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb);
677
-    
677
+
678 678
     if (optget(opts, "disable-cache")->enabled)
679 679
         cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
680 680
 
... ...
@@ -873,6 +873,24 @@ int scanmanager(const struct optstruct *opts)
873 873
 
874 874
     /* set limits */
875 875
 
876
+    /* TODO: Remove deprecated option in a future feature release */
877
+    if ((opt = optget(opts, "timelimit"))->active) {
878
+        if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
879
+            logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
880
+
881
+            cl_engine_free(engine);
882
+            return 2;
883
+        }
884
+    }
885
+    if ((opt = optget(opts, "max-scantime"))->active) {
886
+        if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
887
+            logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
888
+
889
+            cl_engine_free(engine);
890
+            return 2;
891
+        }
892
+    }
893
+
876 894
     if((opt = optget(opts, "max-scansize"))->active) {
877 895
         if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
878 896
             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
... ...
@@ -994,15 +1012,6 @@ int scanmanager(const struct optstruct *opts)
994 994
         }
995 995
     }
996 996
 
997
-    if ((opt = optget(opts, "timelimit"))->active) {
998
-        if ((ret = cl_engine_set_num(engine, CL_ENGINE_TIME_LIMIT, opt->numarg))) {
999
-            logg("!cli_engine_set_num(CL_ENGINE_TIME_LIMIT) failed: %s\n", cl_strerror(ret));
1000
-
1001
-            cl_engine_free(engine);
1002
-            return 2;
1003
-        }
1004
-    }
1005
-
1006 997
     if ((opt = optget(opts, "pcre-max-filesize"))->active) {
1007 998
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
1008 999
             logg("!cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
... ...
@@ -1038,7 +1047,7 @@ int scanmanager(const struct optstruct *opts)
1038 1038
         options.parse |= CL_SCAN_PARSE_ARCHIVE;
1039 1039
 
1040 1040
     /* TODO: Remove deprecated option in a future feature release */
1041
-    if ((optget(opts, "detect-broken")->enabled) || 
1041
+    if ((optget(opts, "detect-broken")->enabled) ||
1042 1042
         (optget(opts, "alert-broken")->enabled)) {
1043 1043
         options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
1044 1044
     }
... ...
@@ -1096,7 +1105,7 @@ int scanmanager(const struct optstruct *opts)
1096 1096
     }
1097 1097
 
1098 1098
     /* TODO: Remove deprecated option in a future feature release */
1099
-    if ((optget(opts, "block-max")->enabled) || 
1099
+    if ((optget(opts, "block-max")->enabled) ||
1100 1100
         (optget(opts, "alert-exceeds-max")->enabled)) {
1101 1101
         options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
1102 1102
     }
... ...
@@ -85,7 +85,7 @@ Example
85 85
 # Default: no
86 86
 #OfficialDatabaseOnly no
87 87
 
88
-# The daemon can work in local mode, network mode or both. 
88
+# The daemon can work in local mode, network mode or both.
89 89
 # Due to security reasons we recommend the local mode.
90 90
 
91 91
 # Path to a local socket file the daemon will listen on.
... ...
@@ -231,7 +231,7 @@ Example
231 231
 #DetectPUA yes
232 232
 
233 233
 # Exclude a specific PUA category. This directive can be used multiple times.
234
-# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for 
234
+# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for
235 235
 # the complete list of PUA categories.
236 236
 # Default: Load all categories (if DetectPUA is activated)
237 237
 #ExcludePUA NetTool
... ...
@@ -271,9 +271,9 @@ Example
271 271
 # the end of a scan. If an archive contains both a heuristically detected
272 272
 # virus/phish, and a real malware, the real malware will be reported
273 273
 #
274
-# Keep this disabled if you intend to handle "*.Heuristics.*" viruses 
274
+# Keep this disabled if you intend to handle "*.Heuristics.*" viruses
275 275
 # differently from "real" malware.
276
-# If a non-heuristically-detected virus (signature-based) is found first, 
276
+# If a non-heuristically-detected virus (signature-based) is found first,
277 277
 # the scan is interrupted immediately, regardless of this config option.
278 278
 #
279 279
 # Default: no
... ...
@@ -475,6 +475,16 @@ Example
475 475
 # The options below protect your system against Denial of Service attacks
476 476
 # using archive bombs.
477 477
 
478
+# This option sets the maximum amount of time to a scan may take.
479
+# In this version, this field only affects the scan time of ZIP archives.
480
+# Value of 0 disables the limit
481
+# Note: disabling this limit or setting it too high may result allow scanning
482
+# of certain files to lock up the scanning process/threads resulting in a Denial
483
+# of Service.
484
+# Time is in milliseconds.
485
+# Default: 120000
486
+#MaxScanTime 300000
487
+
478 488
 # This option sets the maximum amount of data to be scanned for each input
479 489
 # file.
480 490
 # Archives and other containers are recursively extracted and scanned up to
... ...
@@ -697,7 +707,7 @@ Example
697 697
 ## Bytecode
698 698
 ##
699 699
 
700
-# With this option enabled ClamAV will load bytecode from the database. 
700
+# With this option enabled ClamAV will load bytecode from the database.
701 701
 # It is highly recommended you keep this option on, otherwise you'll miss
702 702
 # detections for many new viruses.
703 703
 # Default: yes
... ...
@@ -721,7 +731,7 @@ Example
721 721
 #BytecodeSecurity TrustSigned
722 722
 
723 723
 # Set bytecode timeout in milliseconds.
724
-# 
724
+#
725 725
 # Default: 5000
726 726
 # BytecodeTimeout 1000
727 727
 
... ...
@@ -298,7 +298,7 @@ enum cl_engine_field {
298 298
     CL_ENGINE_MAX_PARTITIONS,       /* uint32_t */
299 299
     CL_ENGINE_MAX_ICONSPE,          /* uint32_t */
300 300
     CL_ENGINE_MAX_RECHWP3,          /* uint32_t */
301
-    CL_ENGINE_TIME_LIMIT,           /* uint32_t */
301
+    CL_ENGINE_MAX_SCANTIME,         /* uint32_t */
302 302
     CL_ENGINE_PCRE_MATCH_LIMIT,     /* uint64_t */
303 303
     CL_ENGINE_PCRE_RECMATCH_LIMIT,  /* uint64_t */
304 304
     CL_ENGINE_PCRE_MAX_FILESIZE,    /* uint64_t */
... ...
@@ -31,6 +31,7 @@
31 31
 
32 32
 #define CLI_DEFAULT_BM_OFFMODE_FSIZE	262144
33 33
 
34
+#define CLI_DEFAULT_MAXSCANTIME     120000
34 35
 #define CLI_DEFAULT_MAXSCANSIZE	    104857600
35 36
 #define CLI_DEFAULT_MAXFILESIZE	    26214400
36 37
 #define CLI_DEFAULT_MAXRECLEVEL	    16
... ...
@@ -687,6 +687,12 @@ int cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const char **
687 687
 
688 688
         /* if the global flag is set, loop through the scanning */
689 689
         do {
690
+            if (cli_checktimelimit(ctx) != CL_SUCCESS) {
691
+                cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime);
692
+                ret = CL_ETIMEOUT;
693
+                break;
694
+            }
695
+
690 696
             /* reset the match results */
691 697
             if ((ret = cli_pcre_results_reset(&p_res, pd)) != CL_SUCCESS)
692 698
                 break;
... ...
@@ -262,7 +262,7 @@ const char *cl_strerror(int clerror)
262 262
 	case CL_EMEM:
263 263
 	    return "Can't allocate memory";
264 264
 	case CL_ETIMEOUT:
265
-	    return "Time limit reached";
265
+	    return "CL_ETIMEOUT: Time limit reached";
266 266
 	/* internal (needed for debug messages) */
267 267
 	case CL_EMAXREC:
268 268
 	    return "CL_EMAXREC";
... ...
@@ -324,6 +324,7 @@ struct cl_engine *cl_engine_new(void)
324 324
     }
325 325
 
326 326
     /* Setup default limits */
327
+    new->maxscantime = CLI_DEFAULT_MAXSCANTIME;
327 328
     new->maxscansize = CLI_DEFAULT_MAXSCANSIZE;
328 329
     new->maxfilesize = CLI_DEFAULT_MAXFILESIZE;
329 330
     new->maxreclevel = CLI_DEFAULT_MAXRECLEVEL;
... ...
@@ -616,9 +617,9 @@ int cl_engine_set_num(struct cl_engine *engine, enum cl_engine_field field, long
616 616
 	case CL_ENGINE_MAX_RECHWP3:
617 617
 	    engine->maxrechwp3 = (uint32_t)num;
618 618
 	    break;
619
-	case CL_ENGINE_TIME_LIMIT:
620
-	    engine->time_limit = (uint32_t)num;
621
-	    break;
619
+    case CL_ENGINE_MAX_SCANTIME:
620
+        engine->maxscantime = (uint32_t)num;
621
+        break;
622 622
 	case CL_ENGINE_PCRE_MATCH_LIMIT:
623 623
 	    engine->pcre_match_limit = (uint64_t)num;
624 624
 	    break;
... ...
@@ -717,8 +718,8 @@ long long cl_engine_get_num(const struct cl_engine *engine, enum cl_engine_field
717 717
 	    return engine->maxiconspe;
718 718
     case CL_ENGINE_MAX_RECHWP3:
719 719
 	    return engine->maxrechwp3;
720
-	case CL_ENGINE_TIME_LIMIT:
721
-            return engine->time_limit;
720
+    case CL_ENGINE_MAX_SCANTIME:
721
+        return engine->maxscantime;
722 722
 	case CL_ENGINE_PCRE_MATCH_LIMIT:
723 723
 	    return engine->pcre_match_limit;
724 724
 	case CL_ENGINE_PCRE_RECMATCH_LIMIT:
... ...
@@ -798,6 +799,7 @@ struct cl_settings *cl_engine_settings_copy(const struct cl_engine *engine)
798 798
     settings->ac_maxdepth = engine->ac_maxdepth;
799 799
     settings->tmpdir = engine->tmpdir ? strdup(engine->tmpdir) : NULL;
800 800
     settings->keeptmp = engine->keeptmp;
801
+    settings->maxscantime = engine->maxscantime;
801 802
     settings->maxscansize = engine->maxscansize;
802 803
     settings->maxfilesize = engine->maxfilesize;
803 804
     settings->maxreclevel = engine->maxreclevel;
... ...
@@ -852,6 +854,7 @@ int cl_engine_settings_apply(struct cl_engine *engine, const struct cl_settings
852 852
     engine->ac_mindepth = settings->ac_mindepth;
853 853
     engine->ac_maxdepth = settings->ac_maxdepth;
854 854
     engine->keeptmp = settings->keeptmp;
855
+    engine->maxscantime = settings->maxscantime;
855 856
     engine->maxscansize = settings->maxscansize;
856 857
     engine->maxfilesize = settings->maxfilesize;
857 858
     engine->maxreclevel = settings->maxreclevel;
... ...
@@ -940,8 +943,9 @@ void cli_check_blockmax(cli_ctx *ctx, int rc)
940 940
     }
941 941
 }
942 942
 
943
-int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3) {
944
-    int ret = CL_SUCCESS;
943
+cl_error_t cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3)
944
+{
945
+    cl_error_t ret = CL_SUCCESS;
945 946
     unsigned long needed;
946 947
 
947 948
     /* if called without limits, go on, unpack, scan */
... ...
@@ -950,6 +954,9 @@ int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned
950 950
     needed = (need1>need2)?need1:need2;
951 951
     needed = (needed>need3)?needed:need3;
952 952
 
953
+    /* Enforce timelimit */
954
+    ret = cli_checktimelimit(ctx);
955
+
953 956
     /* if we have global scan limits */
954 957
     if(needed && ctx->engine->maxscansize) {
955 958
         /* if the remaining scansize is too small... */
... ...
@@ -978,8 +985,9 @@ int cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned
978 978
     return ret;
979 979
 }
980 980
 
981
-int cli_updatelimits(cli_ctx *ctx, unsigned long needed) {
982
-    int ret=cli_checklimits("cli_updatelimits", ctx, needed, 0, 0);
981
+cl_error_t cli_updatelimits(cli_ctx *ctx, unsigned long needed)
982
+{
983
+    cl_error_t ret = cli_checklimits("cli_updatelimits", ctx, needed, 0, 0);
983 984
 
984 985
     if (ret != CL_CLEAN) return ret;
985 986
     ctx->scannedfiles++;
... ...
@@ -989,18 +997,33 @@ int cli_updatelimits(cli_ctx *ctx, unsigned long needed) {
989 989
     return CL_CLEAN;
990 990
 }
991 991
 
992
-int cli_checktimelimit(cli_ctx *ctx)
992
+/**
993
+ * @brief Check if we've exceeded the time limit.
994
+ * If ctx is NULL, there can be no timelimit so just return success.
995
+ *
996
+ * @param ctx         The scanning context.
997
+ * @return cl_error_t CL_SUCCESS if has not exceeded, CL_ETIMEOUT if has exceeded.
998
+ */
999
+cl_error_t cli_checktimelimit(cli_ctx *ctx)
993 1000
 {
1001
+    cl_error_t ret = CL_SUCCESS;
1002
+
1003
+    if (NULL == ctx) {
1004
+        goto done;
1005
+    }
1006
+
994 1007
     if (ctx->time_limit.tv_sec != 0) {
995 1008
         struct timeval now;
996 1009
         if (gettimeofday(&now, NULL) == 0) {
997
-            if (now.tv_sec < ctx->time_limit.tv_sec)
998
-                return CL_SUCCESS;
999
-            if (now.tv_sec > ctx->time_limit.tv_sec || now.tv_usec > ctx->time_limit.tv_usec)
1000
-                return CL_ETIMEOUT;
1010
+            if (now.tv_sec > ctx->time_limit.tv_sec)
1011
+                ret = CL_ETIMEOUT;
1012
+            else if (now.tv_sec == ctx->time_limit.tv_sec && now.tv_usec > ctx->time_limit.tv_usec)
1013
+                ret = CL_ETIMEOUT;
1001 1014
         }
1002 1015
     }
1003
-    return CL_SUCCESS;
1016
+
1017
+done:
1018
+    return ret;
1004 1019
 }
1005 1020
 
1006 1021
 /*
... ...
@@ -1078,7 +1101,7 @@ int cli_unlink(const char* pathname)
1078 1078
 {
1079 1079
     if (unlink(pathname) == -1) {
1080 1080
 #ifdef _WIN32
1081
-        /* Windows may fail to unlink a file if it is marked read-only, 
1081
+        /* Windows may fail to unlink a file if it is marked read-only,
1082 1082
 		 * even if the user has permissions to delete the file. */
1083 1083
         if (-1 == _chmod(pathname, _S_IWRITE)) {
1084 1084
             char err[128];
... ...
@@ -1105,7 +1128,7 @@ void cli_virus_found_cb(cli_ctx * ctx)
1105 1105
         ctx->engine->cb_virus_found(fmap_fd(*ctx->fmap), (const char *)*ctx->virname, ctx->cb_ctx);
1106 1106
 }
1107 1107
 
1108
-int cli_append_possibly_unwanted(cli_ctx * ctx, const char * virname)
1108
+cl_error_t cli_append_possibly_unwanted(cli_ctx *ctx, const char *virname)
1109 1109
 {
1110 1110
     if (SCAN_ALLMATCHES)
1111 1111
         return cli_append_virus(ctx, virname);
... ...
@@ -1128,7 +1151,7 @@ int cli_append_virus(cli_ctx * ctx, const char * virname)
1128 1128
     if (!SCAN_ALLMATCHES && ctx->num_viruses != 0)
1129 1129
         if (SCAN_HEURISTIC_PRECEDENCE)
1130 1130
             return CL_CLEAN;
1131
-    if (ctx->limit_exceeded == 0 || SCAN_ALLMATCHES) { 
1131
+    if (ctx->limit_exceeded == 0 || SCAN_ALLMATCHES) {
1132 1132
         ctx->num_viruses++;
1133 1133
         *ctx->virname = virname;
1134 1134
         cli_virus_found_cb(ctx);
... ...
@@ -1225,7 +1248,7 @@ int
1225 1225
 cli_rmdirs(const char *name)
1226 1226
 {
1227 1227
 	int rc;
1228
-	STATBUF statb;	
1228
+	STATBUF statb;
1229 1229
 	DIR *dd;
1230 1230
 	struct dirent *dent;
1231 1231
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
... ...
@@ -1288,7 +1311,7 @@ cli_rmdirs(const char *name)
1288 1288
 	return -1;
1289 1289
     }
1290 1290
 
1291
-    return rc;	
1291
+    return rc;
1292 1292
 }
1293 1293
 #else
1294 1294
 int cli_rmdirs(const char *dirname)
... ...
@@ -1367,7 +1390,7 @@ int cli_rmdirs(const char *dirname)
1367 1367
 	    rewinddir(dd);
1368 1368
 	}
1369 1369
 
1370
-    } else { 
1370
+    } else {
1371 1371
 	return -1;
1372 1372
     }
1373 1373
 
... ...
@@ -1397,7 +1420,7 @@ static unsigned long nearest_power(unsigned long num)
1397 1397
 bitset_t *cli_bitset_init(void)
1398 1398
 {
1399 1399
 	bitset_t *bs;
1400
-	
1400
+
1401 1401
 	bs = cli_malloc(sizeof(bitset_t));
1402 1402
 	if (!bs) {
1403 1403
         cli_errmsg("cli_bitset_init: Unable to allocate memory for bs %llu\n", (long long unsigned)sizeof(bitset_t));
... ...
@@ -1428,7 +1451,7 @@ static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size)
1428 1428
 {
1429 1429
 	unsigned long new_length;
1430 1430
 	unsigned char *new_bitset;
1431
-	
1431
+
1432 1432
 	new_length = nearest_power(min_size);
1433 1433
 	new_bitset = (unsigned char *) cli_realloc(bs->bitset, new_length);
1434 1434
 	if (!new_bitset) {
... ...
@@ -1443,7 +1466,7 @@ static bitset_t *bitset_realloc(bitset_t *bs, unsigned long min_size)
1443 1443
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset)
1444 1444
 {
1445 1445
 	unsigned long char_offset;
1446
-	
1446
+
1447 1447
 	char_offset = bit_offset / BITS_PER_CHAR;
1448 1448
 	bit_offset = bit_offset % BITS_PER_CHAR;
1449 1449
 
... ...
@@ -1460,11 +1483,11 @@ int cli_bitset_set(bitset_t *bs, unsigned long bit_offset)
1460 1460
 int cli_bitset_test(bitset_t *bs, unsigned long bit_offset)
1461 1461
 {
1462 1462
 	unsigned long char_offset;
1463
-	
1463
+
1464 1464
 	char_offset = bit_offset / BITS_PER_CHAR;
1465 1465
 	bit_offset = bit_offset % BITS_PER_CHAR;
1466 1466
 
1467
-	if (char_offset >= bs->length) {	
1467
+	if (char_offset >= bs->length) {
1468 1468
 		return FALSE;
1469 1469
 	}
1470 1470
 	return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
... ...
@@ -286,6 +286,7 @@ struct cl_engine {
286 286
     uint64_t engine_options;
287 287
 
288 288
     /* Limits */
289
+    uint32_t maxscantime;  /* Time limit (in milliseconds) */
289 290
     uint64_t maxscansize;  /* during the scanning of archives this size
290 291
 				     * will never be exceeded
291 292
 				     */
... ...
@@ -405,9 +406,6 @@ struct cl_engine {
405 405
     uint32_t maxiconspe; /* max number of icons to scan for PE */
406 406
     uint32_t maxrechwp3; /* max recursive calls for HWP3 parsing */
407 407
 
408
-    /* millisecond time limit for preclassification scanning */
409
-    uint32_t time_limit;
410
-
411 408
     /* PCRE matching limitations */
412 409
     uint64_t pcre_match_limit;
413 410
     uint64_t pcre_recmatch_limit;
... ...
@@ -429,6 +427,7 @@ struct cl_settings {
429 429
     uint32_t ac_maxdepth;
430 430
     char *tmpdir;
431 431
     uint32_t keeptmp;
432
+    uint32_t maxscantime;
432 433
     uint64_t maxscansize;
433 434
     uint64_t maxfilesize;
434 435
     uint32_t maxreclevel;
... ...
@@ -811,21 +810,20 @@ cl_error_t cli_gentempfd_with_prefix(const char* dir, char* prefix, char** name,
811 811
 
812 812
 unsigned int cli_rndnum(unsigned int max);
813 813
 int cli_filecopy(const char *src, const char *dest);
814
-int cli_mapscan(fmap_t *map, off_t offset, size_t size, cli_ctx *ctx, cli_file_t type);
815 814
 bitset_t *cli_bitset_init(void);
816 815
 void cli_bitset_free(bitset_t *bs);
817 816
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset);
818 817
 int cli_bitset_test(bitset_t *bs, unsigned long bit_offset);
819 818
 const char* cli_ctime(const time_t *timep, char *buf, const size_t bufsize);
820 819
 void cli_check_blockmax(cli_ctx *, int);
821
-int cli_checklimits(const char *, cli_ctx *, unsigned long, unsigned long, unsigned long);
822
-int cli_updatelimits(cli_ctx *, unsigned long);
820
+cl_error_t cli_checklimits(const char *, cli_ctx *, unsigned long, unsigned long, unsigned long);
821
+cl_error_t cli_updatelimits(cli_ctx *, unsigned long);
823 822
 unsigned long cli_getsizelimit(cli_ctx *, unsigned long);
824 823
 int cli_matchregex(const char *str, const char *regex);
825 824
 void cli_qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *));
826 825
 void cli_qsort_r(void *a, size_t n, size_t es, int (*cmp)(const void*, const void *, const void *), void *arg);
827
-int cli_checktimelimit(cli_ctx *ctx);
828
-int cli_append_possibly_unwanted(cli_ctx * ctx, const char * virname);
826
+cl_error_t cli_checktimelimit(cli_ctx *ctx);
827
+cl_error_t cli_append_possibly_unwanted(cli_ctx *ctx, const char *virname);
829 828
 
830 829
 /* symlink behaviour */
831 830
 #define CLI_FTW_FOLLOW_FILE_SYMLINK 0x01
... ...
@@ -3743,7 +3743,6 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
3743 3743
             case CL_ETMPFILE:
3744 3744
             case CL_ETMPDIR:
3745 3745
             case CL_EMEM:
3746
-            case CL_ETIMEOUT:
3747 3746
                 cli_dbgmsg("Descriptor[%d]: cli_scanraw error %s\n", fmap_fd(*ctx->fmap), cl_strerror(res));
3748 3747
                 cli_bitset_free(ctx->hook_lsig_matches);
3749 3748
                 ctx->hook_lsig_matches = old_hook_lsig_matches;
... ...
@@ -3756,7 +3755,15 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
3756 3756
                 cli_bitset_free(ctx->hook_lsig_matches);
3757 3757
                 ctx->hook_lsig_matches = old_hook_lsig_matches;
3758 3758
                 return magic_scandesc_cleanup(ctx, type, hash, hashed_size, cache_clean, ret, parent_property);
3759
-            /* "MAX" conditions should still fully scan the current file */
3759
+            /* The CL_ETIMEOUT "MAX" condition should set exceeds max flag and exit out quietly. */
3760
+            case CL_ETIMEOUT:
3761
+                cli_check_blockmax(ctx, ret);
3762
+                cli_bitset_free(ctx->hook_lsig_matches);
3763
+                ctx->hook_lsig_matches = old_hook_lsig_matches;
3764
+                cli_dbgmsg("Descriptor[%d]: Stopping after cli_scanraw reached %s\n",
3765
+                            fmap_fd(*ctx->fmap), cl_strerror(res));
3766
+                return magic_scandesc_cleanup(ctx, type, hash, hashed_size, cache_clean, CL_CLEAN, parent_property);
3767
+            /* All other "MAX" conditions should still fully scan the current file */
3760 3768
             case CL_EMAXREC:
3761 3769
             case CL_EMAXSIZE:
3762 3770
             case CL_EMAXFILES:
... ...
@@ -3820,14 +3827,16 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
3820 3820
 
3821 3821
     switch (ret)
3822 3822
     {
3823
-    /* Malformed file cases */
3824
-    case CL_EFORMAT:
3825
-    case CL_EREAD:
3826
-    case CL_EUNPACK:
3827 3823
     /* Limits exceeded */
3824
+    case CL_ETIMEOUT:
3828 3825
     case CL_EMAXREC:
3829 3826
     case CL_EMAXSIZE:
3830 3827
     case CL_EMAXFILES:
3828
+        cli_check_blockmax(ctx, ret);
3829
+    /* Malformed file cases */
3830
+    case CL_EFORMAT:
3831
+    case CL_EREAD:
3832
+    case CL_EUNPACK:
3831 3833
         cli_dbgmsg("Descriptor[%d]: %s\n", fmap_fd(*ctx->fmap), cl_strerror(ret));
3832 3834
 #if HAVE_JSON
3833 3835
         ctx->wrkproperty = parent_property;
... ...
@@ -3868,7 +3877,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx
3868 3868
 
3869 3869
         status = CL_ESTAT;
3870 3870
         cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__);
3871
-        goto done;  
3871
+        goto done;
3872 3872
     }
3873 3873
     if (sb.st_size <= 5)
3874 3874
     {
... ...
@@ -3876,7 +3885,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx
3876 3876
 
3877 3877
         status = CL_CLEAN;
3878 3878
         cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__);
3879
-        goto done;  
3879
+        goto done;
3880 3880
     }
3881 3881
 
3882 3882
     ctx->fmap++;
... ...
@@ -3889,7 +3898,7 @@ static cl_error_t cli_base_scandesc(int desc, const char *filepath, cli_ctx *ctx
3889 3889
 
3890 3890
         status = CL_EMEM;
3891 3891
         cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", status, __AT__);
3892
-        goto done;  
3892
+        goto done;
3893 3893
     }
3894 3894
     perf_stop(ctx, PERFT_MAP);
3895 3895
 
... ...
@@ -4144,12 +4153,12 @@ static cl_error_t scan_common(int desc, cl_fmap_t *map, const char * filepath, c
4144 4144
     }
4145 4145
     perf_init(&ctx);
4146 4146
 
4147
-    if (ctx.options->general & CL_SCAN_GENERAL_COLLECT_METADATA && ctx.engine->time_limit != 0)
4147
+    if (ctx.engine->maxscantime != 0)
4148 4148
     {
4149 4149
         if (gettimeofday(&ctx.time_limit, NULL) == 0)
4150 4150
         {
4151
-            uint32_t secs = ctx.engine->time_limit / 1000;
4152
-            uint32_t usecs = (ctx.engine->time_limit % 1000) * 1000;
4151
+            uint32_t secs = ctx.engine->maxscantime / 1000;
4152
+            uint32_t usecs = (ctx.engine->maxscantime % 1000) * 1000;
4153 4153
             ctx.time_limit.tv_sec += secs;
4154 4154
             ctx.time_limit.tv_usec += usecs;
4155 4155
             if (ctx.time_limit.tv_usec >= 1000000)
... ...
@@ -4161,7 +4170,7 @@ static cl_error_t scan_common(int desc, cl_fmap_t *map, const char * filepath, c
4161 4161
         else
4162 4162
         {
4163 4163
             char buf[64];
4164
-            cli_dbgmsg("scan_common; gettimeofday error: %s\n", cli_strerror(errno, buf, 64));
4164
+            cli_dbgmsg("scan_common: gettimeofday error: %s\n", cli_strerror(errno, buf, 64));
4165 4165
         }
4166 4166
     }
4167 4167
 
... ...
@@ -777,6 +777,11 @@ int cli_unzip(cli_ctx *ctx) {
777 777
 	      cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
778 778
 	      ret=CL_EMAXFILES;
779 779
 	  }
780
+
781
+    if (cli_checktimelimit(ctx) != CL_SUCCESS) {
782
+        cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime);
783
+        ret = CL_ETIMEOUT;
784
+    }
780 785
     /*
781 786
      * Detect overlapping files and zip bombs.
782 787
      */
... ...
@@ -288,13 +288,13 @@ const struct clam_option __clam_options[] = {
288 288
     /* Scan options */
289 289
     { "Bytecode", "bytecode", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes" },
290 290
 
291
-    { "BytecodeSecurity", NULL, 0, CLOPT_TYPE_STRING, "^(TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD, 
291
+    { "BytecodeSecurity", NULL, 0, CLOPT_TYPE_STRING, "^(TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD,
292 292
 	"Set bytecode security level.\nPossible values:\n\tTrustSigned - trust bytecode loaded from signed .c[lv]d files,\n\t\t insert runtime safety checks for bytecode loaded from other sources\n\tParanoid - don't trust any bytecode, insert runtime checks for all\nRecommended: TrustSigned, because bytecode in .cvd files already has these checks.","TrustSigned"},
293 293
 
294
-    { "BytecodeTimeout", "bytecode-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
294
+    { "BytecodeTimeout", "bytecode-timeout", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN,
295 295
 	"Set bytecode timeout in milliseconds.","5000"},
296 296
 
297
-    { "BytecodeUnsigned", "bytecode-unsigned", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
297
+    { "BytecodeUnsigned", "bytecode-unsigned", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN,
298 298
 	"Allow loading bytecode from outside digitally signed .c[lv]d files.","no"},
299 299
 
300 300
     { "BytecodeMode", "bytecode-mode", 0, CLOPT_TYPE_STRING, "^(Auto|ForceJIT|ForceInterpreter|Test)$", -1, "Auto", FLAG_REQUIRED, OPT_CLAMD | OPT_CLAMSCAN,
... ...
@@ -366,6 +366,8 @@ const struct clam_option __clam_options[] = {
366 366
 
367 367
     { "ForceToDisk", "force-to-disk", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option causes memory or nested map scans to dump the content to disk.\nIf you turn on this option, more data is written to disk and is available\nwhen the leave-temps option is enabled at the cost of more disk writes.", "no" },
368 368
 
369
+    { "MaxScanTime", "max-scantime", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum amount of time a scan may take to complete.\nIn this version, this field only affects the scan time of ZIP archives.\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result allow scanning\nof certain files to lock up the scanning process/threads resulting in a Denial of Service.\nThe value is in milliseconds.", "120000"},
370
+
369 371
     { "MaxScanSize", "max-scansize", 0, CLOPT_TYPE_SIZE, MATCH_SIZE, CLI_DEFAULT_MAXSCANSIZE, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum amount of data to be scanned for each input file.\nArchives and other containers are recursively extracted and scanned up to this\nvalue.\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result in severe\ndamage.", "100M" },
370 372
 
371 373
     { "MaxFileSize", "max-filesize", 0, CLOPT_TYPE_SIZE, MATCH_SIZE, CLI_DEFAULT_MAXFILESIZE, NULL, 0, OPT_CLAMD | OPT_MILTER | OPT_CLAMSCAN, "Files/messages larger than this limit won't be scanned. Affects the input\nfile itself as well as files contained inside it (when the input file is\nan archive, a document or some other kind of container).\nThe value of 0 disables the limit.\nWARNING: disabling this limit or setting it too high may result in severe\ndamage to the system.", "25M" },
... ...
@@ -391,8 +393,6 @@ const struct clam_option __clam_options[] = {
391 391
 
392 392
     { "MaxRecHWP3", "max-rechwp3", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_MAXRECHWP3, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum recursive calls to HWP3 parsing function.\nHWP3 files using more than this limit will be terminated and alert the user.\nScans will be unable to scan any HWP3 attachments if the recursive limit is reached.\nNegative values are not allowed.\nWARNING: setting this limit too high may result in severe damage or impact performance.", "16" },
393 393
 
394
-    { "TimeLimit", "timelimit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMSCAN, "This clamscan option is currently for testing only. It sets the engine parameter CL_ENGINE_TIME_LIMIT. The value is in milliseconds.", "0" },
395
-
396 394
     { "PCREMatchLimit", "pcre-match-limit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_PCRE_MATCH_LIMIT, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum calls to the PCRE match function during an instance of regex matching.\nInstances using more than this limit will be terminated and alert the user but the scan will continue.\nFor more information on match_limit, see the PCRE documentation.\nNegative values are not allowed.\nWARNING: setting this limit too high may severely impact performance.", "100000" },
397 395
 
398 396
     { "PCRERecMatchLimit", "pcre-recmatch-limit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CLI_DEFAULT_PCRE_RECMATCH_LIMIT, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option sets the maximum recursive calls to the PCRE match function during an instance of regex matching.\nInstances using more than this limit will be terminated and alert the user but the scan will continue.\nFor more information on match_limit_recursion, see the PCRE documentation.\nNegative values are not allowed and values > PCREMatchLimit are superfluous.\nWARNING: setting this limit too high may severely impact performance.", "5000" },
... ...
@@ -491,6 +491,7 @@ const struct clam_option __clam_options[] = {
491 491
 
492 492
     /* Deprecated options */
493 493
 
494
+    { "TimeLimit", "timelimit", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_CLAMSCAN | OPT_DEPRECATED, "Deprecated option to set the max-scantime.\nThe value is in milliseconds.", "120000"},
494 495
     { "DetectBrokenExecutables", "detect-broken", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN | OPT_DEPRECATED, "Deprecated option to alert on broken PE and ELF executable files.", "no" },
495 496
     { "AlgorithmicDetection", "algorithmic-detection", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Deprecated option to enable heuristic alerts (e.g. \"Heuristics.<sig name>\")", "no" },
496 497
     { "BlockMax", "block-max", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "", "" },
... ...
@@ -1188,39 +1189,39 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1188 1188
 	long long numarg, lnumarg;
1189 1189
 	int regflags = REG_EXTENDED | REG_NOSUB;
1190 1190
     const struct clam_option *optentry = NULL;
1191
-    
1191
+
1192 1192
     if(oldopts)
1193 1193
         opts = oldopts;
1194
-    
1195
-    
1194
+
1195
+
1196 1196
     for(i = 0; ; i++) {
1197 1197
         optentry = &clam_options[i];
1198 1198
         if(!optentry->name && !optentry->longopt)
1199 1199
             break;
1200
-        
1200
+
1201 1201
         if(((optentry->owner & toolmask) && ((optentry->owner & toolmask) != OPT_DEPRECATED)) || (ignore && (optentry->owner & ignore))) {
1202 1202
             if(!oldopts && optadd(&opts, &opts_last, optentry->name, optentry->longopt, optentry->strarg, optentry->numarg, optentry->flags, i) < 0) {
1203 1203
                 fprintf(stderr, "ERROR: optparse: Can't register new option (not enough memory)\n");
1204 1204
                 optfree(opts);
1205 1205
                 return NULL;
1206 1206
             }
1207
-            
1207
+
1208 1208
         }
1209 1209
     }
1210
-    
1210
+
1211 1211
     if(MAX(sc, lc) > MAXCMDOPTS) {
1212 1212
 	    fprintf(stderr, "ERROR: optparse: (short|long)opts[] is too small\n");
1213 1213
 	    optfree(opts);
1214 1214
 	    return NULL;
1215 1215
 	}
1216
-    
1216
+
1217 1217
     while(1) {
1218 1218
         if(!name) {
1219 1219
             fprintf(stderr, "ERROR: Problem parsing options (name == NULL)\n");
1220 1220
             err = 1;
1221 1221
             break;
1222 1222
         }
1223
-        
1223
+
1224 1224
         opt = optget_i(opts, name);
1225 1225
         if(!opt) {
1226 1226
             if(verbose)
... ...
@@ -1229,13 +1230,13 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1229 1229
             break;
1230 1230
         }
1231 1231
         optentry = &clam_options[opt->idx];
1232
-        
1232
+
1233 1233
         if(ignore && (optentry->owner & ignore) && !(optentry->owner & toolmask)) {
1234 1234
             if(verbose)
1235 1235
                 fprintf(stderr, "WARNING: Ignoring unsupported option %s\n", opt->name);
1236 1236
             continue;
1237 1237
         }
1238
-        
1238
+
1239 1239
         if(optentry->owner & OPT_DEPRECATED) {
1240 1240
             if(toolmask & OPT_DEPRECATED) {
1241 1241
                 if(optaddarg(opts, name, "foo", 1) < 0) {
... ...
@@ -1249,11 +1250,11 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1249 1249
             }
1250 1250
             continue;
1251 1251
         }
1252
-        
1252
+
1253 1253
         if(optentry->regex) {
1254 1254
             if(!(optentry->flags & FLAG_REG_CASE))
1255 1255
                 regflags |= REG_ICASE;
1256
-            
1256
+
1257 1257
             if(cli_regcomp(&regex, optentry->regex, regflags)) {
1258 1258
                 fprintf(stderr, "ERROR: optparse: Can't compile regular expression %s for option %s\n", optentry->regex, name);
1259 1259
                 err = 1;
... ...
@@ -1267,15 +1268,15 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1267 1267
                 break;
1268 1268
             }
1269 1269
         }
1270
-        
1270
+
1271 1271
         numarg = -1;
1272 1272
         switch(optentry->argtype) {
1273 1273
             case CLOPT_TYPE_STRING:
1274 1274
                 if(!arg)
1275 1275
                     arg = optentry->strarg;
1276
-                
1276
+
1277 1277
                 break;
1278
-                
1278
+
1279 1279
             case CLOPT_TYPE_NUMBER:
1280 1280
                 if (arg)
1281 1281
                     numarg = atoi(arg);
... ...
@@ -1283,7 +1284,7 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1283 1283
                     numarg = 0;
1284 1284
                 arg = NULL;
1285 1285
                 break;
1286
-                
1286
+
1287 1287
             case CLOPT_TYPE_SIZE:
1288 1288
                 errno = 0;
1289 1289
                 if(arg)
... ...
@@ -1311,41 +1312,41 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
1311 1311
                             err = 1;
1312 1312
                     }
1313 1313
                 }
1314
-                
1314
+
1315 1315
                 arg = NULL;
1316 1316
                 if(err) break;
1317 1317
                 if(errno == ERANGE) {
1318 1318
                     fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to 4G\n", name);
1319 1319
                     lnumarg = UINT_MAX;
1320 1320
                 }
1321
-                
1321
+
1322 1322
                 numarg = lnumarg ? lnumarg : UINT_MAX;
1323 1323
                 break;
1324
-                
1324
+
1325 1325
             case CLOPT_TYPE_BOOL:
1326 1326
                 if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true"))
1327 1327
                     numarg = 1;
1328 1328
                 else
1329 1329
                     numarg = 0;
1330
-                
1330
+
1331 1331
                 arg = NULL;
1332 1332
                 break;
1333 1333
         }
1334
-        
1334
+
1335 1335
         if(err)
1336 1336
             break;
1337
-        
1337
+
1338 1338
         if(optaddarg(opts, name, arg, numarg) < 0) {
1339 1339
             fprintf(stderr, "ERROR: Can't register argument for option --%s\n", optentry->longopt);
1340 1340
             err = 1;
1341 1341
         }
1342 1342
         break;
1343 1343
     }
1344
-    
1344
+
1345 1345
     if(err) {
1346 1346
         optfree(opts);
1347 1347
         return NULL;
1348 1348
     }
1349
-      
1349
+
1350 1350
     return opts;
1351 1351
 }
... ...
@@ -76,8 +76,8 @@ Example
76 76
 # Default: no
77 77
 #OfficialDatabaseOnly no
78 78
 
79
-# The daemon on Windows only supports unsecured TCP sockets. 
80
-# Due to security reasons make sure that your IP & port is not 
79
+# The daemon on Windows only supports unsecured TCP sockets.
80
+# Due to security reasons make sure that your IP & port is not
81 81
 # exposed to the open internet.
82 82
 
83 83
 # TCP port address.
... ...
@@ -203,7 +203,7 @@ TCPAddr 127.0.0.1
203 203
 #DetectPUA yes
204 204
 
205 205
 # Exclude a specific PUA category. This directive can be used multiple times.
206
-# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for 
206
+# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for
207 207
 # the complete list of PUA categories.
208 208
 # Default: Load all categories (if DetectPUA is activated)
209 209
 #ExcludePUA NetTool
... ...
@@ -243,9 +243,9 @@ TCPAddr 127.0.0.1
243 243
 # the end of a scan. If an archive contains both a heuristically detected
244 244
 # virus/phish, and a real malware, the real malware will be reported
245 245
 #
246
-# Keep this disabled if you intend to handle "*.Heuristics.*" viruses 
246
+# Keep this disabled if you intend to handle "*.Heuristics.*" viruses
247 247
 # differently from "real" malware.
248
-# If a non-heuristically-detected virus (signature-based) is found first, 
248
+# If a non-heuristically-detected virus (signature-based) is found first,
249 249
 # the scan is interrupted immediately, regardless of this config option.
250 250
 #
251 251
 # Default: no
... ...
@@ -446,6 +446,16 @@ TCPAddr 127.0.0.1
446 446
 # The options below protect your system against Denial of Service attacks
447 447
 # using archive bombs.
448 448
 
449
+# This option sets the maximum amount of time to a scan may take.
450
+# In this version, this field only affects the scan time of ZIP archives.
451
+# Value of 0 disables the limit
452
+# Note: disabling this limit or setting it too high may result allow scanning
453
+# of certain files to lock up the scanning process/threads resulting in a Denial
454
+# of Service.
455
+# Time is in milliseconds.
456
+# Default: 120000
457
+#MaxScanTime 300000
458
+
449 459
 # This option sets the maximum amount of data to be scanned for each input file.
450 460
 # Archives and other containers are recursively extracted and scanned up to this
451 461
 # value.
... ...
@@ -584,7 +594,7 @@ TCPAddr 127.0.0.1
584 584
 ## Bytecode
585 585
 ##
586 586
 
587
-# With this option enabled ClamAV will load bytecode from the database. 
587
+# With this option enabled ClamAV will load bytecode from the database.
588 588
 # It is highly recommended you keep this option on, otherwise you'll miss
589 589
 # detections for many new viruses.
590 590
 # Default: yes
... ...
@@ -608,7 +618,7 @@ TCPAddr 127.0.0.1
608 608
 #BytecodeSecurity TrustSigned
609 609
 
610 610
 # Set bytecode timeout in milliseconds.
611
-# 
611
+#
612 612
 # Default: 5000
613 613
 # BytecodeTimeout 1000
614 614