Raise artificial 4G limit for MaxScanSize

This limit is internally "long long", so >=64-bit even on 32-bit platforms.
Also fixes a related issue where limits could have been set to negative
values on 64-bit platforms where setting a "long long" (64-bit signed) can
overflow if assigned from an "unsigned long" (64-bit unsigned).

Resolves: https://github.com/Cisco-Talos/clamav/issues/809
This commit is contained in:
Matthias Fratz 2023-06-07 11:16:52 +02:00 committed by Micah Snyder
parent e4ac4b646a
commit 2962509609
2 changed files with 48 additions and 17 deletions

View file

@ -63,7 +63,7 @@
#define MAXCMDOPTS 150
#define MATCH_NUMBER "^[0-9]+$"
#define MATCH_SIZE "^[0-9]+[KM]?$"
#define MATCH_SIZE "^[0-9]+[KMG]?$"
#define MATCH_BOOL "^(yes|true|1|no|false|0)$"
#define FLAG_MULTIPLE 1 /* option can be used multiple times */
@ -440,7 +440,7 @@ const struct clam_option __clam_options[] = {
{"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.\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"},
{"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.", "400M"},
{"MaxScanSize", "max-scansize", 0, CLOPT_TYPE_SIZE64, 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.", "400M"},
{"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.", "100M"},
@ -646,7 +646,7 @@ const struct clam_option __clam_options[] = {
{"LeaveTemporaryFiles", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"LocalSocket", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"MailFollowURLs", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"MaxScanSize", NULL, 0, CLOPT_TYPE_SIZE, MATCH_SIZE, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"MaxScanSize", NULL, 0, CLOPT_TYPE_SIZE64, MATCH_SIZE, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"MaxFiles", NULL, 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"MaxRecursion", NULL, 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"PhishingSignatures", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
@ -923,7 +923,7 @@ struct optstruct *optparse(const char *cfgfile, int argc, char **argv, int verbo
struct option longopts[MAXCMDOPTS];
char shortopts[MAXCMDOPTS];
regex_t regex;
long long numarg, lnumarg;
long long numarg, lnumarg, lnumlimit;
int regflags = REG_EXTENDED | REG_NOSUB;
#ifdef _WIN32
@ -1204,25 +1204,40 @@ struct optstruct *optparse(const char *cfgfile, int argc, char **argv, int verbo
break;
case CLOPT_TYPE_SIZE:
case CLOPT_TYPE_SIZE64:
if (optentry->argtype == CLOPT_TYPE_SIZE64)
/* guaranteed to be "long long" (64 bit but possibly signed) further down the line */
lnumlimit = LLONG_MAX;
else
/* probably mostly "unsigned long int" (32 or 64 bit unsigned depending on platform),
* but may be expected to fit into a "long long" (64-bit signed) */
lnumlimit = LLONG_MAX < ULONG_MAX ? LLONG_MAX : ULONG_MAX;
errno = 0;
if (arg)
lnumarg = strtoul(arg, &buff, 0);
lnumarg = strtoll(arg, &buff, 0);
else {
numarg = 0;
break;
}
if (errno != ERANGE) {
switch (*buff) {
case 'G':
case 'g':
if (lnumarg <= lnumlimit / (1024 * 1024 * 1024))
lnumarg *= 1024 * 1024 * 1024;
else
errno = ERANGE;
break;
case 'M':
case 'm':
if (lnumarg <= UINT_MAX / (1024 * 1024))
if (lnumarg <= lnumlimit / (1024 * 1024))
lnumarg *= 1024 * 1024;
else
errno = ERANGE;
break;
case 'K':
case 'k':
if (lnumarg <= UINT_MAX / 1024)
if (lnumarg <= lnumlimit / 1024)
lnumarg *= 1024;
else
errno = ERANGE;
@ -1246,17 +1261,17 @@ struct optstruct *optparse(const char *cfgfile, int argc, char **argv, int verbo
if (err) break;
if (errno == ERANGE) {
if (cfgfile) {
fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to 4G\n", name);
fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to %lld\n", name, lnumlimit);
} else {
if (optentry->shortopt)
fprintf(stderr, "WARNING: Numerical value for option --%s (-%c) too high, resetting to 4G\n", optentry->longopt, optentry->shortopt);
fprintf(stderr, "WARNING: Numerical value for option --%s (-%c) too high, resetting to %lld\n", optentry->longopt, optentry->shortopt, lnumlimit);
else
fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to 4G\n", optentry->longopt);
fprintf(stderr, "WARNING: Numerical value for option %s too high, resetting to %lld\n", optentry->longopt, lnumlimit);
}
lnumarg = UINT_MAX;
lnumarg = lnumlimit;
}
numarg = lnumarg ? lnumarg : UINT_MAX;
numarg = lnumarg ? lnumarg : lnumlimit;
break;
case CLOPT_TYPE_BOOL:
@ -1319,7 +1334,7 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
struct optstruct *opts = NULL, *opts_last = NULL, *opt;
char *buff;
regex_t regex;
long long numarg, lnumarg;
long long numarg, lnumarg, lnumlimit;
int regflags = REG_EXTENDED | REG_NOSUB;
const struct clam_option *optentry = NULL;
@ -1417,25 +1432,40 @@ struct optstruct *optadditem(const char *name, const char *arg, int verbose, int
break;
case CLOPT_TYPE_SIZE:
case CLOPT_TYPE_SIZE64:
if (optentry->argtype == CLOPT_TYPE_SIZE64)
/* guaranteed to be "long long" (64 bit but possibly signed) further down the line */
lnumlimit = LLONG_MAX;
else
/* probably mostly "unsigned long int" (32 or 64 bit unsigned depending on platform),
* but may be expected to fit into a "long long" (64-bit signed) */
lnumlimit = LLONG_MAX < ULONG_MAX ? LLONG_MAX : ULONG_MAX;
errno = 0;
if (arg)
lnumarg = strtoul(arg, &buff, 0);
lnumarg = strtoll(arg, &buff, 0);
else {
numarg = 0;
break;
}
if (errno != ERANGE) {
switch (*buff) {
case 'G':
case 'g':
if (lnumarg <= lnumlimit / (1024 * 1024 * 1024))
lnumarg *= 1024 * 1024 * 1024;
else
errno = ERANGE;
break;
case 'M':
case 'm':
if (lnumarg <= UINT_MAX / (1024 * 1024))
if (lnumarg <= lnumlimit / (1024 * 1024))
lnumarg *= 1024 * 1024;
else
errno = ERANGE;
break;
case 'K':
case 'k':
if (lnumarg <= UINT_MAX / 1024)
if (lnumarg <= lnumlimit / 1024)
lnumarg *= 1024;
else
errno = ERANGE;

View file

@ -38,8 +38,9 @@
#define CLOPT_TYPE_STRING 1 /* quoted/regular string */
#define CLOPT_TYPE_NUMBER 2 /* raw number */
#define CLOPT_TYPE_SIZE 3 /* number possibly followed by modifiers (M/m or K/k) */
#define CLOPT_TYPE_SIZE 3 /* number possibly followed by modifiers (K/k, M/m or G/g) */
#define CLOPT_TYPE_BOOL 4 /* boolean */
#define CLOPT_TYPE_SIZE64 5 /* 64-bit number possibly followed by modifiers (K/k, M/m or G/g) */
#ifdef _WIN32
extern char _DATADIR[MAX_PATH];