From eac19e007b7e4628620093d9a78e2f476d766722 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:09:45 -0400 Subject: [PATCH 1/9] Fix cl_fmap_set_hash hash buffer type cl_fmap_set_hash declared the hash input as a single char, but the implementation passes that value to fmap_set_hash() as if it were a full hash buffer. That causes the API contract to be wrong and can lead to invalid memory reads. Change the public API and implementation to accept a hash buffer pointer, validate that the pointer is not NULL, and update NEWS.md to match the corrected signature. --- NEWS.md | 2 +- libclamav/clamav.h | 4 ++-- libclamav/fmap.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index cb9f3e8099..7838eb06d2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -366,7 +366,7 @@ ClamAV 1.5.0 includes the following improvements and changes: cl_error_t cl_fmap_get_path(cl_fmap_t *map, const char **path_out, size_t *offset_out, size_t *len_out); cl_error_t cl_fmap_get_fd(const cl_fmap_t *map, int *fd_out, size_t *offset_out, size_t *len_out); cl_error_t cl_fmap_get_size(const cl_fmap_t *map, size_t *size_out); - cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, char hash); + cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, const char *hash); cl_error_t cl_fmap_have_hash(const cl_fmap_t *map, const char *hash_alg, bool *have_hash_out); cl_error_t cl_fmap_will_need_hash_later(const cl_fmap_t *map, const char *hash_alg); cl_error_t cl_fmap_get_hash(const cl_fmap_t *map, const char *hash_alg, char **hash_out); diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 815438377a..80e6b6ee64 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -685,10 +685,10 @@ extern cl_error_t cl_fmap_get_size(const cl_fmap_t *map, size_t *size_out); * * @param map The file map to modify. * @param hash_alg The hash algorithm to use (e.g., "md5", "sha1", "sha2-256"). - * @param hash The hash value to set. + * @param hash Pointer to the hash value to set. * @return cl_error_t CL_SUCCESS if the hash was successfully set. */ -extern cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, char hash); +extern cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, const char *hash); /** * @brief Check if we already calculated a file hash of a specific type. diff --git a/libclamav/fmap.c b/libclamav/fmap.c index 350855c640..0bf3b1e560 100644 --- a/libclamav/fmap.c +++ b/libclamav/fmap.c @@ -1455,12 +1455,12 @@ extern cl_error_t cl_fmap_get_size(const cl_fmap_t *map, size_t *size_out) return status; } -extern cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, char hash) +extern cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, const char *hash) { cl_error_t status = CL_ERROR; cli_hash_type_t type; - if (!map || !hash_alg) { + if (!map || !hash_alg || !hash) { status = CL_ENULLARG; goto done; } @@ -1477,7 +1477,7 @@ extern cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, c goto done; } - status = fmap_set_hash((fmap_t *)map, (uint8_t *)&hash, type); + status = fmap_set_hash((fmap_t *)map, (uint8_t *)hash, type); if (status != CL_SUCCESS) { cli_errmsg("cl_fmap_set_hash: Failed to set hash for algorithm: %s\n", hash_alg); goto done; From cf224709fd7cc0614e9bc69df504cc7b0395b66a Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:15:37 -0400 Subject: [PATCH 2/9] Fix cl_hash_file_fd_ex zero-length handling Fix cl_hash_file_fd_ex so a length of zero hashes the rest of the file as documented. The old loop reduced the read size to zero immediately and returned the digest of an empty input. Keep the existing bounded-read path for explicit lengths, but leave the block size unchanged when length is zero so the function reads until EOF. --- libclamav/crypto.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libclamav/crypto.c b/libclamav/crypto.c index 8b398e402f..892a139c28 100644 --- a/libclamav/crypto.c +++ b/libclamav/crypto.c @@ -709,12 +709,16 @@ extern cl_error_t cl_hash_file_fd_ex( } do { - blocksize = MIN(blocksize, length - byte_read); + size_t read_size = blocksize; + + if (length != 0) { + read_size = MIN(read_size, length - byte_read); + } #ifdef _WIN32 - nread = _read(fd, block, blocksize); + nread = _read(fd, block, read_size); #else - nread = read(fd, block, blocksize); + nread = read(fd, block, read_size); #endif if (nread < 0) { cli_errmsg("cl_hash_data_ex: Failed to read from file descriptor %d: %s\n", fd, cl_strerror(CL_EREAD)); From 5d5a836605731ff9f6d659158c65eb412982b9ab Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:20:17 -0400 Subject: [PATCH 3/9] Docs: fix cl_cvdverify_ex signature verification note Fix the cl_cvdverify_ex documentation to match the current implementation. The header said CL_DB_UNSIGNED disables CVD signature verification, but .cvd files are still verified. Clarify that .cvd verification occurs for .cvd files and drop the unsupported CL_DB_UNSIGNED note from this API comment. --- libclamav/clamav.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 80e6b6ee64..38868f7c01 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -1959,13 +1959,12 @@ extern cl_error_t cl_cvdverify(const char *file); /** * @brief Verify a CVD file by loading and unloading it. * - * May also verify the CVD digital signature. + * For `.cvd` files, this also verifies the CVD digital signature. * * @param file Filepath of CVD file. * @param certs_directory Directory containing CA certificates required to verify the CVD digital signature. * @param dboptions Bitmask of flags to modify behavior. * Set CL_DB_FIPS_LIMITS to require the CVD to be signed with a FIPS-compliant external '.sign' file. - * Set CL_DB_UNSIGNED to disable verification of CVD digital signatures. Allow load testing unsigned CVD files. * @return cl_error_t CL_SUCCESS if success, else a CL_E* error code. */ extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory, uint32_t dboptions); From fbcd43f6cab69705028cec08441d950fb1684a29 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:27:00 -0400 Subject: [PATCH 4/9] Fix legacy cl_scanfile scanned count conversion Fix the legacy cl_scanfile wrappers to preserve their original scanned count contract. They should report scanned / CL_COUNT_PRECISION, but after cl_scanfile_ex was added they started returning exact byte counts. Apply the same conversion used by cl_scanmap_callback() in cl_scanfile() and cl_scanfile_callback(), including the 32-bit saturation check on the scaled value. --- libclamav/scanners.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libclamav/scanners.c b/libclamav/scanners.c index b282662169..d9247468aa 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -6488,11 +6488,12 @@ cl_error_t cl_scanfile( NULL); if (NULL != scanned) { - if (SIZEOF_LONG == 4 && scanned_bytes > UINT32_MAX) { + if ((SIZEOF_LONG == 4) && + (scanned_bytes / CL_COUNT_PRECISION > UINT32_MAX)) { cli_warnmsg("cl_scanfile_callback: scanned_bytes exceeds UINT32_MAX, setting to UINT32_MAX\n"); *scanned = UINT32_MAX; } else { - *scanned = (unsigned long int)scanned_bytes; + *scanned = (unsigned long int)(scanned_bytes / CL_COUNT_PRECISION); } } @@ -6532,11 +6533,12 @@ cl_error_t cl_scanfile_callback( NULL); if (NULL != scanned) { - if (SIZEOF_LONG == 4 && scanned_out > UINT32_MAX) { + if ((SIZEOF_LONG == 4) && + (scanned_out / CL_COUNT_PRECISION > UINT32_MAX)) { cli_warnmsg("cl_scanfile_callback: scanned_out exceeds UINT32_MAX, setting to UINT32_MAX\n"); *scanned = UINT32_MAX; } else { - *scanned = (unsigned long int)scanned_out; + *scanned = (unsigned long int)(scanned_out / CL_COUNT_PRECISION); } } From 6784a0866960c66fff547729d9671df6b0f0ccb4 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:28:09 -0400 Subject: [PATCH 5/9] Docs: fix cl_fmap_get_name return documentation Fix the cl_fmap_get_name header comment to match the public API. The comment claimed the function returns a const char *, but the prototype and implementation return cl_error_t and write the name through an output parameter. Update the return documentation to describe the actual status code contract. --- libclamav/clamav.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 38868f7c01..6cdff8a79f 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -591,7 +591,7 @@ extern cl_error_t cl_fmap_set_name(cl_fmap_t *map, const char *name); * * @param map The file map to query. * @param[out] name_out Pointer to a variable to receive the name of the file map. - * @return const char* The name of the file map, or NULL if not set. + * @return cl_error_t CL_SUCCESS if the name was retrieved successfully. */ extern cl_error_t cl_fmap_get_name(cl_fmap_t *map, const char **name_out); From 18bb093b54e5bc33ba09044ec1b46600454ca8c2 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:29:53 -0400 Subject: [PATCH 6/9] Docs: fix scan API cross-references Fix incorrect cross-references in the scan API header comments. The cl_scandesc_ex() and cl_scanfile_ex() documentation described them as extensions of cl_scanmap_callback(), which is the wrong legacy API. Update each comment to refer to the matching legacy wrapper so the public API relationships are documented correctly. --- libclamav/clamav.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 6cdff8a79f..81c5fa048e 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -1636,7 +1636,7 @@ extern cl_error_t cl_scandesc_callback( * This callback variant allows the caller to provide a context structure that * caller provided callback functions can interpret. * - * This extended version of cl_scanmap_callback allows the caller to provide + * This extended version of cl_scandesc_callback allows the caller to provide * additional hints to the scanning engine, such as a file hash and file type. * * This variant also upgrades the `scanned` output parameter to a 64-bit integer. @@ -1737,7 +1737,7 @@ extern cl_error_t cl_scanfile_callback( * This callback variant allows the caller to provide a context structure that * caller provided callback functions can interpret. * - * This extended version of cl_scanmap_callback allows the caller to provide + * This extended version of cl_scanfile_callback allows the caller to provide * additional hints to the scanning engine, such as a file hash and file type. * * This variant also upgrades the `scanned` output parameter to a 64-bit integer. From d2f7c28c91bfef1f36a59e0af6760d305ea77644 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:30:50 -0400 Subject: [PATCH 7/9] Docs: fix fmap hash API references Fix stale hash API references in the scanmap header comments. The comments pointed users to cl_fmap_get_file_hash(), but that public function does not exist. Update both references to the actual public API, cl_fmap_get_hash(), so the post-scan guidance matches the header. --- libclamav/clamav.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 81c5fa048e..7a23d164cf 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -1797,7 +1797,7 @@ extern cl_error_t cl_scanfile_ex( * - `cl_fmap_open_handle()` for a file handle, or * - `cl_fmap_open_memory()` for a memory buffer. * - * After the scan, you can also get the file hash with `cl_fmap_get_file_hash()`. + * After the scan, you can also get the file hash with `cl_fmap_get_hash()`. * * @param map Buffer to be scanned, in form of a cl_fmap_t. * @param filename Name of data origin. Does not need to be an actual @@ -1831,7 +1831,7 @@ extern cl_error_t cl_scanmap_callback( * - `cl_fmap_open_handle()` for a file handle, or * - `cl_fmap_open_memory()` for a memory buffer. * - * After the scan, you can also get the file hash with `cl_fmap_get_file_hash()`. + * After the scan, you can also get the file hash with `cl_fmap_get_hash()`. * * This extended version of cl_scanmap_callback allows the caller to provide * additional hints to the scanning engine, such as a file hash and file type. From 0d615342731c7fb222af95eddf6a2d82e860ff79 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:34:16 -0400 Subject: [PATCH 8/9] Docs: fix cl_scandesc_ex parameter description Fix the cl_scandesc_ex parameter documentation to match the actual API. The comment said callers must provide a file descriptor or a map, but this function only accepts a file descriptor. Remove the stale map reference so the parameter description matches the public prototype. --- libclamav/clamav.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 7a23d164cf..4da22df2fb 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -1641,7 +1641,7 @@ extern cl_error_t cl_scandesc_callback( * * This variant also upgrades the `scanned` output parameter to a 64-bit integer. * - * @param desc File descriptor of an open file. The caller must provide this or the map. + * @param desc File descriptor of an open file. * @param filename (Optional) Filepath of the open file descriptor or file map. * @param[out] verdict_out A pointer to a cl_verdict_t that will be set to the scan verdict. * You should check the verdict even if the function returns an error. From 3abd289d185ce91018658daf81dfe0d6665678cd Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Tue, 31 Mar 2026 17:34:36 -0400 Subject: [PATCH 9/9] Docs: fix cl_fmap_get_path error description Fix the cl_fmap_get_path return documentation to match the implementation. The comment said CL_EACCES means the map has no file descriptor, but this getter actually returns CL_EACCES when the map has no stored path. Update the error description so callers see the correct failure condition. --- libclamav/clamav.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 4da22df2fb..bcd79e1aa9 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -635,7 +635,7 @@ extern cl_error_t cl_fmap_set_path(cl_fmap_t *map, const char *path); * @param[out] offset_out (optional) Pointer to a variable to receive the offset of the current layer within the given file. * @param[out] len_out (optional) Pointer to a variable to receive the length of the current layer within the given file. * @return cl_error_t CL_SUCCESS if the path was successfully retrieved. - * CL_EACCES if the map does not have a file descriptor. + * CL_EACCES if the map does not have a stored path. * CL_ENULLARG if null arguments were provided. */ extern cl_error_t cl_fmap_get_path(cl_fmap_t *map, const char **path_out, size_t *offset_out, size_t *len_out);