From 85ca997bf2cdfee20bf39b087630f9be793da5a4 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 3 Jan 2024 20:02:42 +0100 Subject: [PATCH] Fix integer overflow when computing the progress percentage --- cmdline/state.c | 20 ++++++++++---------- cmdline/status.c | 18 +++++------------- cmdline/util.c | 19 +++++++++++++++++++ cmdline/util.h | 7 ++++++- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/cmdline/state.c b/cmdline/state.c index 66f17da8..0c5bf28a 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -4245,7 +4245,7 @@ void state_progress_end(struct snapraid_state* state, block_off_t countpos, bloc elapsed = now - state->progress_whole_start - state->progress_wasted; - msg_bar("%u%% completed, %u MB accessed", countpos * 100 / countmax, countsize_MB); + msg_bar("%u%% completed, %u MB accessed", muldiv(countpos, 100, countmax), countsize_MB); msg_bar(" in %u:%02u", (unsigned)(elapsed / 3600), (unsigned)((elapsed % 3600) / 60)); @@ -4405,7 +4405,7 @@ static void state_progress_graph(struct snapraid_state* state, struct snapraid_i struct snapraid_disk* disk = i->data; v = disk->progress_tick[current] - ref(disk->progress_tick, oldest); printr(disk->name, pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); @@ -4416,32 +4416,32 @@ static void state_progress_graph(struct snapraid_state* state, struct snapraid_i for (l = 0; l < state->level; ++l) { v = state->parity[l].progress_tick[current] - ref(state->parity[l].progress_tick, oldest); printr(lev_config_name(l), pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); } v = state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest); printr("raid", pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); v = state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest); printr("hash", pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); v = state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest); printr("sched", pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); v = state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest); printr("misc", pad); - printf("%3" PRIu64 "%% | ", v * 100 / tick_total); + printf("%3u%% | ", muldiv(v, 100, tick_total)); printc('*', v * bar / tick_total); printf("\n"); @@ -4499,7 +4499,7 @@ int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_o /* completion percentage */ if (countmax) - out_perc = countpos * 100 / countmax; + out_perc = muldiv(countpos, 100, countmax); /* if we have at least 5 measures */ if (state->progress_tick >= 5 @@ -4565,11 +4565,11 @@ int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_o /* estimate the cpu usage percentage */ if (delta_tick_total != 0) - out_cpu = (unsigned)(delta_tick_cpu * 100U / delta_tick_total); + out_cpu = muldiv(delta_tick_cpu, 100, delta_tick_total); /* estimate the remaining time in minutes */ if (delta_pos != 0) - out_eta = (countmax - countpos) * delta_time / (60 * delta_pos); + out_eta = muldiv(countmax - countpos, delta_time, 60 * delta_pos); if (state->opt.force_stats) { os_clear(); diff --git a/cmdline/status.c b/cmdline/status.c index 9f621de1..f35d3450 100644 --- a/cmdline/status.c +++ b/cmdline/status.c @@ -39,14 +39,6 @@ unsigned day_ago(time_t ref, time_t now) #define GRAPH_COLUMN 70 #define GRAPH_ROW 15 -static unsigned perc(uint64_t part, uint64_t total) -{ - if (!total) - return 0; - - return (unsigned)(part * 100 / total); -} - /** * Bit used to mark unscrubbed time info. */ @@ -239,7 +231,7 @@ int state_status(struct snapraid_state* state) printf(" - "); } else { printf("%8" PRIu64, (disk_block_max - disk_block_count) * (uint64_t)state->block_size / GIGA); - printf(" %3u%%", perc(disk_block_count, disk_block_max)); + printf(" %3u%%", muldiv(disk_block_count, 100, disk_block_max)); } printf(" %s\n", disk->name); @@ -266,7 +258,7 @@ int state_status(struct snapraid_state* state) printf("%8.1f", (double)all_wasted / GIGA); printf("%8" PRIu64, file_size / GIGA); printf("%8" PRIu64, file_block_free * state->block_size / GIGA); - printf(" %3u%%", perc(file_block_count, file_block_count + file_block_free)); + printf(" %3u%%", muldiv(file_block_count, 100, file_block_count + file_block_free)); printf("\n"); /* warn about invalid data free info */ @@ -468,13 +460,13 @@ int state_status(struct snapraid_state* state) if (unsynced_blocks) { printf("WARNING! The array is NOT fully synced.\n"); - printf("You have a sync in progress at %u%%.\n", (blockmax - unsynced_blocks) * 100 / blockmax); + printf("You have a sync in progress at %u%%.\n", muldiv(blockmax - unsynced_blocks, 100, blockmax)); } else { printf("No sync is in progress.\n"); } if (unscrubbed_blocks) { - printf("The %u%% of the array is not scrubbed.\n", (unscrubbed_blocks * 100 + blockmax - 1) / blockmax); + printf("The %u%% of the array is not scrubbed.\n", muldiv_upper(unscrubbed_blocks, 100, blockmax)); } else { printf("The full array was scrubbed at least one time.\n"); } @@ -487,7 +479,7 @@ int state_status(struct snapraid_state* state) } if (rehash) { - printf("You have a rehash in progress at %u%%.\n", (count - rehash) * 100 / count); + printf("You have a rehash in progress at %u%%.\n", muldiv(count - rehash, 100, count)); } else { if (state->besthash != state->hash) { printf("No rehash is in progress, but for optimal performance one is recommended.\n"); diff --git a/cmdline/util.c b/cmdline/util.c index 7de2baba..30462a1b 100644 --- a/cmdline/util.c +++ b/cmdline/util.c @@ -672,3 +672,22 @@ int lock_unlock(int f) } #endif +/****************************************************************************/ +/* muldiv */ + +unsigned muldiv(uint64_t v, uint64_t mul, uint64_t div) +{ + if (!div) + return 0; + + return (uint32_t)(v * mul / div); +} + +unsigned muldiv_upper(uint64_t v, uint64_t mul, uint64_t div) +{ + if (!div) + return 0; + + return (uint32_t)((v * mul + div - 1) / div); +} + diff --git a/cmdline/util.h b/cmdline/util.h index 4f9a5aa6..18998e89 100644 --- a/cmdline/util.h +++ b/cmdline/util.h @@ -251,5 +251,10 @@ static inline int bit_vect_test(bit_vect_t* bit_vect, size_t off) return (bit_vect[off / BIT_VECT_SIZE] & mask) != 0; } -#endif +/****************************************************************************/ +/* muldiv */ +unsigned muldiv(uint64_t v, uint64_t mul, uint64_t div); +unsigned muldiv_upper(uint64_t v, uint64_t mul, uint64_t div); + +#endif