From: Adam Bozanich Date: 2007-05-03T15:30:14+09:00 Subject: ruby packer prototype: core dumps ( design review request ) ------=_Part_23176_4242455.1178173811625 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline Hello All. I'm developing a ruby-packer type of thing ( i'll give more details below ), and have run into a problem where I keep getting core dumps. I guess I'll lay out the problem area first. I've isolated the coredump issue to this spot: rb_funcall( rb_cModule , rb_intern("require") , 1 , rb_str_new2("mu/loader") ); VALUE tmp = rb_funcall( config , rb_intern("to_s") , 0 ); If I switch the order of those calls, the process runs fine ( there's other issues, which I'm trying to debug. could be related ). Stepping through the debugger, the crash fails in rb_rescue2() in the JUMP_TAG() macro when prot_tag is dereferenced ( it is null ). This is proving to be a nasty bug to pin down, and I'm just wondering if anybody has any comments/suggestions about the situation. Let me give you an outline of what I'm trying to do: We have a protocol fuzzer built with ruby. One of the features of our product is that we give the user the option to download a single executable P.O.C. to re-produce a crash. Since we're using ( and loving ) ruby, we need a scheme that will allow us to both give the user that single executable _and_ protect our I.P. The first iteration of this project is to simply embed the scripts into the resulting executable, and override Kernel.require. We call the various Init_* functions for extensions, and eval() the strings when they are needed. Eventually we're gonna try to use YARV instructions ( we've even looked into adding an opcode representation other than SimpledataFormat ). The sample code is below (no errors -- runs fine). I'm wondering if the design is just too naive. Does anybody see any problems with our general approach? I'm sorry if this whole post is a bit vague, i just really don't know where to start with this one. If there are any requests for more information, i'd be happy to answer them. Thanks, -Adam #include struct test_resource { char path[1024]; char loaded; char * src; } test_resources[] = { /* ble.rb */ { "ble" , 0 , "require \"foo\"\n" "puts Foo.new.inspect\n" } , /* foo.rb */ { "foo" , 0 , "require \"bar\"\n" "class Foo < Bar\n" " hidden_method :baz\n" "end\n" } , /* bar.rb */ { "bar" , 0 , "class Bar\n" " def self.hidden_method(*args)\n" " args.each do |meth|\n" " define_method(meth) do \n" " puts meth\n" " end\n" " end\n" " end\n" "end" } , { } }; static VALUE resource_require_path( VALUE str ) { char * path ; VALUE binding, ret ; struct test_resource * res; /* Find the source code */ path = StringValueCStr(str); for( res = &test_resources[0] ; res->path[0] ; res++ ) if(!strcmp(res->path,path)) break; if(!res->path[0]) rb_bug("can't find path %s",res->path); if( res->loaded ) return Qtrue; /* Evaluate the string */ binding = rb_const_get(rb_mKernel,rb_intern("TOPLEVEL_BINDING")); VALUE argv[4] = { rb_str_new2( res->src ) , binding , rb_str_new2( res->path ) , INT2FIX(1) }; ret = rb_funcall2( rb_mKernel , rb_intern("eval") , 4 , argv ); res->loaded = 1; return ret; } static VALUE hooked_require( VALUE self , VALUE str ) { return resource_require_path( str ); } /* set the require hook */ static void hook_require() { rb_define_global_function( "require" , hooked_require , 1 ); } int main( int argc , char * argv[] ) { ruby_init(); ruby_script("Test"); ruby_init_loadpath(); hook_require(); resource_require_path( rb_str_new2("ble") ); return 0; } ------=_Part_23176_4242455.1178173811625 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline Hello All.

I'm developing a ruby-packer type of thing ( i'll give more details below ), and have run into a problem where I keep getting core dumps. 

I guess I'll lay out the problem area first.

I've isolated the coredump issue to this spot:

    rb_funcall( rb_cModule  ,
                rb_intern("require") , 1 , rb_str_new2("mu/loader") );
    VALUE tmp = rb_funcall( config , rb_intern("to_s") , 0 );

If I switch the order of those calls, the process runs fine ( there's other issues, which I'm trying to debug.  could be related ).

Stepping through the debugger, the crash fails in rb_rescue2() in the JUMP_TAG() macro when prot_tag is dereferenced ( it is null ).

This is proving to be a nasty bug to pin down, and I'm just wondering if anybody has any comments/suggestions about the situation.

Let me give you an outline of what I'm trying to do:

We have a protocol fuzzer built with ruby. One of the features of our product is that we give the user the option to download a single executable P.O.C. to re-produce a crash.  Since we're using ( and loving ) ruby, we need a scheme that will allow us to both give the user that single executable _and_ protect our I.P.  The first iteration of this project is to simply embed the scripts into the resulting executable, and override Kernel.require.  We call the various Init_* functions for extensions, and eval() the strings when they are needed.  Eventually we're gonna try to use YARV instructions ( we've even looked into adding an opcode representation other than SimpledataFormat ).

The sample code is below (no errors -- runs fine).  I'm wondering if the design is just too naive.  Does anybody see any problems with our general approach?  I'm sorry if this whole post is a bit vague, i just really don't know where to start with this one.  If there are any requests for more information, i'd be happy to answer them.

Thanks,
-Adam

#include<ruby.h>

struct test_resource {
    char path[1024];
    char loaded;
    char * src;
} test_resources[] = {

/* ble.rb */
    { "ble" , 0 ,
"require \"foo\"\n"
"puts Foo.new.inspect\n"
} ,

/* foo.rb */
    { "foo" , 0 ,
"require \"bar\"\n"
"class Foo < Bar\n"
"  hidden_method :baz\n"
"end\n"
} ,

/* bar.rb */
    { "bar" , 0 ,
"class Bar\n"
"  def self.hidden_method(*args)\n"
"    args.each do |meth|\n"
"      define_method(meth) do \n"
"        puts meth\n"
"      end\n"
"    end\n"
"  end\n"
"end"
} ,
    { }
};

static VALUE resource_require_path( VALUE str ) {
    char * path ;
    VALUE binding, ret ;
    struct test_resource * res;

    /* Find the source code */
    path    = StringValueCStr(str);
    for( res = &test_resources[0] ; res->path[0] ; res++ )
        if(!strcmp(res->path,path))
            break;
    if(!res->path[0])
        rb_bug("can't find path %s",res->path);
    if( res->loaded ) return Qtrue;

    /* Evaluate the string */
    binding = rb_const_get(rb_mKernel,rb_intern("TOPLEVEL_BINDING"));
    VALUE argv[4] = {
        rb_str_new2( res->src ) ,
        binding ,
        rb_str_new2( res->path ) ,
        INT2FIX(1)
    };

    ret = rb_funcall2( rb_mKernel , rb_intern("eval") , 4 , argv );
    res->loaded = 1;
    return ret;
}

static VALUE hooked_require( VALUE self , VALUE str ) {
    return resource_require_path( str );
}

/* set the require hook */
static void hook_require() {
    rb_define_global_function( "require" , hooked_require , 1 );
}

int main( int argc , char * argv[] ) {
    ruby_init();
    ruby_script("Test");
    ruby_init_loadpath();
    hook_require();
    resource_require_path( rb_str_new2("ble") );
    return 0;
}
------=_Part_23176_4242455.1178173811625--