Skip to content

Do not divide by zero when a download reports no Content-Length - #4551

Open
Daksha1611 wants to merge 1 commit into
openvinotoolkit:mainfrom
Daksha1611:fix-progress-divzero
Open

Do not divide by zero when a download reports no Content-Length#4551
Daksha1611 wants to merge 1 commit into
openvinotoolkit:mainfrom
Daksha1611:fix-progress-divzero

Conversation

@Daksha1611

@Daksha1611 Daksha1611 commented Sep 11, 2026

Copy link
Copy Markdown

🛠 Summary

Fixes #4550.

print_progress() computed (float)count / max with max taken straight from libcurl's dltotal. A response using chunked transfer encoding reports dltotal == 0, and the guard in progress_callback only short-circuits while dltotal == dlnow, so once any bytes arrive the callback falls through to print_progress(dlnow, 0, ...).

The ratio is then infinite, and converting that to int is undefined — on x86-64 it yields INT_MIN, so the bar-fill loop does not run and the padding loop below it runs from INT_MIN to bar_width. That is roughly 2.1 billion printf(" ") calls per tick, once a second, so the pull looks frozen and floods the terminal.

Reports the running byte count when the total is unknown, and moves the bar arithmetic into computeProgressBarCells(), which returns 0 for an unknown total and clamps to [0, barWidth]. The clamp also covers a server reporting more bytes than it announced, which previously overran the bar the other way.

computeProgressBarCells() is declared in the header only so the arithmetic can be unit tested — print_progress() is a file-local static that writes to stdout, and there is no existing test surface for the pull module's console output. If you would rather keep the header minimal I am happy to make it static again and drop the tests. The tests live in pull_hf_model_test.cpp, whose target already links curl_downloader transitively, so no BUILD change was needed.

I have no OVMS build container available, so this is not compiled against the full tree and CI will need to confirm the build. I did check the helper in isolation, including reproducing the original INT_MIN result from the two upstream lines. Draft for that reason.

🧪 Checklist

  • Unit tests added.
  • The documentation updated.
  • Change follows security best practices.

print_progress() computed (float)count / max with max taken straight from
libcurl's dltotal. A response using chunked transfer encoding reports
dltotal == 0, and the guard in progress_callback only short-circuits while
dltotal == dlnow, so as soon as any bytes arrive the callback falls through
to print_progress(dlnow, 0, ...).

The ratio is then infinite. Converting that to int is undefined; on x86-64 it
yields INT_MIN, so the bar-fill loop does not run and the padding loop below
it runs from INT_MIN to bar_width - roughly 2.1 billion putchar calls on
every progress tick, once a second. The pull looks frozen and floods the
terminal.

Handle an unknown total explicitly by reporting the running byte count
instead of a percentage, and move the bar arithmetic into
computeProgressBarCells(), which returns 0 for an unknown total and clamps
the result to [0, barWidth]. The clamp also covers a server reporting more
bytes than it announced, which previously overran the bar.

computeProgressBarCells() is declared in curl_downloader.hpp only so the
arithmetic can be unit tested - print_progress() itself is a file-local
static that writes to stdout. Happy to inline it and drop the tests if the
smaller header surface is preferred.

Tests: adds CurlDownloaderProgressTest covering the unknown-total case that
caused the hang, normal ratio tracking, and clamping at both ends.
@Daksha1611
Daksha1611 marked this pull request as ready for review September 12, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Download progress bar divides by zero without Content-Length and prints ~2.1 billion spaces per tick

1 participant