-
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
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,42 @@ 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 | ||
| /* | ||
| * For large buffers, each thread that called fast_get() has a partition | ||
| * in its memory domain. Each thread must remove its own partition here | ||
| * to prevent partition leaks. | ||
| * | ||
| * Order matters: free buffer first (needs partition for cache access), | ||
| * then remove partition. | ||
| */ | ||
| if (entry->size > FAST_GET_MAX_COPY_SIZE && entry->thread) { | ||
| 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
+311
|
||
| #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.
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.
From the existing logic in the code I concluded that we remove access only for the current thread and not all of them.