Remove max-allocation limits where not required

The cli_max_malloc, cli_max_calloc, and cli_max_realloc functions
provide a way to protect against allocating too much memory
when the size of the allocation is derived from the untrusted input.
Specifically, we worry about values in the file being scanned being
manipulated to exhaust the RAM and crash the application.

There is no need to check the limits if the size of the allocation
is fixed, or if the size of the allocation is necessary for signature
loading, or the general operation of the applications.
E.g. checking the max-allocation limit for the size of a hash, or
for the size of the scan recursion stack, is a complete waste of
time.

Although we significantly increased the max-allocation limit in
a recent release, it is best not to check an allocation if the
allocation will be safe. It would be a waste of time.

I am also hopeful that if we can reduce the number allocations
that require a limit-check to those that require it for the safe
scan of a file, then eventually we can store the limit in the scan-
context, and make it configurable.
pull/859/head^2
Micah Snyder 1 year ago committed by Micah Snyder
parent 73c6d4619a
commit 902623972d
  1. 2
      clambc/bcrun.c
  2. 4
      clamonacc/inotif/hash.c
  3. 10
      clamonacc/inotif/inotif.c
  4. 2
      clamonacc/misc/utils.c
  5. 6
      freshclam/freshclam.c
  6. 4
      libclamav/cvd.c
  7. 2
      libclamav/dsig.c
  8. 25
      libclamav/others.c
  9. 12
      libclamav/others_common.c
  10. 12
      libclamav/pe.c
  11. 2
      libfreshclam/libfreshclam.c
  12. 4
      sigtool/sigtool.c
  13. 8
      unit_tests/check_bytecode.c
  14. 12
      unit_tests/check_clamav.c
  15. 8
      unit_tests/check_matchers.c

