How to free ruby object allocated through rb_marshal_load through C api?

44 views Asked by At

I have this code which takes bytes from stdin and then tries to unmarshal (basically deserialize) that data through the rubys c api rb_marshal_load function:

#include <ruby.h>
#include "ruby/re.h"


#define MAX_INPUT_SIZE 120




VALUE handle_error(VALUE obj1) {

    return 0;
}


VALUE dangerous_func(VALUE x)
{
    /* code that could raise an exception */
    int thing;
    printf("Calling the rb_marshal_load function: \n");
    thing = rb_marshal_load(x);
    
    return thing;
}

int main(int argc, char** argv) {
    VALUE x;

    VALUE result;

    int state = 0;
    char string[MAX_INPUT_SIZE];
    ruby_setup();
    ruby_init();

    ruby_init_loadpath();




    state = 0;

    memset(string, 0, MAX_INPUT_SIZE);

    

    read(0, string, MAX_INPUT_SIZE);

    if (string[MAX_INPUT_SIZE-2]) {
        return 0;
    }

    printf("Got this: %s\n", string);

    x = rb_str_new_cstr(string);

    
    result = rb_protect(dangerous_func, x, &state);

    printf("result %d\n", state);



    


    return 0;



}

I know that deserializing user defined input is dangerous, but I do not care about that in this context. I am trying to replicate this: https://medium.com/fuzzstation/breaking-rubys-unmarshal-with-afl-fuzz-6b5f72b581d5 with the C api instead of calling the ruby interpreter on a ruby script, because this way I think that it is a lot faster. This code works, but when I add the afl __AFL_LOOP() persistent loop to this code like so:

#include <ruby.h>
#include "ruby/re.h"


#define MAX_INPUT_SIZE 120




VALUE handle_error(VALUE obj1) {

    return 0;
}


VALUE dangerous_func(VALUE x)
{
    /* code that could raise an exception */
    int thing;
    printf("Calling the rb_marshal_load function: \n");
    thing = rb_marshal_load(x);
    printf("Regex return value: %d\n", thing);
    return thing;
}

int main(int argc, char** argv) {
    VALUE x;

    VALUE result;

    int state = 0;
    char string[MAX_INPUT_SIZE];
    ruby_setup();
    ruby_init();

    ruby_init_loadpath();

    while (__AFL_LOOP(1000)) {


    state = 0;

    memset(string, 0, MAX_INPUT_SIZE);

    

    read(0, string, MAX_INPUT_SIZE);

    if (string[MAX_INPUT_SIZE-2]) {
        return 0;
    }

    printf("Got this: %s\n", string);

    x = rb_str_new_cstr(string);

    
    result = rb_protect(dangerous_func, x, &state);

    printf("result %d\n", state);

    }

    


    return 0;



}

I get an out-of-memory error after a bit of fuzzing with afl. I think that this is because the objects allocated by rb_marshal_load never get freed. I tried to do simply:

free(thing);

but that did not work.

How do I free the allocated object properly?

Thanks in advance for the help!

0

There are 0 answers