在此代码段中发现未定义的行为

时间:2018-01-28 13:17:32

标签: python c cython undefined-behavior

我90%肯定我在这里看到的奇怪结果是由我的代码触发的某种UB,但是,我找不到它:

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"))

如果我取消注释将第一个参数分配给message的行,则args的内容会发生变化。我的猜测是编译器试图在这里优化一些东西,并且它可能与内存分配的完成方式有关,但我看不出它有任何问题。我也知道,原则上我不应该转向const char**,但是我收到了不投的警告,所以我加了它。如果我忽略了警告并且没有施放,它就不会改变任何内容。

我没有发布生成的C代码,因为它很庞大......但如果这不可避免,我会把它发布到其他地方。

1 个答案:

答案 0 :(得分:1)

result[i] = item

这将创建指向Python bytes对象item的第一个元素的指针。它不会导致item保持活跃状态​​。一旦释放item(可能在循环的下一次迭代中),指针就会立即失效。

你需要实际复制内存 - 例如:

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))

(您可能需要使用len(item)+1为null终止符提供空间)

当你完成这个记忆时,你需要释放这个记忆