Is it safe to delete the buffer when returning it to ctypes?

Hi all,
I am using ctypes to call my dll which is written in Odin. Odin is a C like language with manual memory management. This is a part of my odin code.

@(export) control_gettext :: proc "std" (ctl: ^Control) -> wstring
{
	context = podius_context
	wbuffer := control_gettext_internal(ctl) // Allocating memory in heap
	defer delete(wbuffer) // This is the magic
	return &wbuffer[0] 
}

So, as you can see, I am returning the buffer’s pointer which is in heap. At first glance, we will think that this is dangerous. But ctypes will read the contents of this buffer and after that the buffer get deleted. In Odin, we can use tracking allocators. The context is an implicit variable which contains a tracking allocator. It has a map for each and every allocations. It print out a report on demand if there is any leak or bad freeing. But surprisingly, this worked nicely without any error. So I posted this code in Odin’s discord server and a few guys discouraged me from doing this. In Odin, defer executes right after return statement and before the function exits.
This is my python part

control_gettext = dll[7]
control_gettext.argtypes = [voidp]
control_gettext.restype = c_wchar_p 

@property
    def text(self):
        self._text = control_gettext(self._ptr)
        return self._text

So this is my question. Is it safe to do this with ctypes ? Does ctypes copies the contents of the buffer before odin function exits ? Please guide me. This thing is baffling me. Thanks in advance.

Deferred code will be executed when execution leaves the scope, which, in this case, is when the function exits. Control then returns to the caller.

This means that your function is returning a pointer to a block of memory after it has been freed.

You’ve just been lucky that the memory hasn’t been reused yet.

1 Like

@MRAB
Yeah, I got it. At first flance, I thought it was like black magic. String is printed in console, and tracking allocator shows everything is clean. So I thought, Yay! it worked. But it’s not.

I would suggest using something like valgrind to do proper cleanup checks, but apparently odin doesn’t easily support it.

1 Like

@MegaIng
Odin had a tracking allocator. So, if we use that tracking allocator everywhere to allocate memory, it can print an on demand report of any leaks or bad freeing.

Edit : I changed my python script like this.

class OdinString(ctypes.c_wchar_p):
    pass

control_gettext = dll[7]
control_gettext.argtypes = [ctypes.void_p]
control_gettext.restype = OdinString

free_memory = dll[8]
free_memory.argtypes = [ctypes.c_void_p]

@property
 def text(self):
        odinstr = control_gettext(self._ptr)
        self._text = odinstr.value
        free_memory(odinstr)
        return self._text

Now it is working perfectly, and it’s safe.

But apparently not of bad access by non-odin parts, which valgrind should be able to catch.