Skip to content

Conversation

@tmleman
Copy link
Contributor

@tmleman tmleman commented Jun 10, 2025

This pull request resolves memory leak issues in the buffer component release processes across various audio and IPC components. Each commit ensures the complete deallocation of buffer structures by adding necessary rfree calls following buffer_free. This prevents potential memory leaks and enhances the stability and reliability of the system.

Copilot AI review requested due to automatic review settings June 10, 2025 14:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

tmleman added 5 commits June 10, 2025 17:50
This patch addresses a memory leak issue in the `copier_dai_free`
function within the `copier_dai.c` file. The leak occurred because the
`multi_endpoint_buffer` was freed using `buffer_free`, which only
releases the memory allocated for its members and not the `comp_buffer`
structure itself. This patch adds a call to `rfree` for
`multi_endpoint_buffer` and sets it to NULL after freeing, ensuring that
the entire buffer structure is properly released.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
This patch resolves a memory leak issue in the `ipc_buffer_new` function
within the `helper.c` file of the IPC3. When memory allocation for
`ipc_comp_dev` fails, the `buffer` object is freed using `buffer_free`,
which releases only the memory allocated for its members and not the
`comp_buffer` structure itself. This patch adds an `rfree` call to
ensure the complete release of the `buffer` structure, preventing
potential memory leaks.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
This patch addresses memory leak issues in the `chain_dma.c` file,
specifically in the `chain_release` and `chain_task_init` functions. The
leak occurs due to incomplete freeing of the `dma_buffer` using
`buffer_free`, which only releases memory allocated for its members and
not the `comp_buffer` structure itself. This patch adds calls to `rfree`
after `buffer_free` to ensure the complete release of the `dma_buffer`
structure and sets the pointer to NULL post-freeing to prevent dangling
references.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
This patch fixes memory leak issues in the `dai_common_reset` and
`host_common_reset` functions within the legacy audio components
(`dai-legacy.c` and `host-legacy.c`). The leak was caused by using
`buffer_free` to release the `dma_buffer`, which only frees the memory
allocated for its members but does not release the `comp_buffer`
structure itself. This patch adds `rfree` calls after `buffer_free` to
ensure complete deallocation of the `dma_buffer` structure, before
setting the pointer to NULL.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
This patch enhances the memory management in the `dai-zephyr.c` and
`host-zephyr.c` files by ensuring full deallocation of the `dma_buffer`
in error handling and reset functions. It adds `rfree` calls following
`buffer_free` to completely release the `dma_buffer` structure before
setting the pointer to NULL. This modification prevents memory leaks by
guaranteeing that both the internal buffer memory and the `comp_buffer`
structure are freed.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
Copy link
Collaborator

@lyakh lyakh left a comment

Choose a reason for hiding this comment

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

looks wrong to me, instead of fixing this seems to add a double-free bug


if (cd->dma_buffer) {
buffer_free(cd->dma_buffer);
rfree(cd->dma_buffer);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this looks wrong to me. Looking at buffer_free()

static inline void buffer_free(struct comp_buffer *buffer)
{
audio_buffer_free(&buffer->audio_buffer);
}

and then in audio_buffer_free()
rfree(buffer);
and taking into account the definition of struct comp_buffer
struct comp_buffer {
/* data buffer */
struct sof_audio_buffer audio_buffer;
we come to several conclusions: (1) calling rfree(buffer) in audio_buffer_free() isn't a good idea, because audio_buffer_free() isn't called on a whole object, that can be freed, but on a member of an object. (2) that does however happen to work because that member is currently the first in the object, so its address is equal to the address of the object, so it ends up freeing the object, (3) these fixes aren't correct because the buffer is indeed freed by buffer_free() because of the above. So, this PR isn't fixing a problem but in fact adding a double free bug, instead audio_buffer_free() and / or buffer_free() should be fixed to clarify which one frees the underlying buffer memory.

Copy link
Contributor

@marcinszkudlinski marcinszkudlinski Jun 11, 2025

Choose a reason for hiding this comment

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

isn't called on a whole object, that can be freed, but on a member of an object.

not really. The main object is struct sof_audio_buffer, a base class for all buffers (currently comp_buffer and ring buffer)
in c++ it would be simple and clear

class comp_buffer : public sof_audio_buffer

class ring_buffer : public sof_audio_buffer

also freeing would be obvious by using virtual destructors

but we're using an obsolete C, so we need to put sof_audio_buffer as a member :(

this part:
static inline void buffer_free(struct comp_buffer *buffer) { audio_buffer_free(&buffer->audio_buffer); }

is just a wrapper. Although sof_audio_buffer should be used (what would make possible using comp_buffer and ring_buffer - and other buffers - directly in a pipeline), yet some code is still using comp_buffer as a main structure (with unfortunate double buffering when ring_buffer is needed)

Copy link
Contributor

@marcinszkudlinski marcinszkudlinski left a comment

Choose a reason for hiding this comment

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

@tmleman
the change is not needed at all

see:

static inline void buffer_free(struct comp_buffer *buffer)  
{  
	audio_buffer_free(&buffer->audio_buffer);  
} 


void audio_buffer_free(struct sof_audio_buffer *buffer)
{
	if (!buffer)
		return;

	CORE_CHECK_STRUCT(buffer);
#if CONFIG_PIPELINE_2_0
	audio_buffer_free(buffer->secondary_buffer_sink);
	audio_buffer_free(buffer->secondary_buffer_source);
#endif /* CONFIG_PIPELINE_2_0 */
	if (buffer->ops->free)
		buffer->ops->free(buffer);
	rfree(buffer);
}

as you see, the buffer itself is freed

BUT
this requires that struct sof_audio_buffer is the very first member of all structures that "inherite" from it.
This is enforced by exception - rfree will fail if freed pointer is not the same that was allocated before.

HOWEVER - it may be changed to more secure code:

switch(buffer->buffer_type) {
case BUFFER_TYPE_LEGACY_BUFFER: rfree(container_of(audio_buffer, struct comp_buffer, audio_buffer);
case BUFFER_TYPE_RING_BUFFER: rfree(container_of(ring_buffer, struct ring_buffer, audio_buffer);
}

(above may have incorrect syntax, just a quick demo code)

I'm not convinced it is worth, will cause more code doing the very very same operation

Maybe better a compilation time assert, enforcing sof_audio_buffer to be a first member?

@lyakh ?

@marcinszkudlinski
Copy link
Contributor

OR we can make ops->free obligatory and put the actual call to rfree there

@tmleman
Copy link
Contributor Author

tmleman commented Jun 11, 2025

the change is not needed at all

Yes, that's correct. The tests also clearly show this. I reached the wrong conclusions while analyzing the code.

@tmleman tmleman closed this Jun 11, 2025
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.

4 participants