Bug 13487 - Segfault when mistakenly calling [.data.frame
Segfault when mistakenly calling [.data.frame
Status: RESOLVED FIXED
Product: R
Classification: Unclassified
Component: Misc
old
All Linux
: P5 normal
Assigned To: R-core
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2009-01-29 21:31 UTC by Jitterbug compatibility account
Modified: 2014-02-16 11:42 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jitterbug compatibility account 2009-01-29 21:31:45 UTC
From: brechbuehler@gmail.com
Full_Name: Christian Brechbuehler
Version: 2.7.2, 2.8.1
OS: linux-gnu
Submission from: (NULL) (24.128.51.18)


Calling [.data.frame on an object that's not a data frame, specifically 1:10,
causes segmentation fault.

Context
=======
We can subscript with a number of different notations:

   > (1:10)[3]
   [1] 3
   > do.call(get("[",pos="package:base"),list(1:10,3))
   [1] 3
   > do.call(get("[.numeric_version",pos="package:base"),list(1:10,3))
   [1] 3

Problem
=======
If we mistakenly believe the object is a data frame (as we did in a much more
complicated real situation), this happens: 

   > do.call(get("[.data.frame",pos="package:base"),list(1:10,3))
   Error in NextMethod("[") : 
     no calling generic was found: was a method called directly?

    *** caught segfault ***
   address (nil), cause 'unknown'

   Process R:2 segmentation fault (core dumped) at Thu Jan 29 09:26:29 2009

The Error message is appropriate.  But the segmentation fault is unexpected.


Versions
========
I reproduced the problem on R 2.7.2 and 2.8.1.  Details:

> version
               _                                          
platform       x86_64-unknown-linux-gnu                   
arch           x86_64                                     
os             linux-gnu                                  
system         x86_64, linux-gnu                          
status         Patched                                    
major          2                                          
minor          7.2                                        
year           2008                                       
month          09                                         
day            20                                         
svn rev        46776                                      
language       R                                          
version.string R version 2.7.2 Patched (2008-09-20 r46776)

==========================================================

> version
               _                                          
platform       x86_64-unknown-linux-gnu                   
arch           x86_64                                     
os             linux-gnu                                  
system         x86_64, linux-gnu                          
status         Patched                                    
major          2                                          
minor          8.1                                        
year           2009                                       
month          01                                         
day            26                                         
svn rev        47743                                      
language       R                                          
version.string R version 2.8.1 Patched (2009-01-26 r47743)

Comment 1 Jitterbug compatibility account 2009-01-30 03:44:40 UTC
From: Prof Brian Ripley <ripley@stats.ox.ac.uk>
What did your actual application do?  This seems a very strange thing 
to do, and the segfault is in trying to construct the traceback.

Only by using do.call on the object (and not even by name) do I get 
this error.  E.g.

> `[.data.frame`(1:10, 3)
Error in NextMethod("[") : object not specified
> do.call("[.data.frame", list(1:10, 3))
Error in NextMethod("[") : object not specified

are fine.

Obviously it would be nice to fix this, but I'd like to understand the 
actual circumstances: there is more to it than the subject line.

On Thu, 29 Jan 2009, brechbuehler@gmail.com wrote:

> Full_Name: Christian Brechbuehler
> Version: 2.7.2, 2.8.1
> OS: linux-gnu
> Submission from: (NULL) (24.128.51.18)
>
>
> Calling [.data.frame on an object that's not a data frame, specifically 1:10,
> causes segmentation fault.
>
> Context
> =======
> We can subscript with a number of different notations:
>
>   > (1:10)[3]
>   [1] 3
>   > do.call(get("[",pos="package:base"),list(1:10,3))
>   [1] 3
>   > do.call(get("[.numeric_version",pos="package:base"),list(1:10,3))
>   [1] 3
>
> Problem
> =======
> If we mistakenly believe the object is a data frame (as we did in a much more
> complicated real situation), this happens:
>
>   > do.call(get("[.data.frame",pos="package:base"),list(1:10,3))
>   Error in NextMethod("[") :
>     no calling generic was found: was a method called directly?
>
>    *** caught segfault ***
>   address (nil), cause 'unknown'
>
>   Process R:2 segmentation fault (core dumped) at Thu Jan 29 09:26:29 2009
>
> The Error message is appropriate.  But the segmentation fault is unexpected.
>
>
> Versions
> ========
> I reproduced the problem on R 2.7.2 and 2.8.1.  Details:
>
>> version
>               _
> platform       x86_64-unknown-linux-gnu
> arch           x86_64
> os             linux-gnu
> system         x86_64, linux-gnu
> status         Patched
> major          2
> minor          7.2
> year           2008
> month          09
> day            20
> svn rev        46776
> language       R
> version.string R version 2.7.2 Patched (2008-09-20 r46776)
>
> ==========================================================
>
>> version
>               _
> platform       x86_64-unknown-linux-gnu
> arch           x86_64
> os             linux-gnu
> system         x86_64, linux-gnu
> status         Patched
> major          2
> minor          8.1
> year           2009
> month          01
> day            26
> svn rev        47743
> language       R
> version.string R version 2.8.1 Patched (2009-01-26 r47743)
>
> ______________________________________________
> R-devel@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

-- 
Brian D. Ripley,                  ripley@stats.ox.ac.uk
Professor of Applied Statistics,  http://www.stats.ox.ac.uk/~ripley/
University of Oxford,             Tel:  +44 1865 272861 (self)
1 South Parks Road,                     +44 1865 272866 (PA)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595

Comment 2 Jitterbug compatibility account 2009-01-30 21:30:26 UTC
From: Christian Brechbühler <brechbuehler@gmail.com>
On Thu, Jan 29, 2009 at 4:44 PM, Prof Brian Ripley <ripley@stats.ox.ac.uk>wrote:

> What did your actual application do?  This seems a very strange thing to
> do, and the segfault is in trying to construct the traceback.
>
> Only by using do.call on the object (and not even by name) do I get this
> error.  E.g.
>
>  `[.data.frame`(1:10, 3)
>>
> Error in NextMethod("[") : object not specified
>
>> do.call("[.data.frame", list(1:10, 3))
>>
> Error in NextMethod("[") : object not specified
>
> are fine.
>
> Obviously it would be nice to fix this, but I'd like to understand the
> actual circumstances: there is more to it than the subject line.


Yes, there is more.  For reporting the problem, we tried to pare it down to
a concise, self-contained test case.

My boss was debugging an issue in our R code.  We have our own "[...."
functions, because stock R drops names when subscripting.  To bypass our
now-suspect functions and get the "real" subscripting method, he used "get"
from package:base.  He was examining a large object, and believing it was a
data frame, chose "[.data.frame".  As it turns out, that object was not a
data frame, and he got an unexpected segfault.  I think it was a matrix.
But it doesn't matter -- a vector as in the test case will give the same.

We have since fixed the bug in our replacement subscripting function, so the
issue might not affect us any more.

Thanks,
/Christian


> On Thu, 29 Jan 2009, brechbuehler@gmail.com wrote:
>
>  Full_Name: Christian Brechbuehler
>> Version: 2.7.2, 2.8.1
>> OS: linux-gnu
>>
>> If we mistakenly believe the object is a data frame (as we did in a much
>> more
>> complicated real situation), this happens:
>>
>>  > do.call(get("[.data.frame",pos="package:base"),list(1:10,3))
>>  Error in NextMethod("[") :
>>    no calling generic was found: was a method called directly?
>>
>>   *** caught segfault ***
>>  address (nil), cause 'unknown'
>>
>>  Process R:2 segmentation fault (core dumped) at Thu Jan 29 09:26:29 2009
>>
>> The Error message is appropriate.  But the segmentation fault is
>> unexpected.
>>
>

	[[alternative HTML version deleted]]

Comment 3 Jitterbug compatibility account 2009-02-01 16:58:00 UTC
NOTES:
 Error is in forming traceback
Comment 4 Jitterbug compatibility account 2009-02-01 17:58:01 UTC
Audit (from Jitterbug):
Sun Feb  1 11:58:01 2009	ripley	changed notes
Comment 5 Martin Maechler 2010-07-14 11:01:55 UTC
As Brian alludes to, a shorter way to trigger the seg.fault is

  do.call(`[.data.frame`, list(2,3))

the problem is still with us today,
and even if stemming from very improbable user code use, it's bug we should squish...

(all this is mostly to trigger a reminder to all of us)
Comment 6 Brian Ripley 2010-07-15 18:44:24 UTC
I have spent an hour or two looking for this in the past.  Yes, we 
should squash it *if* we have enough resources -- I don't for now.

On Wed, 14 Jul 2010, r-bugs@r-project.org wrote:

> https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=13487
>
>
> Martin Maechler <maechler@stat.math.ethz.ch> changed:
>
>           What    |Removed                     |Added
> ----------------------------------------------------------------------------
>                 CC|                            |maechler@stat.math.ethz.ch
>
>
>
>
> --- Comment #5 from Martin Maechler <maechler@stat.math.ethz.ch>  2010-07-14 06:01:55 ---
> As Brian alludes to, a shorter way to trigger the seg.fault is
>
>  do.call(`[.data.frame`, list(2,3))
>
> the problem is still with us today,
> and even if stemming from very improbable user code use, it's bug we should
> squish...
>
> (all this is mostly to trigger a reminder to all of us)
>
> -- 
> Configure bugmail: https://bugs.r-project.org/bugzilla3/userprefs.cgi?tab=email
> ------- You are receiving this mail because: -------
> You are the assignee for the bug.
>
> _______________________________________________
> R-core list: https://stat.ethz.ch/mailman/listinfo/r-core
>


-- 
Brian D. Ripley,                  ripley@stats.ox.ac.uk
Professor of Applied Statistics,  http://www.stats.ox.ac.uk/~ripley/
University of Oxford,             Tel:  +44 1865 272861 (self)
1 South Parks Road,                     +44 1865 272866 (PA)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595


Comment 7 Matt Shotwell 2010-08-15 06:02:06 UTC
This bug has ruined my weekend :-), but I believe I have something that will help put it behind us. The summary below is terse. I have many more details and notes if necessary.

`[.data.frame` calls NextMethod("["), which calls do_nextmethod. do_nextmethod then gets the context (RCNTXT) of the function that called NextMethod from the context stack. do_nextmethod expects the CDR(RCNTXT->call) member to be a symbol, then uses R_LookupMethod to get the closure of the caller. When we execute do.call(`[.data.frame`, list(1,1)), the call in the context _is already a closure_. That is, CDR(RCNTXT->call) is a CLOSXP. do_nextmethod doesn't check this, and still passes CDR(RCNTXT->call) to R_LookupMethod, which (apparently) corrupts the CLOSXP. In particular, the FORMALS of the CLOSXP become corrupted. When do_nextmethod then calls error(), the corrupted CLOSXP is eventually deparsed, resulting in a call to args2buff, where the actual segfault occurs.

Supposing that the RCNTXT->call is constructed correctly, the next step is to decide the action of do_nextmethod when it encounters a CLOSXP in CDR(RCNTXT->call), rather than an SYMSXP. I corrected the immediate problem by making do_nextmethod aware that CDR(RCNTXT->call) might be the the calling CLOSXP, rather than a SYMBOL for it:

Index: src/main/objects.c
===================================================================
--- src/main/objects.c	(revision 52708)
+++ src/main/objects.c	(working copy)
@@ -547,7 +547,10 @@
     else if (defenv == R_UnboundValue) defenv = R_GlobalEnv;
 
     /* set up the arglist */
-    s = R_LookupMethod(CAR(cptr->call), env, callenv, defenv);
+    if (TYPEOF(CAR(cptr->call)) == CLOSXP)
+        s = CAR(cptr->call);
+    else 
+        s = R_LookupMethod(CAR(cptr->call), env, callenv, defenv);
     if (TYPEOF(s) == SYMSXP && s == R_UnboundValue)
 	error(_("no calling generic was found: was a method called directly?"));
     if (TYPEOF(s) != CLOSXP){ /* R_LookupMethod looked for a function */

This patch renders our offending function call (first of three below) equivalent to similar calls

> do.call(`[.data.frame`,list(1,1))
Error in NextMethod("[") : object not specified
> do.call("[.data.frame",list(1,1))
Error in NextMethod("[") : object not specified
> `[.data.frame`(1,1)
Error in NextMethod("[") : object not specified

> do.call(`[.data.frame`,list(data.frame(1),1))
  X1
1  1
> do.call("[.data.frame",list(data.frame(1),1))
  X1
1  1
> `[.data.frame`(data.frame(1),1)
  X1
1  1

However, I think it would be prudent, at this time, for someone more familiar with the S3 internals to review these findings. I would be grateful if any further discussion of this (if not here) were copied to my address.

Regards,
Matt Shotwell
Comment 8 Matt Shotwell 2010-09-07 17:25:25 UTC
Further illustrations:

This bug is not related specific to data frames. The segfault occurs when do.call is used with a function argument (as opposed to character string), which calls NextMethod. For example:

-------------------------
Example 1.
-------------------------
> do.call(function(x) NextMethod('foo'),list())
Error in NextMethod("foo") : 
  no calling generic was found: was a method called directly?

 *** caught segfault ***
address 0x3ad992, cause 'memory not mapped'
Segmentation fault


-------------------------
Example 2.
-------------------------
> f <- function(x) NextMethod('foo')
> do.call(f,list())
Error in NextMethod("foo") : 
  no calling generic was found: was a method called directly?

 *** caught segfault ***
address 0x3abf82, cause 'memory not mapped'
Segmentation fault

However, passing the string name of the function is OK, no segfault:

-------------------------
Example 3.
-------------------------
> f <- function(x) NextMethod('foo')
> do.call("f",list())
Error in NextMethod("foo") : object not specified

Also notice that supplying a function without an argument, but still calling NextMethod does not result in segfault. This reflects the fact that segfault occurs in deparse of FORMALS.

-------------------------
Example 4.
-------------------------
> f <- function() NextMethod('foo')
> do.call(f,list())
Error in NextMethod("foo") : 
  no calling generic was found: was a method called directly?


Applying the patch I mentioned before corrects this behavior.

-Matt Shotwell
Comment 9 Martin Maechler 2010-09-09 09:02:48 UTC
(In reply to comment #8)

Thank you, Matt.  This is very convincing to me.
I'm currently testing your patch and will commit it unless I see problems in the standard checks.
Martin
Comment 10 Jackie Rosen 2014-02-16 11:42:49 UTC
(spam comment removed)