From jeff.horner at vanderbilt.edu  Thu Jul  5 18:12:38 2007
From: jeff.horner at vanderbilt.edu (Jeffrey Horner)
Date: Thu, 05 Jul 2007 11:12:38 -0500
Subject: [Rd] problem with findFun call from embedded R
In-Reply-To: <468C0961.9020804@joeconway.com>
References: <468C0961.9020804@joeconway.com>
Message-ID: <468D1876.2030605@vanderbilt.edu>

Joe Conway wrote:
> I was debugging a problem reported to me regarding PL/R, and found that 
> I can duplicate it using only R sources. It might be characterized as 
> possibly a misuse of the findFun() function, but I leave that for the R 
> devel experts to decide.
> 
> The below results are all with R-2.5.1 (I can't seem to download 
> r-patched at the moment, but didn't see anything in the 2.5.1-patched 
> release notes indicating this issue had been noticed) on Fedora Core 6 
> x86_64 (also duplicated with R-2.5.0 on FC7 i386).
> 
> Steps to reproduce:
> 8<------------------------
>   configure, make, make install from source tree
>   cd tests/Embedding/
>   make RNamedCall
>   ./RNamedCall  #works as expected
>   mv foo.R foo.R.orig
>   ./RNamedCall  #segfaults
> 8<------------------------
> 
> output:
> 8<------------------------
> Error in file(file, "r", encoding = encoding) :
>          unable to open connection
> In addition: Warning message:
> cannot open file 'foo.R', reason 'No such file or directory' in: 
> file(file, "r", encoding = encoding)
> Error: could not find function "foo"
> 
>   *** caught segfault ***
> address (nil), cause 'memory not mapped'
> 
> Possible actions:
> 1: abort (with core dump, if enabled)
> 2: normal R exit
> 3: exit R without saving workspace
> 4: exit R saving workspace
> Selection: 1
> aborting ...
> Segmentation fault
> 8<------------------------
> 
> The problem comes from RNamedCall starting at line 54:
> 8<------------------------
>    fun = findFun(install("foo"), R_GlobalEnv);
>    if(fun == R_NilValue) {
> 	fprintf(stderr, "No definition for function foo.
>                           Source foo.R and save the session.\n");
>    UNPROTECT(1);
>    exit(1);
>    }
> 8<------------------------
> 
> If foo.R was not found and never sourced, meaning that the object "foo" 
> does not exist, findFun never returns. Instead the segfault occurs at 
> line 719 in errors.c at the LONGJMP statement. Setting a breakpoint at 
> erros.c:719, the backtrace looks like this (will wrap poorly):
> 
> 8<------------------------
> Breakpoint 2, jump_to_top_ex (traceback=TRUE, tryUserHandler=TRUE, 
> processWarnings=TRUE, resetConsole=TRUE,
>      ignoreRestartContexts=FALSE) at errors.c:719
> 719         LONGJMP(R_ToplevelContext->cjmpbuf, 0);
> (gdb) bt
> #0  jump_to_top_ex (traceback=TRUE, tryUserHandler=TRUE, 
> processWarnings=TRUE, resetConsole=TRUE,
>      ignoreRestartContexts=FALSE) at errors.c:719
> #1  0x00002aaaaab77e5d in verrorcall_dflt (call=0x609d78, 
> format=0x2aaaaace4a4d "%s", ap=0x7fffa13a94c0)
>      at errors.c:516
> #2  0x00002aaaaab7814a in Rf_errorcall (call=0x609d78, 
> format=0x2aaaaace4a4d "%s") at errors.c:551
> #3  0x00002aaaaab78347 in Rf_error (format=0x2aaaaace42da "could not 
> find function \"%s\"") at errors.c:578
> #4  0x00002aaaaab708e5 in Rf_findFun (symbol=0xc64f20, rho=0x649fa0) at 
> envir.c:1244
> #5  0x0000000000400e0c in bar1 () at RNamedCall.c:54
> #6  0x0000000000400d59 in main (argc=1, argv=0x7fffa13ab838) at 
> RNamedCall.c:16
> 8<------------------------
> 
> And then stepping into line 719 generates the segfault:
> 8<------------------------
> (gdb) s
> 
> Program received signal SIGSEGV, Segmentation fault.
> 0x00002aaaaab701ce in Rf_findVar (symbol=0x6c12b8, rho=0x0) at envir.c:998
> 998         if (TYPEOF(rho) == NILSXP)
> 8<------------------------
> 
> I believe this is happening because findFun() was not executed inside a 
> valid context, and therefore R_ToplevelContext->cjmpbuf is invalid.
> 
> My question is -- is the above an abuse of findFun() by RNamedCall.c 
> (meaning I should avoid the same pattern)? Or maybe it should be wrapped 
> in something similar to R_ToplevelExec?

Yes, when one embeds R, findFun() will call error() which causes the 
longjmp() to the top level, so the embedding application shouldn't use 
findFun().

  Here's what I've written for the next version of RApache. Note that 
it's behavior is slightly different than the original findFun(), in that 
it doesn't search the enclosing environments. (Also, comments welcome as 
the implementation is based on the original, and I'm unsure if promise 
evaluation is needed.)


/* This one doesn't longjmp when function not found */
static SEXP MyfindFun(SEXP symb, SEXP envir){
     SEXP fun;
     SEXPTYPE t;
     fun = findVar(symb,envir);
     t = TYPEOF(fun);

     /* eval promise if need be */
     if (t == PROMSXP){
         int error=1;
         fun = R_tryEval(fun,envir,&error);
         if (error) return R_UnboundValue;
         t = TYPEOF(fun);
     }

     if (t == CLOSXP || t == BUILTINSXP || t == BUILTINSXP || t == 
SPECIALSXP)
         return fun;
     return R_UnboundValue;
}

> 
> ==========================
> 
> On a related note, I found during my PL/R debugging that the segfault 
> was causing the same console based, interactive, "*** caught segfault 
> ***" logic to execute. I was able to confirm that R_Interactive was set 
> to TRUE in my PL/R session.
> 
> It seems that Rf_initEmbeddedR() sets R_Interactive to TRUE and depends 
> on the output of isatty() to change it to FALSE in Rf_initialize_R() if 
> there is no tty. Unfortunately, the most common methods for starting 
> Postgres leave the tty attached (stdout and stderr are directed to the 
> log file). I ended up explicitly writing "R_Interactive = FALSE" just 
> after running Rf_initEmbeddedR() -- is this a safe and reasonable thing 
> to do?
> 
> Thanks,
> 
> Joe
> 
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel


-- 
http://biostat.mc.vanderbilt.edu/JeffreyHorner



[Rd] problem with findFun call from embedded R
Jeffrey Horner