Skip to content

Commit

Permalink
swiotlb: do not free decrypted pages if dynamic
Browse files Browse the repository at this point in the history
Fix these two error paths:

1. When set_memory_decrypted() fails, pages may be left fully or partially
   decrypted.

2. Decrypted pages may be freed if swiotlb_alloc_tlb() determines that the
   physical address is too high.

To fix the first issue, call set_memory_encrypted() on the allocated region
after a failed decryption attempt. If that also fails, leak the pages.

To fix the second issue, check that the TLB physical address is below the
requested limit before decrypting.

Let the caller differentiate between unsuitable physical address (=> retry
from a lower zone) and allocation failures (=> no point in retrying).

Cc: stable@vger.kernel.org
Fixes: 79636ca ("swiotlb: if swiotlb is full, fall back to a transient memory pool")
Signed-off-by: Petr Tesarik <petr.tesarik1@huawei-partners.com>
Reviewed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
  • Loading branch information
Petr Tesarik authored and Christoph Hellwig committed Nov 3, 2023
1 parent 8f6f76a commit a5e3b12
Showing 1 changed file with 16 additions and 9 deletions.
25 changes: 16 additions & 9 deletions kernel/dma/swiotlb.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,29 +558,40 @@ void __init swiotlb_exit(void)
* alloc_dma_pages() - allocate pages to be used for DMA
* @gfp: GFP flags for the allocation.
* @bytes: Size of the buffer.
* @phys_limit: Maximum allowed physical address of the buffer.
*
* Allocate pages from the buddy allocator. If successful, make the allocated
* pages decrypted that they can be used for DMA.
*
* Return: Decrypted pages, or %NULL on failure.
* Return: Decrypted pages, %NULL on allocation failure, or ERR_PTR(-EAGAIN)
* if the allocated physical address was above @phys_limit.
*/
static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes)
static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
{
unsigned int order = get_order(bytes);
struct page *page;
phys_addr_t paddr;
void *vaddr;

page = alloc_pages(gfp, order);
if (!page)
return NULL;

vaddr = page_address(page);
paddr = page_to_phys(page);
if (paddr + bytes - 1 > phys_limit) {
__free_pages(page, order);
return ERR_PTR(-EAGAIN);
}

vaddr = phys_to_virt(paddr);
if (set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))
goto error;
return page;

error:
__free_pages(page, order);
/* Intentional leak if pages cannot be encrypted again. */
if (!set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
__free_pages(page, order);
return NULL;
}

Expand Down Expand Up @@ -618,11 +629,7 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
else if (phys_limit <= DMA_BIT_MASK(32))
gfp |= __GFP_DMA32;

while ((page = alloc_dma_pages(gfp, bytes)) &&
page_to_phys(page) + bytes - 1 > phys_limit) {
/* allocated, but too high */
__free_pages(page, get_order(bytes));

while (IS_ERR(page = alloc_dma_pages(gfp, bytes, phys_limit))) {
if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
phys_limit < DMA_BIT_MASK(64) &&
!(gfp & (__GFP_DMA32 | __GFP_DMA)))
Expand Down

0 comments on commit a5e3b12

Please sign in to comment.