-
Notifications
You must be signed in to change notification settings - Fork 350
fast-get: fix partition leak for multi-thread usage #10606
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -277,40 +277,34 @@ void fast_put(struct k_heap *heap, const void *sram_ptr) | |
| LOG_ERR("Put called to unknown address %p", sram_ptr); | ||
| goto out; | ||
| } | ||
|
|
||
| entry->refcount--; | ||
|
|
||
| if (!entry->refcount) { | ||
| #if CONFIG_USERSPACE | ||
| /* For large buffers, we need to: | ||
| * 1. Free the heap buffer FIRST (while partition still grants us access) | ||
| * 2. Then remove the partition (to prevent partition leaks) | ||
| * This order is critical - we must free while we still have access. | ||
| */ | ||
| if (entry->size > FAST_GET_MAX_COPY_SIZE) { | ||
| struct k_mem_partition part; | ||
| struct k_mem_domain *domain = entry->thread->mem_domain_info.mem_domain; | ||
| void *addr = entry->sram_ptr; | ||
|
|
||
| sof_heap_free(heap, addr); | ||
|
|
||
| part.start = (uintptr_t)addr; | ||
| part.size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE); | ||
| part.attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB; | ||
|
|
||
| int err = k_mem_domain_remove_partition(domain, &part); | ||
|
|
||
| if (err < 0) | ||
| LOG_WRN("partition removal failed err=%d", err); | ||
| LOG_DBG("freeing buffer %p", entry->sram_ptr); | ||
| sof_heap_free(heap, entry->sram_ptr); | ||
| } | ||
|
|
||
| } else | ||
| #endif /* CONFIG_USERSPACE */ | ||
| { | ||
| /* Small buffers have no partitions, just free */ | ||
| sof_heap_free(heap, entry->sram_ptr); | ||
| } | ||
| #if CONFIG_USERSPACE | ||
| if (entry->size > FAST_GET_MAX_COPY_SIZE) { | ||
| struct k_mem_partition part = { | ||
| .start = (uintptr_t)entry->sram_ptr, | ||
| .size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE), | ||
| .attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB, | ||
| }; | ||
| struct k_mem_domain *domain = k_current_get()->mem_domain_info.mem_domain; | ||
|
|
||
| LOG_DBG("removing partition %p size %#zx from thread %p", | ||
| (void *)part.start, part.size, k_current_get()); | ||
| int err = k_mem_domain_remove_partition(domain, &part); | ||
|
|
||
| if (err) | ||
| LOG_WRN("partition removal failed: %d", err); | ||
| } | ||
|
Comment on lines
+288
to
+303
|
||
| #endif | ||
|
|
||
| if (!entry->refcount) | ||
| memset(entry, 0, sizeof(*entry)); | ||
| } | ||
| out: | ||
| LOG_DBG("put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0, | ||
| entry ? entry->size : 0, entry ? entry->refcount : 0); | ||
|
|
||
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.
k_mem_domain_remove_partition() operates on a memory domain, which can be shared by multiple threads. Removing the partition from k_current_get()->mem_domain_info.mem_domain can therefore revoke access for other threads in the same domain that still hold references (entry->refcount > 0). To avoid use-after-revoke faults, this code should track granted partitions per-domain (and only remove when the last user in that domain releases), or otherwise enforce/document that each fast_get() user has a unique mem_domain.