Search code examples
crubyruby-c-extension

free string after rb_raise


I'm writing a Ruby C extension, and need to call rb_raise() and pass it a char *. I also need to free that given char *. My current behavior won't free anything, and another approach I've tried lead me into undefined behavior. What is the correct way to do so ?

Here's a minimal example of my issue:

static VALUE
rb_some_function(VALUE self)
{
    char *foo;
    
    foo = malloc(100);
    strcpy(foo, "message\0");
    
    rb_raise(rb_eRuntimeError, "%s\n", foo);
    free(foo); // This won't work since rb_raise exits the function using longjmp()
}

My idea would be to free before rb_raise. I've tried this with a tiny program, to check if it would lead to any leak:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void stuff(int i)
{
    char *str;

    str = malloc(100);
    strcpy(str, "message\0");
    free(str); // this feel very wrong to free and then use
    printf("\r%s %i", str, i);
}

int main(int argc, char const *argv[])
{
    // Time for instrument to attach.
    sleep(5);
    for (int i = 0; i < 1e6; i++) stuff(i);
    printf("\n");
    return 0;
}

It seems that this is safe from leak. But freeing before usage makes me think this can lead to troubles I don't known about. Like an undefined behavior as suggested in this answer.

Do you know a safe way rb_raise and free the string that contained the error message ?


Solution

  • How about use Ruby String object like this.

    static VALUE
    rb_some_function(VALUE self)
    {
        volatile VALUE str;
        char *foo;
        
        foo = malloc(100);
        strcpy(foo, "message\0");
        
        str = rb_str_new2(foo);
        free(foo); 
    
        rb_raise(rb_eRuntimeError, "%s\n", StringValuePtr(str));
    }
    

    If you use a Ruby String object, it will be freed by ruby's garbage collector.