Closed Sharpie closed 13 years ago
So this is seriously strange, I currently have it narrowed down to something in the runme.R
code.
require(tikzDevice)
tikz(sanitize=T)
source('runme.R')
# *crash*
It definitely has to do with the the sanitize option because it works fine without it. Can anyone else confirm this?
Just got a new error:
Error in grid.Call.graphics("L_setviewport", pvp, TRUE) :
REAL() can only be applied to a 'numeric', not a 'NULL'
If I comment out the piece of my ~/.Rprofile
that sets a permanent metrics dictionary, I get the following behavior for R 2.11.1:
R.app
executes:
require(tikzDevice)
tikz(sanitize=T)
source('runme.R')
Just fine.
R64.app
crashes.The plot thickens.
env R_ARCH=/i386 R
dies with:
Creating temporary TikZ metrics dictionary at: /var/folders/Iy/IyVZ0c2iFn042dabCYLImE+++TI/-Tmp-//RtmpqunnTI/tikzMetricsDictionary Abort trap
env R_ARCH=/x86_64 R
throws an error with:
Creating temporary TikZ metrics dictionary at: /var/folders/Iy/IyVZ0c2iFn042dabCYLImE+++TI/-Tmp-//RtmpYW64jx/tikzMetricsDictionary Error in .Call(fnname, ..., PACKAGE = "grid") : C symbol name "L_getEngineDLon" not in DLL for package "grid"
env R_ARCH=/x86_64 R
error when using 32 bit R. #
Error in get(name, envir = asNamespace(pkg), inherits = FALSE)
object 'sha1' not found
Error in dbExists(dictionary, sha1(key))
error in evaluating the argument 'key' in selecting a method for function 'dbExists'
Error in grid.Call("L_setDLelt", x)
SET_VECTOR_ELT() can only be applied to a 'list', not a 'NULL'
I am now convinced that the problem has something to do with the way R is feeling when I open it. It is being moody lately.
What the hell did you feed it in the sanitize function? :P
I'm getting the same feeling you are-- R
was running the example just fine this morning. Now it crashes in a different way.
It is really difficult to reproduce this problem and figure out exactly what is going on.
It must have had a bad night
I'm starting to suspect a buffer overflow in the C implementation of Sanitize()
.
The reason is when buffer overflows occur, other pieces of memory get overwritten. This causes weird shit to happen such as random functions suddenly not being present in the symbol table. i.e. C symbol name "L_getEngineDLon" not in DLL for package "grid"
.
If I change the allocation on line 1572 of tikzDevice.c
from:
char *cleanStringCP = (char *) calloc( strlen(cleanString), sizeof(char) );
To:
char *cleanStringCP = (char *) calloc( strlen(cleanString) * 2, sizeof(char) );
This essentially doubles the space available for the sanitized string and the crashes seem to disappear (at least for command line R, have not tried R GUI). I'm not advocating this as a final fix, but it could pin the problem down as being a buffer overflow, in which case we need to carefully consider our string lengths, allocation of string pointers, and freeing them when they are no longer used.
Could you check to see if this modification fixes the problem on your end?
Pretty positive that it is a string buffer overflow. According to devx.com, first Google hit for sizeof strlen:
Furthermore, there's a potential bug that may result from using strlen: it does not count the terminating null character. In order to get a string size correctly, the value returned from strlen has to be incremented by 1.
So the way calloc
is used in Sanitize
provides a variable that is 1 slot too small to hold the results of sanitizeTexString
and so we are always causing a buffer overflow when this fucntion is called.
The tikzMetrics dictionary comes into the picture in the following way. When no permanent metrics dictionary is used a temporary one is created. The temporary contains no cached metrics, so R has to call TikZ_MetricInfo
in addition to TikZ_Text
. This doubles the number of calls to Sanitize
therefore doubling the number of buffer overflows. The end result is that the probability something critical to R will be overwritten is increased when a temporary metrics dictionary is used.
Ain't C a beautiful language?
Great! I Love C! I can confirm that the modification fixes the problem in the GUI and the command line. It makes sense that the problem is a buffer overflow. That would explain the erratic behavior. But this still surprises me since I thought that cleanStringCP would be allocated to whatever size the sanitized string was that came back from R. Do you know how to achieve this?
Just another example of me not really knowing wtf is going on in C and it coming back to bite us in the ass.
EDIT: So the line just needs to be
char *cleanStringCP = (char *) calloc( strlen(cleanString) + 1, sizeof(char) );
Allright, I will pull it in and add some other stringlen-related fixes I have been meaning to do and then push to R-Forge.
Reported in issue 15. Apparently only happens when Sweave is invoked from Rgui or Rscript.
Appears to be related to the creation of temporary metrics dictionaries:
Files to reproduce located at:
http://www.sendspace.com/file/obbg5h
EDIT: Changed the name of the issue because it is not specific to Sweave