free string after rb_raise

132 Views Asked by At

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 ?

1

There are 1 best solutions below

2
binzo On BEST ANSWER

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.