Skip to content
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

Fix memory leak in print_string #27

Merged
merged 1 commit into from
Jan 13, 2025
Merged

Conversation

Ebulus7899
Copy link

The cstr pointer is assigned memory inside the lyd_print_mem function however the print_string function returns without explicitly freeing this memory. Update the function to call char_ptr_to_string, free the cstr, and then pass result to Ok

@Ebulus7899
Copy link
Author

Ebulus7899 commented Jan 13, 2025

In our application we noticed a pretty severe memory leak. Each time we ran our yang validation code the memory usage would increase (26GB over 3 days).

This fixes the issue however it seems to have failed some compilation step in the tests?

@rwestphal
Copy link
Member

@Ebulus7899 Wow. Good catch!

It seems most usages of char_ptr_to_string() have the same problem, except when converting the return value of ffi::lyd_value_get_canonical(), which isn't heap-allocated.

Would you mind fixing the other cases as well? I was thinking we could add a free parameter to char_ptr_to_string() to handle memory deallocation when set, which would simplify the call sites.

I'll update the CI to use the stable toolchain and get rid of those instabilities.

@Ebulus7899
Copy link
Author

No worries. I changed the code so the free is in the char_ptr_to_string function instead

@rwestphal
Copy link
Member

I think we need something like this:

pub(crate) fn char_ptr_to_string(c_str: *const c_char, free: bool) -> String {
    let string = unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() };
    if free {
        unsafe { free(c_str as *mut std::ffi::c_void) };
    }
    string
}

We do not always want to free the string passed as input, as it might not be heap-allocated in some cases (e.g. string returned by ffi::lyd_value_get_canonical()).

@Ebulus7899
Copy link
Author

Just making sure.

For the function char_ptr_to_opt_string. All current uses are not using the heap so should be set to false for now?

src/data.rs Outdated
@@ -1259,7 +1259,7 @@ impl<'a> DataNodeRef<'a> {
panic!("Failed to generate path of the data node");
}

char_ptr_to_string(buf.as_ptr())
char_ptr_to_string(buf.as_ptr(), true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be false because we're passing a local buffer to lyd_path(). This is done for performance reasons.

src/schema.rs Outdated
@@ -576,7 +576,7 @@ impl<'a> SchemaNode<'a> {
panic!("Failed to generate path of the schema node");
}

char_ptr_to_string(buf.as_ptr())
char_ptr_to_string(buf.as_ptr(), true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

src/schema.rs Outdated
@@ -1247,7 +1247,7 @@ impl SchemaLeafType<'_> {
/// Returns the typedef name if it exists.
pub fn typedef_name(&self) -> Option<String> {
let typedef = unsafe { (*self.raw).name };
char_ptr_to_opt_string(typedef)
char_ptr_to_opt_string(typedef, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false here too

src/schema.rs Outdated
@@ -1292,7 +1292,7 @@ impl<'a> SchemaExtInstance<'a> {
/// Returns the optional extension's argument.
pub fn argument(&self) -> Option<String> {
let argument = unsafe { (*self.raw).argument };
char_ptr_to_opt_string(argument)
char_ptr_to_opt_string(argument, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false here too

src/error.rs Outdated
let apptag = unsafe { char_ptr_to_opt_string((*error).apptag) };
let msg = unsafe { char_ptr_to_opt_string((*error).msg, true) };
let path = unsafe { char_ptr_to_opt_string((*error).data_path, true) };
let apptag = unsafe { char_ptr_to_opt_string((*error).apptag, true) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false for all three above, libyang will handle this internally

The cstr pointer is assigned memory inside the lyd_print_mem function
however the print_string function returns without explicitly freeing
this memory.

Update the function to call char_ptr_to_string with a free boolean
that when true will free the memory. Also updated the char_ptr_to_opt_string
function to also take the free boolean as we do not want to free the memory for
the lyd_value_get_canonical case
Copy link
Member

@rwestphal rwestphal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ebulus7899 Thank you a lot! I'll publish a point release containing this fix now.

@rwestphal rwestphal merged commit 980affb into holo-routing:master Jan 13, 2025
3 checks passed
@Ebulus7899
Copy link
Author

Thank you very much for your patience

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants