[Rd] Forcing a PROTECT Bug to Occur

Tomas Kalibera tom@@@k@||ber@ @end|ng |rom gm@||@com
Sun Apr 30 10:46:17 CEST 2023


On 4/30/23 06:05, Michael Milton wrote:
> Hi Tomas, thanks for the reply.
>
> I played with some of the factors you mentioned like allocating more 
> INTSXP of the same size as vec_1, to little success. The thing that 
> actually "worked" and caused a segfault, was simply allocating a 
> larger vector the first time. 100 elements seemed to do it, but 10 or 
> less elements almost never caused a segfault. Is there some other part 
> of memory that small vectors are allocated that would prevent them 
> being collected? Might this relate to the Ncells vs Vcells distinction?

I don't think it would be directly related (these are both vectors), but 
I don't remember all the details. If you are interested in how exactly 
the allocator works, I suggest reading memory.c - the sources are quite 
small and easy to read. There is some external fragmentation which 
limits what can be re-used. You could in principle force re-use by using 
objects of the same size and type (but the number might have to be 
large, maybe it wasn't large enough), or a single larger object (as you 
did). It doesn't make sense speculating more without reading the code, 
instrumenting and possibly debugging, if you want to find out exactly 
what is happening in your situation.

Tomas

> > z = inline::cfunction(body="
>     SEXP vec_1 = Rf_allocVector(INTSXP, 100);
>     SEXP vec_2 = Rf_allocVector(VECSXP, 3);
>     SET_VECTOR_ELT(vec_2, 1, vec_1);
> ")
> > z()
> NULL
> Warning message:
> In z() : your C program does not return anything!
> > gctorture(TRUE)
> > z()
>
>  *** caught segfault ***
> address 0x55a68ce57f90, cause 'memory not mapped'
>
> Traceback:
>  1: doWithOneRestart(return(expr), restart)
>  2: withOneRestart(expr, restarts[[1L]])
>  3: withRestarts({  .Internal(.signalCondition(simpleWarning(msg, 
> call), msg,     call))    .Internal(.dfltWarn(msg, call))}, 
> muffleWarning = function() NULL)
>  4: .signalSimpleWarning("your C program does not return anything!",   
>   base::quote(z()))
>  5: .Primitive(".Call")(<pointer: 0x7f2a02572160>)
>  6: z()
>
>
>
>
> On Sun, Apr 30, 2023 at 4:04 AM Tomas Kalibera 
> <tomas.kalibera using gmail.com> wrote:
>
>
>     On 4/29/23 19:26, Michael Milton wrote:
>     > I'm trying to learn about R's PROTECT system. To that end I've
>     tried to
>     > create an example of C code that doesn't protect anything. I was
>     hoping it
>     > would either segfault from trying to access deallocated memory,
>     or maybe
>     > print out nonsense results because the unprotected memory got
>     overwritten,
>     > but I can't make either happen.
>     >
>     > Here's my current code (all in R, using the inline package for
>     simplicity):
>     >
>     >> gctorture(TRUE)
>     >> z = inline::cfunction(body="
>     >      SEXP vec_1 = Rf_ScalarInteger(99);
>     >      SEXP vec_2 = Rf_allocVector(VECSXP, 10);
>     >      SET_VECTOR_ELT(vec_2, 1, vec_1);
>     >      Rf_PrintValue(vec_2);
>     > ")
>     >
>     > My thinking was that, with torture mode enabled, the allocation
>     of vec_2
>     > should ensure that vec_1 is collected, and then trying to put it
>     into vec_2
>     > and then print it would then fail. But it consistently prints 99
>     in the
>     > list's second element.
>     >
>     > Why does this code not have issues? Alternatively, is there a
>     simpler
>     > example of C code that demonstrates missing PROTECT calls?
>
>     It is not guaranteed that a PROTECT error will always lead to memory
>     corruption or a crash in all executions of a program, not even with
>     gctorture.
>
>     To increase the chances, you can instruct gctorture to run the gc
>     more
>     often (but the execution would be slower). You can build R with
>     strict
>     write barrier checking (see e.g. Writing R Extensions, look for
>     gctorture) to make it more likely errors will be detected. There is
>     always a tradeoff between this probability and slowdown of the
>     execution. In theory we could invalidate all unreachable objects at
>     every allocation, but that would be so slow that we could not test
>     any
>     programs in practice.
>
>     Then, not every piece of memory can be used by any allocation, due to
>     how the memory allocator and gc works. If you need to provoke an
>     error
>     e.g. for educational purposes, perhaps chances would be higher if you
>     allocate an object of the same type and size as the one not
>     protected in
>     error.
>
>     You can also play with R sources - e.g. introduce a PROTECT error
>     into
>     some key part of the R interpreter, it should not be too hard to
>     trigger
>     that via make check-devel.
>
>     Best
>     Tomas
>
>     >
>     >       [[alternative HTML version deleted]]
>     >
>     > ______________________________________________
>     > R-devel using r-project.org mailing list
>     > https://stat.ethz.ch/mailman/listinfo/r-devel
>
	[[alternative HTML version deleted]]



More information about the R-devel mailing list