Fix 32-bit integer truncation in block metadata allocations and file_block

This commit is contained in:
Andrea Mazzoleni 2026-08-26 17:06:28 +02:00
parent 527bd69129
commit c7b8068e36
5 changed files with 85 additions and 11 deletions

View file

@ -462,18 +462,30 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_
{
struct snapraid_file* file;
block_off_t i;
block_off_t blockmax;
blockmax = size / block_size;
if (size % block_size != 0)
++blockmax;
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / block_sizeof())) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", sub, blockmax);
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(sub);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->blockmax = blockmax;
file->mtime_sec = mtime_sec;
file->mtime_nsec = mtime_nsec;
file->inode = inode;
file->physical = physical;
file->flag = 0;
file->shared_flag = 0;
file->blockvec = nalloc_nofail(file->blockmax, block_sizeof());
file->blockvec = nalloc_nofail((size_t)file->blockmax, block_sizeof());
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = file_block(file, i);
@ -489,6 +501,13 @@ struct snapraid_file* file_dup(struct snapraid_file* copy)
struct snapraid_file* file;
block_off_t i;
#if SIZE_MAX == UINT32_MAX
if (copy->blockmax > (block_off_t)(SIZE_MAX / block_sizeof())) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", copy->sub, copy->blockmax);
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_file));
file->sub = strdup_nofail(copy->sub);
file->size = copy->size;
@ -499,7 +518,7 @@ struct snapraid_file* file_dup(struct snapraid_file* copy)
file->physical = copy->physical;
file->flag = copy->flag;
file->shared_flag = 0;
file->blockvec = nalloc_nofail(file->blockmax, block_sizeof());
file->blockvec = nalloc_nofail((size_t)file->blockmax, block_sizeof());
for (i = 0; i < file->blockmax; ++i) {
struct snapraid_block* block = file_block(file, i);
@ -847,14 +866,26 @@ int dir_name_compare(const void* void_arg, const void* void_data)
struct snapraid_dealloc* dealloc_alloc(unsigned block_size, const char* sub, data_off_t size, int64_t mtime_sec, int32_t mtime_nsec)
{
struct snapraid_dealloc* dealloc;
block_off_t blockmax;
blockmax = size / block_size;
if (size % block_size != 0)
++blockmax;
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / BLOCK_HASH_SIZE)) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", sub, blockmax);
exit(EXIT_FAILURE);
}
#endif
dealloc = malloc_nofail(sizeof(struct snapraid_dealloc));
dealloc->sub = strdup_nofail(sub);
dealloc->size = size;
dealloc->mtime_sec = mtime_sec;
dealloc->mtime_nsec = mtime_nsec;
dealloc->blockmax = (size + block_size - 1) / block_size;
dealloc->blockhash = nalloc_nofail(dealloc->blockmax, BLOCK_HASH_SIZE);
dealloc->blockmax = blockmax;
dealloc->blockhash = nalloc_nofail((size_t)dealloc->blockmax, BLOCK_HASH_SIZE);
return dealloc;
}
@ -1488,6 +1519,15 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap
struct snapraid_extent* parity_extent;
struct snapraid_extent* file_extent;
#if SIZE_MAX == UINT32_MAX
if (parity_pos > (block_off_t)(SIZE_MAX - 4096)) {
/* LCOV_EXCL_START */
log_fatal(ESOFT, "Parity position %" PRIu64 " is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", parity_pos);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
fs_lock(disk);
if (file_pos > 0) {

View file

@ -866,7 +866,7 @@ void file_copy(struct snapraid_file* src_file, struct snapraid_file* dest_file);
*
* Note that the block size if a runtime value.
*/
static inline struct snapraid_block* file_block(struct snapraid_file* file, size_t pos)
static inline struct snapraid_block* file_block(struct snapraid_file* file, block_off_t pos)
{
unsigned char* ptr = (unsigned char*)file->blockvec;

View file

@ -49,12 +49,24 @@ static void import_file(struct snapraid_state* state, const char* path, uint64_t
int flags;
unsigned block_size = state->block_size;
struct advise_struct advise;
block_off_t blockmax;
blockmax = size / block_size;
if (size % block_size != 0)
++blockmax;
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", path, blockmax);
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_import_file));
file->path = strdup_nofail(path);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->blockimp = nalloc_nofail(file->blockmax, sizeof(struct snapraid_import_block));
file->blockmax = blockmax;
file->blockimp = nalloc_nofail((size_t)file->blockmax, sizeof(struct snapraid_import_block));
file->is_runtime = 1;
buffer = malloc_nofail(block_size);
@ -148,6 +160,7 @@ static void import_dealloc(struct snapraid_state* state, const char* dir, struct
char path[PATH_MAX];
struct snapraid_import_file* file;
block_off_t i;
block_off_t blockmax;
data_off_t offset;
data_off_t size;
unsigned block_size = state->block_size;
@ -156,11 +169,22 @@ static void import_dealloc(struct snapraid_state* state, const char* dir, struct
pathcat(path, sizeof(path), dealloc->sub);
size = dealloc->size;
blockmax = size / block_size;
if (size % block_size != 0)
++blockmax;
#if SIZE_MAX == UINT32_MAX
if (blockmax > (block_off_t)(SIZE_MAX / sizeof(struct snapraid_import_block))) {
log_fatal(ESOFT, "File '%s' with %" PRIu64 " blocks is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", path, blockmax);
exit(EXIT_FAILURE);
}
#endif
file = malloc_nofail(sizeof(struct snapraid_import_file));
file->path = strdup_nofail(path);
file->size = size;
file->blockmax = (size + block_size - 1) / block_size;
file->blockimp = nalloc_nofail(file->blockmax, sizeof(struct snapraid_import_block));
file->blockmax = blockmax;
file->blockimp = nalloc_nofail((size_t)file->blockmax, sizeof(struct snapraid_import_block));
file->is_runtime = 0;
offset = 0;

View file

@ -2004,6 +2004,16 @@ static void state_content_check(struct snapraid_state* state, const char* path)
{
tommy_node* i;
#if SIZE_MAX == UINT32_MAX
block_off_t blockmax = parity_allocated_size(state);
if (blockmax > (block_off_t)(SIZE_MAX - 4096)) {
/* LCOV_EXCL_START */
log_fatal(ESOFT, "Array with %" PRIu64 " blocks in '%s' is too large for a 32-bit build. Use a 64-bit build or increase the block size.\n", blockmax, path);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
/* check that any map has different name and position */
for (i = state->maplist; i != 0; i = i->next) {
struct snapraid_map* map = i->data;

View file

@ -269,7 +269,7 @@ typedef unsigned char bit_vect_t;
static inline size_t bit_vect_size(size_t max)
{
return (max + BIT_VECT_SIZE - 1) / BIT_VECT_SIZE;
return (max / BIT_VECT_SIZE) + ((max % BIT_VECT_SIZE) != 0);
}
static inline void bit_vect_set(bit_vect_t* bit_vect, size_t off)