Skip to content

Memory leaks in rosidl python generated code #5

@martins-mozeiko

Description

@martins-mozeiko

I have found that Python generated code for message type conversion leaks a lot of memory. This happens inside convert_from_py functions for messages that have non-primitive types as fields. convert_from_py function is called both for every message that is sent and received (rclpy_publish and rclpy_take functions from _rclpy.c file).

For example, if message X contains "std_msgs/Header header" as field, the following fragment is generated in its X_convert_from_py function:

  {  // header
    ...
    ... omitting irrelevant code to get convert_from_py function ...
    ...
    std_msgs__msg__Header * tmp = (std_msgs__msg__Header *) convert_from_py(field);
    if (!tmp) {
      return NULL;
    }
    ros_message->header = *tmp;
    Py_DECREF(field);
  }

Nobody frees tmp pointer which is malloc'ed when calling convert_from_py function pointer (for header type).

The question is here is how to free tmp pointer?

  • it is wrong to destroy_from_py(tmp) function before Py_DECREF, because caller of X_convert_from_py (like rclpy_publish from _rclpy.c) will call destroy_ros_message on object, and that will destroy ros_message->header members. Header contains String type which means string destroy function will be called twice and this will either crash program or corrupt memory.

  • it is wrong to put just free(tmp) there, because it won't release the objects inside tmp.

One potential solution would be to introduce new convert_from_py function for each type. Something like convert_from_py_internal.

Its signature would be: void convert_from_py_internal(PyObject* obj, void* msg);
Its purpose would be to not allocate any memory, but only assign necessary fields to existing message structure.

This way code snippet above would change to this:

  {  // header
    ...
    ... omitting irrevant code to get convert_from_py_internal function ...
    ...
    convert_from_py_internal(field, &ros_message->header);
    Py_DECREF(field);
  }

The callers (like rclpy_publish function in _rclpy) would still could call convert_from_py to get newly allocated C message structure, but internal conversions would happen directly on fields of already allocated message structures - no extra allocations, and no memory leaks.

What do you think about this? I can create pull request that implements this.
Let me know if you need more information on this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions