r-lib/progress

Segfault when `total = 0`

Opened this issue · 0 comments

It's easy to create a segfault if you create a progress bar with total = 0. Of course, I didn't mean to do this, it's just an edge case and I will rework things on my end so this doesn't happen.

In my actual usage, I create a progress bar and followed this recommendation:

It is good practice to call tick(0) at the beginning of the computation or download, which shows the progress bar immediately."

and that's where things go sideways if total = 0.

The main problem is that this makes ratio() return NaN. I think ratio() should return either 0 or 1 in the 0/0 case. But I'm not sure which.

double ratio() {
double ratio = current / total;
if (ratio < 0) ratio = 0;
if (ratio > 1) ratio = 1;
return ratio;
}


Contents of segfault.cpp, based on the example package in the tests:

#include <Rcpp.h>
#include <inst/include/RProgress.h>
#include <unistd.h>

// [[Rcpp::export]]
Rcpp::CharacterVector test_progress(Rcpp::CharacterVector formatSEXP =
                                      "[:bar] :percent ", double total = 100) {
  const char *format = formatSEXP[0];
  RProgress::RProgress pb(format, total);

  pb.tick(0);
  for (int i = 0; i < 100; i++) {
    usleep( (useconds_t) (2.0 / 100 * 1000000));
    pb.tick();
  }

  Rcpp::CharacterVector result(1);
  result[0] = "DONE";
  return result;
}

In R:

Rcpp::sourceCpp("segfault.cpp")

## nice demo :)
test_progress()

## this will segfault!
test_progress(total = 0)

Here's what I see in the debugger, which suggests the problem is total = 0 --> ratio() returns NaN --> ratio_now = complete_len = NaN --> i in a for loop initializes to nonsense.

> test_progress(total = 0)
Process 20422 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010a0f1be7 sourceCpp_2.so`RProgress::RProgress::render(this=0x00007ffeefbfd9f8) at RProgress.h:181
   178 	    if (!bar) Rf_error("Progress bar: out of memory");
   179 	    for (int i = 0; i < complete_len; i++) { bar[i] = complete_char; }
   180 	    for (long int i = (long int) complete_len; i < bar_width; i++) {
-> 181 	      bar[i] = incomplete_char;
   182 	    }
   183 	    bar[bar_width] = '\0';
   184 	    replace_all(str, ":bar", bar);
Target 0: (R) stopped.
(lldb) frame variable i
(long) i = -9223372036854775808
(lldb) frame variable complete_len
(double) complete_len = NaN
(lldb) frame variable ratio_now
(double) ratio_now = NaN