1

I am writing a package using Rcpp Function, the package compiles, and R CMD Check also works fine. Earlier, the input to package's cvode function was an XPtr, but now the input can be both XPtr or an R or Rcpp function (the implementation was based on a earlier post). Currently, input functions in R, Rcpp and Rcpp::XPtr work in the package.

The package had previously had clang-UBSAN issues, so I am trying to detect them beforehand now using rhub package. On running check_with_sanitizers() command from the rhub package, I am getting the following error:

eval.c:677:21: runtime error: member access within null pointer of type 'struct SEXPREC'

─   *** caught segfault ***
   address (nil), cause 'memory not mapped'
   Segmentation fault (core dumped)

I have been able to isolate the error to the line ydot1 = rhs_fun(t, y1); in the following piece of code, i.e., commenting/un-commenting the expression above reproduces the error.

My question is - is there a check I should be performing before calling the rhs_fun Function, to avoid the segmentation fault error?

Note - check_with_valgrind() produces no error.

Thanks!

// struct to use if R or Rcpp function is input as RHS function
struct rhs_func{
  Function rhs_eqn;
};

int rhs_func(realtype t, N_Vector y, N_Vector ydot, void* user_data){

  // convert y to NumericVector y1
  int y_len = NV_LENGTH_S(y);

  NumericVector y1(y_len);    // filled with zeros
  realtype *y_ptr = N_VGetArrayPointer(y);
  for (int i = 0; i < y_len; i++){
    y1[i] = y_ptr[i];
  }

  // convert ydot to NumericVector ydot1
  // int ydot_len = NV_LENGTH_S(ydot);

  NumericVector ydot1(y_len);    // filled with zeros

  // // cast void pointer to pointer to struct and assign rhs to a Function
  struct rhs_func *my_rhs_fun = (struct rhs_func*)user_data;

  if(my_rhs_fun){

    Function rhs_fun = (*my_rhs_fun).rhs_eqn;

    // use the function to calculate value of RHS ----
    // Uncommenting the line below gives runtime error
    // ydot1 = rhs_fun(t, y1);

  }
  else {

    stop("Something went wrong, stopping!");
  }


  // convert NumericVector ydot1 to N_Vector ydot
  realtype *ydot_ptr = N_VGetArrayPointer(ydot);
  for (int i = 0; i<  y_len; i++){
    ydot_ptr[i] = ydot1[i];
  }

  // everything went smoothly
  return(0);
}

An update - based on the comments below, I have added checks. So the check succeeds, but I can see the rhs_fun is NULL as the code goes to the stop message.

 if(my_rhs_fun){
    Function rhs_fun = (*my_rhs_fun).rhs_eqn;
    // use the function to calculate value of RHS ----
    if (rhs_fun){
      ydot1 = rhs_fun(t, y1);
    }
    else{
      stop("Something went wrong");
    }
  }
  else {
    stop("Something went wrong, stopping!");
  }

An added check is added to the struct also

  if (Rf_isNull(input_function)){

    stop("Something is wrong with input function, stopping!");
  }

The checks succeed, but I see that rhs_fun is NULL as the else message is printed

   Error in cvode(time_vec, IC, ODE_R, reltol, abstol) : 
     Something went wrong
   Execution halted

Not sure why, as the examples I have tried have worked without complaints.

Satya
  • 1,708
  • 1
  • 15
  • 39
  • @JosephWood You are correct, there is code from another library which is used here,eg, N_Vector. The full code can be seen at - https://github.com/sn248/sundialr/blob/master/src/cvode.cpp . I was trying to reduce to the main Crux of the problem. – Satya Jul 17 '18 at 01:49
  • Yeah, I found that already and looking through it.... I guess a better question is what scenario will it run **without** error and what scenario causes the segfault? That way we can have something to better troubleshoot. – Joseph Wood Jul 17 '18 at 01:51
  • @JosephWood Thanks for looking into the problem. To be clear, the examples work. I have not been able to find a use case where I see a runtime error. I was talking about the message I get when I run 'check_with_sanitizers()' on the package. I am not sure if this is going to be problem on CRAN submission, I suspect it will show an issue. – Satya Jul 17 '18 at 02:20
  • It will definitely show as an issue. I can't say whether it will be a problem with CRAN, but I strongly recommend that you get to the bottom of it before you submit. It may get archived if it isn't addressed. – Joseph Wood Jul 17 '18 at 02:31
  • Do you see any problems when you check with `gctorture`? – Ralf Stubner Jul 17 '18 at 06:14
  • @RalfStubner - Thanks, `R CMD Check --as-cran --use-gct` i.e., `devtools::check(args = c('--as-cran','--use-gct'))` gave no errors, warnings or notes. – Satya Jul 17 '18 at 10:35
  • 1
    Ok. The most likely candidate for a NULL reference is rhs_fun. So it makes sense to test that before executing the function. In addition I would check that you are not inserting a NULL reference into the struct. – Ralf Stubner Jul 17 '18 at 12:47
  • @RalfStubner - Thanks, your suggestions make the checks a success, but from the messages I see that `rhs_fun` is `NULL`. Not sure why, as the examples work. Also, is `if(rhs_fun)` the right way, or do I use `Rf_isNull`, to check for `NULL`. If you write your comment as answer, I can accept it. Thanks! – Satya Jul 17 '18 at 14:45

1 Answers1

2

The most likely candidate for a NULL reference is rhs_fun. So it makes sense to test that before executing the function:

if(rhs_fun) {
   ...
} else {
   stop(...)
}

The R API function Rf_isNull would not be appropriate here, since that checks for an SEXP of type NILSXP and segfaults for an actual nullptr.

In addition I would check that you are not inserting a NULL reference into the struct, though I find it unlikely that anything is going wrong there.

Overall this is just a workaround. It would be interesting to know what triggers this behavior.

Ralf Stubner
  • 26,263
  • 3
  • 40
  • 75