-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
memory leak issues #63
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for finding and opening the pr to fix the memory leak here 👍!
I'm not sure this new function is necessary (maybe I'm wrong and you can explain why you think we need it), but if we keep it, then it needs to be a macro and not a function to preserve the file and line number capture the macro does at the original place the error state is set.
include/rcutils/error_handling.h
Outdated
@@ -103,6 +103,22 @@ rcutils_set_error_state( | |||
#define RCUTILS_SET_ERROR_MSG(msg, allocator) \ | |||
rcutils_set_error_state(msg, __FILE__, __LINE__, allocator); | |||
|
|||
/// Set the formatted error msg and free its memory allocated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: no punctuation
include/rcutils/error_handling.h
Outdated
@@ -103,6 +103,22 @@ rcutils_set_error_state( | |||
#define RCUTILS_SET_ERROR_MSG(msg, allocator) \ | |||
rcutils_set_error_state(msg, __FILE__, __LINE__, allocator); | |||
|
|||
/// Set the formatted error msg and free its memory allocated | |||
/** | |||
* This function sets the error msg with the format specified and free the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implies, at least to me, that a format string is given and extra arguments are expected to generate a string, a la printf
, but that's no the case here. I think you mean something like "allocated string" or "non-literal" string or "string that should be deallocated after use".
include/rcutils/error_handling.h
Outdated
/** | ||
* This function sets the error msg with the format specified and free the | ||
* corresponding memory allocated from function rcutils_format_string_limit() | ||
* or macro rcutils_format_string drived from it to avoid memory leak. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that should be the responsibility of the person who calls rcutils_format_string()
or rcutils_format_string_limit()
.
Why do we need a function here to do that. What if someone uses snprintf
or just did malloc
and memcpy
to create a message string? Should those get functions too? (I would say no)
src/error_handling.c
Outdated
return false; | ||
} | ||
|
||
RCUTILS_SET_ERROR_MSG(formatted_msg, allocator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because you implemented this as a function rather than a maco, (and then call the macro here) the error messages that use this will always get a file and line number here, rather than where it was called from. This isn't correct imo, so this would need to be implemented as a macro instead.
src/error_handling.c
Outdated
RCUTILS_SET_ERROR_MSG(formatted_msg, allocator); | ||
|
||
allocator.deallocate(formatted_msg, allocator.state); | ||
formatted_msg = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot use nullptr
in C.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exactly, it's not in C++ and I'll revise it, thanks.
src/split.c
Outdated
@@ -115,7 +115,7 @@ rcutils_split( | |||
if (rcutils_string_array_fini(string_array) != RCUTILS_RET_OK) { | |||
error_msg = rcutils_format_string(allocator, "FATAL: %s. Leaking memory", error_msg); | |||
} | |||
RCUTILS_SET_ERROR_MSG(error_msg, allocator); | |||
rcutils_set_formatted_error(error_msg, allocator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a memory leak and should be fixed, but again, I don't think a special function is required here. Also this change will change the file and line number associated with this error state.
src/string_map.c
Outdated
@@ -354,8 +354,7 @@ rcutils_string_map_unset(rcutils_string_map_t * string_map, const char * key) | |||
size_t key_index; | |||
if (!__get_index_of_key_if_exists(string_map->impl, key, strlen(key), &key_index)) { | |||
char * msg = rcutils_format_string(allocator, "key '%s' not found", key); | |||
RCUTILS_SET_ERROR_MSG(msg, allocator) | |||
allocator.deallocate(msg, allocator.state); | |||
rcutils_set_formatted_error(msg, allocator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was ok before right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't check whether the memory allocated to msg
is successful or not, just image that msg
is NULL or a certain invalid memory address, and then to operate at that address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense.
@wjwwood agree with you to implement it as a macro. and let me explain why I add that new function: actually I want to directly modify those issues, but I found that these issues are not unique and similar; moreover, it's really prone to forget to free the memory from rcutils_format_string() and I hope a helper API may benefit this. I've ever considered to add one API to address this in format_string.h to implement current API content: anyway, I think this helper API (now should be helper macro) is not mandatory, please weight it and give your comments. thanks. |
I would be fine with fixing the memory leaks (or potential null access) without a new function. Or I would be fine with a new macro to help with this more common case, if it:
What I mean by the second point is something like this: RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"key '%s' not found", key);
return SOME_ERROR_CODE; So that it creates and uses the format string together. I would prefer that to having the user create a string, give it to the macro and let it deallocate it. I would prefer to have the same actor allocate and deallocate if possible, so either the user does both or the function does both, but not a mixture. Instead of code that used to do something like this:
|
@wjwwood I propose not to add |
Yeah, that's what I proposed too:
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
allocator,
"key '%s' not found", key);
return SOME_ERROR_CODE; The return statement is outside of the macro. |
include/rcutils/error_handling.h
Outdated
@@ -23,6 +23,8 @@ extern "C" | |||
#endif | |||
|
|||
#include <stdbool.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <stddef.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: please alphabetically order the includes
src/split.c
Outdated
} | ||
RCUTILS_SET_ERROR_MSG(error_msg, allocator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right, when will line 114's message be printed? (the case where finalizing the string array succeeds)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually keep the original without further checking :), yes, it's not reasonable and gets it tweaked, thanks.
include/rcutils/error_handling.h
Outdated
@@ -39,6 +41,8 @@ typedef struct rcutils_error_state_t | |||
rcutils_allocator_t allocator; | |||
} rcutils_error_state_t; | |||
|
|||
#define SAFE_FWRITE_TO_STDERR(msg) fwrite(msg, sizeof(char), sizeof(msg), stderr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a public header, this needs to be namespaced, e.g. RCUTILS_SAFE_FWRITE_TO_STDERR
or something like that.
include/rcutils/error_handling.h
Outdated
allocator.deallocate(output_msg, allocator.state); \ | ||
} else { \ | ||
RCUTILS_SAFE_FWRITE_TO_STDERR("Failed to allocate memory for error message"); \ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be put into a scope, i.e. within something like {}
or do {} while (0)
, so that you can use this macro more than once in a function without colliding symbol definitions, e.g. char * output_msg
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I didn't see this before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wjwwood exactly, good catch 👍
src/split.c
Outdated
} | ||
RCUTILS_SET_ERROR_MSG(error_msg, allocator); | ||
RCUTILS_SET_ERROR_MSG("unable to allocate memory for string array data", allocator); | ||
rcutils_string_array_fini(string_array); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a good idea to not check the return value. If it is not RCUTILS_RET_OK
then it will have set the error message internally, overwriting the error message set one line above.
I'd do something like this:
if (rcutils_string_array_fini(string_array) != RCUTILS_RET_OK) {
RCUTILS_SAFE_FWRITE_TO_STDERR("failed to finalize string array during error handling: ");
RCUTILS_SAFE_FWRITE_TO_STDERR(rcutils_get_error_string_safe());
RCUTILS_SAFE_FWRITE_TO_STDERR("\n");
rcutils_reset_error();
}
RCUTILS_SET_ERROR_MSG("unable to allocate memory for string array data", allocator);
include/rcutils/error_handling.h
Outdated
RCUTILS_SET_ERROR_MSG(output_msg, allocator); \ | ||
allocator.deallocate(output_msg, allocator.state); \ | ||
} else { \ | ||
RCUTILS_SAFE_FWRITE_TO_STDERR("Failed to allocate memory for error message"); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this string needs a terminating \n
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changes lgtm, but I think the style needs to be fixed up, I'll do that for you, but for future reference I'd recommend using ament_uncrustify path/to/repo --reformat
or at least running the test suite for the package before pushing for review.
I added 536ae0e since I've seen ros/ros_comm#1179 |
memory allocated from rcutils_format_string() is relatively easy easy to forget to free and this leaks system resource. A wrapper API added to make it shared and centralized Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
define the macro RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING to help format error string and fix those memory leak issues with this marco Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
If the `goto fail;` at a certain allocation failure in the middle, just to free those memory before it in `rcutils_string_array_fini()`, those after that failure is invalid
Thanks @dhood, needs to rebased anyways: |
536ae0e
to
3647147
Compare
I reapplied 536ae0e that disappeared during rebase |
@wjwwood @dhood @mikaelarguedas thanks for your contribution. BTW, @wjwwood I'll run ament_uncrustify to format code, but just wanna double confirm with you for
Do you mean "ament build --build-tests" or something else ? |
I mean with
This prevents This command basically just runs CTest, so you could do that too in the build folder of the package you're working on (assuming it is a cmake package). |
I should have just linked to our docs: https://github.com/ros2/ros2/wiki/Ament-Tutorial#run-the-tests |
Ok, finally got a Windows job to finish 😄. |
memory allocated from
rcutils_format_string()
is relatively easyeasy to forget to free and this leaks system resource. A wrapper
API added to make it shared and centralized, then fix those mem
leak issues using that API.
Signed-off-by: Ethan Gao ethan.gao@linux.intel.com