| From b4a0f533e5976cb1a79f31d6152e1d322d79b7f1 Mon Sep 17 00:00:00 2001 |
| From: Andy Lutomirski <luto@kernel.org> |
| Date: Thu, 11 Aug 2016 02:35:22 -0700 |
| Subject: [PATCH] dma-api: Teach the "DMA-from-stack" check about vmapped |
| stacks |
| |
| commit b4a0f533e5976cb1a79f31d6152e1d322d79b7f1 upstream. |
| |
| If we're using CONFIG_VMAP_STACK=y and we manage to point an sg entry |
| at the stack, then either the sg page will be in highmem or sg_virt() |
| will return the direct-map alias. In neither case will the existing |
| check_for_stack() implementation realize that it's a stack page. |
| |
| Fix it by explicitly checking for stack pages. |
| |
| This has no effect by itself. It's broken out for ease of review. |
| |
| Signed-off-by: Andy Lutomirski <luto@kernel.org> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Arnd Bergmann <arnd@arndb.de> |
| Cc: Borislav Petkov <bp@alien8.de> |
| Cc: Brian Gerst <brgerst@gmail.com> |
| Cc: Denys Vlasenko <dvlasenk@redhat.com> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Josh Poimboeuf <jpoimboe@redhat.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Link: http://lkml.kernel.org/r/448460622731312298bf19dcbacb1606e75de7a9.1470907718.git.luto@kernel.org |
| [ Minor edits. ] |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| |
| diff --git a/lib/dma-debug.c b/lib/dma-debug.c |
| index fcfa1939ac41..06f02f6aecd2 100644 |
| --- a/lib/dma-debug.c |
| +++ b/lib/dma-debug.c |
| @@ -22,6 +22,7 @@ |
| #include <linux/stacktrace.h> |
| #include <linux/dma-debug.h> |
| #include <linux/spinlock.h> |
| +#include <linux/vmalloc.h> |
| #include <linux/debugfs.h> |
| #include <linux/uaccess.h> |
| #include <linux/export.h> |
| @@ -1164,11 +1165,32 @@ static void check_unmap(struct dma_debug_entry *ref) |
| put_hash_bucket(bucket, &flags); |
| } |
| |
| -static void check_for_stack(struct device *dev, void *addr) |
| +static void check_for_stack(struct device *dev, |
| + struct page *page, size_t offset) |
| { |
| - if (object_is_on_stack(addr)) |
| - err_printk(dev, NULL, "DMA-API: device driver maps memory from " |
| - "stack [addr=%p]\n", addr); |
| + void *addr; |
| + struct vm_struct *stack_vm_area = task_stack_vm_area(current); |
| + |
| + if (!stack_vm_area) { |
| + /* Stack is direct-mapped. */ |
| + if (PageHighMem(page)) |
| + return; |
| + addr = page_address(page) + offset; |
| + if (object_is_on_stack(addr)) |
| + err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [addr=%p]\n", addr); |
| + } else { |
| + /* Stack is vmalloced. */ |
| + int i; |
| + |
| + for (i = 0; i < stack_vm_area->nr_pages; i++) { |
| + if (page != stack_vm_area->pages[i]) |
| + continue; |
| + |
| + addr = (u8 *)current->stack + i * PAGE_SIZE + offset; |
| + err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [probable addr=%p]\n", addr); |
| + break; |
| + } |
| + } |
| } |
| |
| static inline bool overlap(void *addr, unsigned long len, void *start, void *end) |
| @@ -1291,10 +1313,11 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, |
| if (map_single) |
| entry->type = dma_debug_single; |
| |
| + check_for_stack(dev, page, offset); |
| + |
| if (!PageHighMem(page)) { |
| void *addr = page_address(page) + offset; |
| |
| - check_for_stack(dev, addr); |
| check_for_illegal_area(dev, addr, size); |
| } |
| |
| @@ -1386,8 +1409,9 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, |
| entry->sg_call_ents = nents; |
| entry->sg_mapped_ents = mapped_ents; |
| |
| + check_for_stack(dev, sg_page(s), s->offset); |
| + |
| if (!PageHighMem(sg_page(s))) { |
| - check_for_stack(dev, sg_virt(s)); |
| check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); |
| } |
| |
| -- |
| 2.15.0 |
| |