Reputation: 9464
I'm 90% sure the bizarre result I'm seeing here is some kind of UB triggered by my code, however, I cannot find it:
cdef char** to_cstring_array(strings):
cdef char** result = <char**>malloc(len(strings) * sizeof(char*))
cdef bytes item
nelements = len(strings)
i = 0
while i < nelements:
s = str(strings[i]).encode("utf-8")
item = s
result[i] = item
i += 1
return result
cpdef object pykubectl_get(object items, object options=None):
cdef size_t nargs = len(items);
cdef bytes message
cdef char** args = to_cstring_array(items);
# message = args[0]
# print("1 items encoded: {}".format(message))
json_opts = json.dumps(options or {}).encode("utf-8")
cdef char* opts = json_opts
print("items: {}".format(items))
message = args[0]
print("2 items encoded: {}".format(message))
cdef ResourceGet_return result = kubectl_get(
opts,
len(json_opts),
<const char**>args,
nargs
)
free(args)
if result.r0.n == 0:
message = result.r1.p
raise Exception("kubectl failed: '{}'".format(message.decode("utf-8")))
message = result.r0.p
return json.loads(message.decode("utf-8"))
If I uncomment the lines assigning first argument to message
, the contents of args
changes. My guess is that compiler is trying to optimize something here, and that it could be related to how memory allocation is done, but I cannot see any problems with it. I also know that, in principle, I shouldn't need to cast to const char**
, but I'm getting a warning for not casting, so I added it. It changes nothing if I ignore the warning and don't cast.
I'm not posting the generated C code because it's huge... but if this will prove inevitable, I'll post it elsewhere.
Upvotes: 0
Views: 139
Reputation: 30930
result[i] = item
This creates a pointer to the first element of your Python bytes
object item
. It does not cause item
to be kept alive. As soon as item
is freed (probably on the next iteration of the loop) then the pointer is immediately invalid.
You need to actually copy the memory - something like:
cdef char* item_as_charp
# ....
# then in the loop
item_as_charp = <char*>malloc(sizeof(char)*len(item))
memcpy(item_as_charp,item, len(item))
(You possibly need to use len(item)+1
to give room for the null terminator)
You need to free this memory once you're done with it, of course
Upvotes: 2