@ -399,7 +399,7 @@ int main(int argc, char *argv[])
cctx.evidence = evidence_new();
cctx.recursion_stack_size = cctx.engine->max_recursion_level;
cctx.recursion_stack = cli_max_calloc(sizeof(recursion_level_t), cctx.recursion_stack_size);
cctx.recursion_stack = calloc(sizeof(recursion_level_t), cctx.recursion_stack_size);
if (!cctx.recursion_stack) {
fprintf(stderr, "Out of memory\n");
exit(3);

@ -145,7 +145,7 @@ int onas_ht_init(struct onas_ht **ht, uint32_t size)
.nbckts = 0,
};
if (!((*ht)->htable = (struct onas_bucket **)cli_max_calloc(size, sizeof(struct onas_bucket *)))) {
if (!((*ht)->htable = (struct onas_bucket **)calloc(size, sizeof(struct onas_bucket *)))) {
onas_free_ht(*ht);
return CL_EMEM;
}
@ -794,7 +794,7 @@ int onas_ht_rm_hierarchy(struct onas_ht *ht, const char *pathname, size_t len, i
curr = curr->next;
size_t size = len + strlen(curr->dirname) + 2;
char *child_path = (char *)cli_max_malloc(size);
char *child_path = (char *)malloc(size);
if (child_path == NULL)
return CL_EMEM;
if (hnode->pathname[len - 1] == '/')

@ -105,7 +105,7 @@ static int onas_ddd_init_wdlt(uint64_t nwatches)
if (nwatches <= 0) return CL_EARG;
wdlt = (char **)cli_max_calloc(nwatches << 1, sizeof(char *));
wdlt = (char **)calloc(nwatches << 1, sizeof(char *));
if (!wdlt) return CL_EMEM;
wdlt_len = nwatches << 1;
@ -121,7 +121,7 @@ static int onas_ddd_grow_wdlt(void)
char **ptr = NULL;
ptr = (char **)cli_max_realloc(wdlt, wdlt_len << 1);
ptr = (char **)cli_safer_realloc(wdlt, wdlt_len << 1);
if (ptr) {
wdlt = ptr;
memset(&ptr[wdlt_len], 0, sizeof(char *) * (wdlt_len - 1));
@ -243,7 +243,7 @@ static int onas_ddd_watch_hierarchy(const char *pathname, size_t len, int fd, ui
curr = curr->next;
size_t size = len + strlen(curr->dirname) + 2;
char *child_path = (char *)cli_max_malloc(size);
char *child_path = (char *)malloc(size);
if (child_path == NULL) {
logg(LOGG_ERROR, "ClamInotif: out of memory when adding child for %s\n", hnode->pathname);
return CL_EMEM;
@ -330,7 +330,7 @@ static int onas_ddd_unwatch_hierarchy(const char *pathname, size_t len, int fd,
curr = curr->next;
size_t size = len + strlen(curr->dirname) + 2;
char *child_path = (char *)cli_max_malloc(size);
char *child_path = (char *)malloc(size);
if (child_path == NULL)
return CL_EMEM;
if (hnode->pathname[len - 1] == '/')
@ -679,7 +679,7 @@ void *onas_ddd_th(void *arg)
} else {
len = strlen(path);
size_t size = strlen(child) + len + 2;
char *child_path = (char *)cli_max_malloc(size);
char *child_path = (char *)malloc(size);
if (child_path == NULL) {
logg(LOGG_DEBUG, "ClamInotif: could not allocate space for child path ... aborting\n");
return NULL;

@ -206,7 +206,7 @@ char **onas_get_opt_list(const char *fname, int *num_entries, cl_error_t *err)
}
(*num_entries)++;
rlc_ptr = cli_max_realloc(opt_list, sizeof(char *) * (*num_entries + 1));
rlc_ptr = cli_safer_realloc(opt_list, sizeof(char *) * (*num_entries + 1));
if (rlc_ptr) {
opt_list = rlc_ptr;
opt_list[*num_entries] = NULL;

@ -535,7 +535,7 @@ static fc_error_t string_list_add(const char *item, char ***stringList, uint32_t
}
nItems = *nListItems + 1;
newList = (char **)cli_max_realloc(*stringList, nItems * sizeof(char *));
newList = (char **)cli_safer_realloc(*stringList, nItems * sizeof(char *));
if (newList == NULL) {
mprintf(LOGG_ERROR, "string_list_add: Failed to allocate memory for optional database list entry.\n");
status = FC_EMEM;
@ -1142,7 +1142,7 @@ fc_error_t select_from_official_databases(
goto done;
}
selectedDatabases = cli_max_calloc(nStandardDatabases + nOptionalDatabases, sizeof(char *));
selectedDatabases = calloc(nStandardDatabases + nOptionalDatabases, sizeof(char *));
/*
* Select desired standard databases.
@ -1261,7 +1261,7 @@ fc_error_t select_specific_databases(
*databaseList = NULL;
*nDatabases = 0;
selectedDatabases = cli_max_calloc(nSpecificDatabases, sizeof(char *));
selectedDatabases = calloc(nSpecificDatabases, sizeof(char *));
/*
* Get lists of available databases.

@ -88,7 +88,7 @@ static int cli_untgz(int fd, const char *destdir)
return -1;
}
path = (char *)cli_max_calloc(sizeof(char), pathlen);
path = (char *)calloc(sizeof(char), pathlen);
if (!path) {
cli_errmsg("cli_untgz: Can't allocate memory for path\n");
cli_untgz_cleanup(NULL, infile, NULL, fdd);
@ -259,7 +259,7 @@ static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, un
}
dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE;
dbio->buf = cli_max_malloc(dbio->bufsize);
dbio->buf = malloc(dbio->bufsize);
if (!dbio->buf) {
cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n");
cli_tgzload_cleanup(compr, dbio, fdd);

@ -139,7 +139,7 @@ static unsigned char *cli_decodesig(const char *sig, unsigned int plen, BIGNUM *
bn_bytes, plen);
goto done;
}
plain = cli_max_calloc(plen, sizeof(unsigned char));
plain = calloc(plen, sizeof(unsigned char));
if (!plain) {
cli_errmsg("cli_decodesig: Can't allocate memory for 'plain'\n");
goto done;

@ -269,7 +269,7 @@ static void *get_module_function(HMODULE handle, const char *name)
static void *get_module_function(void *handle, const char *name)
{
void *procAddress = NULL;
procAddress = dlsym(handle, name);
procAddress = dlsym(handle, name);
if (NULL == procAddress) {
const char *err = dlerror();
if (NULL == err) {
@ -1665,7 +1665,7 @@ size_t cli_recursion_stack_get_size(cli_ctx *ctx, int index)
/*
* Windows doesn't allow you to delete a directory while it is still open
*/
int cli_rmdirs(const char *name)
int cli_rmdirs(const char *dirname)
{
int rc;
STATBUF statb;
@ -1673,17 +1673,17 @@ int cli_rmdirs(const char *name)
struct dirent *dent;
char err[128];
if (CLAMSTAT(name, &statb) < 0) {
cli_warnmsg("cli_rmdirs: Can't locate %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
if (CLAMSTAT(dirname, &statb) < 0) {
cli_warnmsg("cli_rmdirs: Can't locate %s: %s\n", dirname, cli_strerror(errno, err, sizeof(err)));
return -1;
}
if (!S_ISDIR(statb.st_mode)) {
if (cli_unlink(name)) return -1;
if (cli_unlink(dirname)) return -1;
return 0;
}
if ((dd = opendir(name)) == NULL)
if ((dd = opendir(dirname)) == NULL)
return -1;
rc = 0;
@ -1696,15 +1696,14 @@ int cli_rmdirs(const char *name)
if (strcmp(dent->d_name, "..") == 0)
continue;
path = cli_max_malloc(strlen(name) + strlen(dent->d_name) + 2);
path = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
if (path == NULL) {
cli_errmsg("cli_rmdirs: Unable to allocate memory for path %u\n", strlen(name) + strlen(dent->d_name) + 2);
cli_errmsg("cli_rmdirs: Unable to allocate memory for path %u\n", strlen(dirname) + strlen(dent->d_name) + 2);
closedir(dd);
return -1;
}
sprintf(path, "%s\\%s", name, dent->d_name);
sprintf(path, "%s\\%s", dirname, dent->d_name);
rc = cli_rmdirs(path);
free(path);
if (rc != 0)
@ -1713,8 +1712,8 @@ int cli_rmdirs(const char *name)
closedir(dd);
if (rmdir(name) < 0) {
cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", name, cli_strerror(errno, err, sizeof(err)));
if (rmdir(dirname) < 0) {
cli_errmsg("cli_rmdirs: Can't remove temporary directory %s: %s\n", dirname, cli_strerror(errno, err, sizeof(err)));
return -1;
}
@ -1742,7 +1741,7 @@ int cli_rmdirs(const char *dirname)
while ((dent = readdir(dd))) {
if (dent->d_ino) {
if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
path = cli_max_malloc(strlen(dirname) + strlen(dent->d_name) + 2);
path = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
if (!path) {
cli_errmsg("cli_rmdirs: Unable to allocate memory for path %llu\n", (long long unsigned)(strlen(dirname) + strlen(dent->d_name) + 2));
closedir(dd);

@ -221,8 +221,7 @@ void *cli_max_malloc(size_t size)
void *alloc;
if (0 == size || size > CLI_MAX_ALLOCATION) {
cli_warnmsg("cli_max_malloc(): File or section is too large to scan (%zu bytes). \
For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
cli_warnmsg("cli_max_malloc(): File or section is too large to scan (%zu bytes). For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
size, CLI_MAX_ALLOCATION);
return NULL;
}
@ -243,8 +242,7 @@ void *cli_max_calloc(size_t nmemb, size_t size)
void *alloc;
if (!nmemb || 0 == size || size > CLI_MAX_ALLOCATION || nmemb > CLI_MAX_ALLOCATION || (nmemb * size > CLI_MAX_ALLOCATION)) {
cli_warnmsg("cli_max_calloc(): File or section is too large to scan (%zu bytes). \
For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
cli_warnmsg("cli_max_calloc(): File or section is too large to scan (%zu bytes). For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
size, CLI_MAX_ALLOCATION);
return NULL;
}
@ -311,8 +309,7 @@ void *cli_max_realloc(void *ptr, size_t size)
void *alloc;
if (0 == size || size > CLI_MAX_ALLOCATION) {
cli_warnmsg("cli_max_realloc(): File or section is too large to scan (%zu bytes). \
For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
cli_warnmsg("cli_max_realloc(): File or section is too large to scan (%zu bytes). For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
size, CLI_MAX_ALLOCATION);
return NULL;
}
@ -333,8 +330,7 @@ void *cli_max_realloc2(void *ptr, size_t size)
void *alloc;
if (0 == size || size > CLI_MAX_ALLOCATION) {
cli_warnmsg("cli_max_realloc2(): File or section is too large to scan (%zu bytes). \
For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
cli_warnmsg("cli_max_realloc2(): File or section is too large to scan (%zu bytes). For your safety, ClamAV limits how much memory an operation can allocate to %d bytes\n",
size, CLI_MAX_ALLOCATION);
return NULL;
}

@ -2297,7 +2297,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
break; \
} \
\
fname = cli_max_calloc(funclen + dlllen + 3, sizeof(char)); \
fname = cli_max_calloc(funclen + dlllen + 3, sizeof(char)); \
if (fname == NULL) { \
cli_dbgmsg("scan_pe: cannot allocate memory for imphash string\n"); \
ret = CL_EMEM; \
@ -2335,7 +2335,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
thuoff += sizeof(struct pe_image_thunk32);
temp = EC32(thunk32.u.Ordinal);
temp = EC32(thunk32.u.Ordinal);
thunk32.u.Ordinal = temp;
if (!(thunk32.u.Ordinal & PE_IMAGEDIR_ORDINAL_FLAG32)) {
@ -5849,22 +5849,22 @@ cl_error_t cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_sect
case 1:
genhash[CLI_HASH_MD5] = 1;
hlen = hashlen[CLI_HASH_MD5];
hash = hashset[CLI_HASH_MD5] = cli_max_calloc(hlen, sizeof(char));
hash = hashset[CLI_HASH_MD5] = calloc(hlen, sizeof(char));
break;
case 2:
genhash[CLI_HASH_SHA1] = 1;
hlen = hashlen[CLI_HASH_SHA1];
hash = hashset[CLI_HASH_SHA1] = cli_max_calloc(hlen, sizeof(char));
hash = hashset[CLI_HASH_SHA1] = calloc(hlen, sizeof(char));
break;
default:
genhash[CLI_HASH_SHA256] = 1;
hlen = hashlen[CLI_HASH_SHA256];
hash = hashset[CLI_HASH_SHA256] = cli_max_calloc(hlen, sizeof(char));
hash = hashset[CLI_HASH_SHA256] = calloc(hlen, sizeof(char));
break;
}
if (!hash) {
cli_errmsg("cli_genhash_pe: cli_max_calloc failed!\n");
cli_errmsg("cli_genhash_pe: calloc failed!\n");
cli_exe_info_destroy(peinfo);
return CL_EMEM;
}

@ -227,7 +227,7 @@ fc_error_t fc_initialize(fc_config *fcConfig)
#else
if (fcConfig->databaseDirectory[strlen(fcConfig->databaseDirectory) - 1] != '/') {
#endif
g_databaseDirectory = cli_max_malloc(strlen(fcConfig->databaseDirectory) + strlen(PATHSEP) + 1);
g_databaseDirectory = malloc(strlen(fcConfig->databaseDirectory) + strlen(PATHSEP) + 1);
snprintf(
g_databaseDirectory,
strlen(fcConfig->databaseDirectory) + strlen(PATHSEP) + 1,

@ -2762,7 +2762,7 @@ static int decodehex(const char *hexsig)
trigger[tlen] = '\0';
/* get the regex expression */
regex = cli_max_calloc(rlen + 1, sizeof(char));
regex = calloc(rlen + 1, sizeof(char));
if (!regex) {
mprintf(LOGG_ERROR, "cannot allocate memory for regex expression\n");
free(trigger);
@ -2773,7 +2773,7 @@ static int decodehex(const char *hexsig)
/* get the compile flags */
if (clen) {
cflags = cli_max_calloc(clen + 1, sizeof(char));
cflags = calloc(clen + 1, sizeof(char));
if (!cflags) {
mprintf(LOGG_ERROR, "cannot allocate memory for compile flags\n");
free(trigger);

@ -83,8 +83,8 @@ static void runtest(const char *file, uint64_t expected, int fail, int nojit,
cctx.dconf = cctx.engine->dconf;
cctx.recursion_stack_size = cctx.engine->max_recursion_level;
cctx.recursion_stack = cli_max_calloc(sizeof(recursion_level_t), cctx.recursion_stack_size);
ck_assert_msg(!!cctx.recursion_stack, "cli_max_calloc() for recursion_stack failed");
cctx.recursion_stack = calloc(sizeof(recursion_level_t), cctx.recursion_stack_size);
ck_assert_msg(!!cctx.recursion_stack, "calloc() for recursion_stack failed");
// ctx was memset, so recursion_level starts at 0.
cctx.recursion_stack[cctx.recursion_level].fmap = NULL;
@ -509,8 +509,8 @@ static void runload(const char *dbname, struct cl_engine *engine, unsigned signo
/* when run from automake srcdir is set, but if run manually then not */
srcdir = SRCDIR;
}
str = cli_max_malloc(strlen(srcdir) + 1 + strlen(dbname) + 1);
ck_assert_msg(!!str, "cli_max_malloc");
str = malloc(strlen(srcdir) + 1 + strlen(dbname) + 1);
ck_assert_msg(!!str, "malloc");
sprintf(str, "%s" PATHSEP "%s", srcdir, dbname);
rc = cl_load(str, engine, &signo, CL_DB_STDOPT);

@ -581,8 +581,8 @@ static void init_testfiles(void)
if (strncmp(dirent->d_name, "clam", 4))
continue;
i++;
testfiles = cli_max_realloc(testfiles, i * sizeof(*testfiles));
ck_assert_msg(!!testfiles, "cli_max_realloc");
testfiles = cli_safer_realloc(testfiles, i * sizeof(*testfiles));
ck_assert_msg(!!testfiles, "cli_safer_realloc");
testfiles[i - 1] = strdup(dirent->d_name);
}
testfiles_n = i;
@ -1921,8 +1921,8 @@ int open_testfile(const char *name, int flags)
srcdir = SRCDIR;
}
str = cli_max_malloc(strlen(name) + strlen(srcdir) + 2);
ck_assert_msg(!!str, "cli_max_malloc");
str = malloc(strlen(name) + strlen(srcdir) + 2);
ck_assert_msg(!!str, "malloc");
sprintf(str, "%s" PATHSEP "%s", srcdir, name);
fd = open(str, flags);
@ -1935,7 +1935,7 @@ void diff_file_mem(int fd, const char *ref, size_t len)
{
char c1, c2;
size_t p, reflen = len;
char *buf = cli_max_malloc(len);
char *buf = malloc(len);
ck_assert_msg(!!buf, "unable to malloc buffer: %zu", len);
p = read(fd, buf, len);
@ -1964,7 +1964,7 @@ void diff_files(int fd, int ref_fd)
off_t siz = lseek(ref_fd, 0, SEEK_END);
ck_assert_msg(siz != -1, "lseek failed");
ref = cli_max_malloc(siz);
ref = malloc(siz);
ck_assert_msg(!!ref, "unable to malloc buffer: " STDi64, (int64_t)siz);
ck_assert_msg(lseek(ref_fd, 0, SEEK_SET) == 0, "lseek failed");

@ -178,8 +178,8 @@ static void setup(void)
ctx.dconf = ctx.engine->dconf;
ctx.recursion_stack_size = ctx.engine->max_recursion_level;
ctx.recursion_stack = cli_max_calloc(sizeof(recursion_level_t), ctx.recursion_stack_size);
ck_assert_msg(!!ctx.recursion_stack, "cli_max_calloc() for recursion_stack failed");
ctx.recursion_stack = calloc(sizeof(recursion_level_t), ctx.recursion_stack_size);
ck_assert_msg(!!ctx.recursion_stack, "calloc() for recursion_stack failed");
// ctx was memset, so recursion_level starts at 0.
ctx.recursion_stack[ctx.recursion_level].fmap = &thefmap;
@ -478,7 +478,7 @@ START_TEST(test_pcre_scanbuff)
for (i = 0; pcre_testdata[i].data; i++) {
hexlen = strlen(PCRE_BYPASS) + strlen(pcre_testdata[i].hexsig) + 1;
hexsig = cli_max_calloc(hexlen, sizeof(char));
hexsig = calloc(hexlen, sizeof(char));
ck_assert_msg(hexsig != NULL, "[pcre] failed to prepend bypass (out-of-memory)");
strncat(hexsig, PCRE_BYPASS, hexlen);
@ -532,7 +532,7 @@ START_TEST(test_pcre_scanbuff_allscan)
for (i = 0; pcre_testdata[i].data; i++) {
hexlen = strlen(PCRE_BYPASS) + strlen(pcre_testdata[i].hexsig) + 1;
hexsig = cli_max_calloc(hexlen, sizeof(char));
hexsig = calloc(hexlen, sizeof(char));
ck_assert_msg(hexsig != NULL, "[pcre] failed to prepend bypass (out-of-memory)");
strncat(hexsig, PCRE_BYPASS, hexlen);

Loading…
Cancel
Save