Hello,
While writing some tests for the Rust bindings, I discovered a memory leak with Valgrind due to a completion callback not being freed. More specificly, the completion callback of `aio_opt_info()` doesn't seem to be if `aio_opt_info()` failes. In this case, `aio_opt_info()` was called in the connected state, so it should indeed fail, but it should perhaps also call the free function associated with the callback.
According to libnbd(3):
Callback lifetimes
You can associate an optional free function with callbacks. Libnbd will call this function when the callback will not be called again by libnbd, including in the case where the API fails.
After studying the code in lib/opt.c, I think that the situation with synchronous callbacks like `nbd_opt_list()` is even worse. Here the list callback will not be freed if the internal call to `nbd_unlocked_aio_opt_list()` failes, but it will be freed if the completion callback got an error which is returned by `nbd_opt_list()`.
/* Issue NBD_OPT_LIST and wait for the reply. */Here the list callback has not been freed.
int
nbd_unlocked_opt_list (struct nbd_handle *h, nbd_list_callback *list)
{
struct list_helper s = { .list = *list };
nbd_list_callback l = { .callback = list_visitor, .user_data = &s };
nbd_completion_callback c = { .callback = list_complete, .user_data = &s };
if (nbd_unlocked_aio_opt_list (h, &l, &c) == -1)
return -1;But here I think that the callback has been freed.
SET_CALLBACK_TO_NULL (*list);
if (wait_fo_option (h) == -1)
return -1;
if (s.err) {
set_error (s.err, "server replied with error to list request");
return -1;
}
return s.count;
}
The problem is that if `nbd_opt_list()` returns an error, I as a user cannot know wether I should free the data associated with the list callback myself or if that has been done already.
Maybe I am just misunderstanding the code or the API, but if not then I wonder how I should know when I need to free the user data associated with a callback myself and when that is done by libnbd?
Best regards,
Tage