openvinotoolkit / openvinotoolkit/model_server

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

Open
#4,550 0 comments 0 reactions 1 assignee View on GitHub

@rasapala is already working on this.

Since Sep 15, 2026.

Dominant language
C++
Stars
931
Forks
277
Avg merge
2d 13h
Merged PRs (30d)
68

Description

Describe the bug

When a model download is served over a response with no Content-Length (chunked transfer encoding), the progress bar divides by zero and then prints roughly 2.1 billion spaces per progress tick. The pull appears to hang; the transfer itself is fine.

src/pull_module/curl_downloader.cpp:49-63:

static void print_progress(size_t count, size_t max, bool first_run, size_t elapsed_time) {
    float progress = (float)count / max;          // :50   max == 0 -> inf
    if (!first_run && progress < 0.01 && count > 0)
        return;

    const int bar_width = 50;
    int bar_length = progress * bar_width;        // :55   (int)inf is undefined
    ...
    for (i = bar_length; i < bar_width; ++i) {    // :62
        printf(" ");
    }

max is libcurl's dltotal, which is 0 when the server does not announce a size. The guard in progress_callback (src/pull_module/curl_downloader.cpp:133-135) only short-circuits while dltotal == dlnow:

if ((dltotal == dlnow) && dltotal < 10000) {
    return 0;
}

Once any bytes have arrived, dlnow > 0 == dltotal, so the callback falls through to print_progress(dlnow, 0, ...).

progress becomes inf. Converting an infinite float to int is undefined behaviour; on x86-64 it yields INT_MIN. The bar-fill loop at :59 therefore does not run at all, and the padding loop at :62 runs from INT_MIN to bar_width.

Compiling exactly those two lines and counting the padding iterations:

bar_length = -2147483648
padding loop would run 2147483698 times

That is ~2.1 billion printf(" ") calls on every tick, and progress_callback prints once a second.

Related, same function: bar_length is never clamped, so a server reporting more bytes than it announced (dlnow > dltotal, which happens with some content encodings) makes progress > 1 and overruns the bar in the other direction.

To Reproduce

Point a pull at any HTTP endpoint that responds with Transfer-Encoding: chunked and no Content-Length — a local mirror, a proxy, an internal registry, or a plain python3 -m http.server in front of the artifacts:

ovms --pull --source_model <model> --model_repository_path /tmp/repo

The download proceeds but the console stops being usable and the process appears stuck.

Expected behavior

With an unknown total size, print a running byte count (or a spinner) rather than a percentage bar, and never divide by max when max == 0. The bar length should also be clamped to the bar width so no ratio can drive the loops out of range.

Logs

Console output only — the terminal fills with whitespace and the \r-based progress line never completes.

Configuration

  1. OVMS version: main @ fadb3314
  2. Any --pull against a source served without Content-Length
  3. CPU
  4. N/A
  5. Any model

Additional context

Suggested fix: early-return from print_progress when max == 0 and report bytes received instead, and move the bar arithmetic into a small helper that returns 0 for an unknown total and clamps to [0, barWidth].

I have a patch for this and will open a PR shortly.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.