aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei F <luxneb@gmail.com>2012-12-19 21:31:19 +0100
committercodeworkx <codeworkx@cyanogenmod.org>2012-12-20 18:47:38 +0100
commit9c1d0f487d28417858778d094f2eb98eb47ea2f7 (patch)
treeca96810f25466e2686ac44b5d71892e63c18592c
parentc3e546ee57369dc2dd340c07868df83380428de0 (diff)
downloadkernel_samsung_smdk4412-9c1d0f487d28417858778d094f2eb98eb47ea2f7.zip
kernel_samsung_smdk4412-9c1d0f487d28417858778d094f2eb98eb47ea2f7.tar.gz
kernel_samsung_smdk4412-9c1d0f487d28417858778d094f2eb98eb47ea2f7.tar.bz2
exynos-mem: Fix major security hole
This fixes the exynos-mem device security hole. The driver allowed any user to access all of the device's lowmem through the provided mmap functionality. We create a small little framework collecting the actual CMA memory blocks that exist on the device; they are the root cause of the existence of this device driver. We white-list only the CMA memory spaces as parameters to the mmap function and deny access to any other memory space requests. We furthermore just allow access to the "s3c-fimc" memory block as this is seemingly the only space which upon access denial actually breaks functionality. Change-Id: I286be4a2546621c66d214c79f480822ecd8138db
-rw-r--r--arch/arm/mach-exynos/mach-midas.c4
-rw-r--r--arch/arm/plat-s5p/reserve_mem.c14
-rw-r--r--drivers/char/exynos_mem.c73
-rw-r--r--include/linux/exynos_mem.h9
4 files changed, 99 insertions, 1 deletions
diff --git a/arch/arm/mach-exynos/mach-midas.c b/arch/arm/mach-exynos/mach-midas.c
index fc6c224..ddc2d14 100644
--- a/arch/arm/mach-exynos/mach-midas.c
+++ b/arch/arm/mach-exynos/mach-midas.c
@@ -58,6 +58,7 @@
#ifdef CONFIG_DMA_CMA
#include <linux/dma-contiguous.h>
+#include <linux/exynos_mem.h>
#endif
#include <asm/mach/arch.h>
@@ -3423,11 +3424,14 @@ static void __init exynos4_reserve(void)
CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC1 * SZ_1K, 0x65800000, 0);
if (ret != 0)
panic("alloc failed for FIMC1\n");
+ cma_region_descriptor_add(s3c_device_fimc1.name, 0x65800000,
+ CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC1 * SZ_1K);
#endif
#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_M0)
ret = dma_declare_contiguous(&s5p_device_mfc.dev,
0x02800000, 0x5C800000, 0);
+ cma_region_descriptor_add(s5p_device_mfc.name, 0x5C800000, 0x02800000);
#endif
if (ret != 0)
printk(KERN_ERR "%s Fail\n", __func__);
diff --git a/arch/arm/plat-s5p/reserve_mem.c b/arch/arm/plat-s5p/reserve_mem.c
index 557938a..32ac973 100644
--- a/arch/arm/plat-s5p/reserve_mem.c
+++ b/arch/arm/plat-s5p/reserve_mem.c
@@ -22,6 +22,7 @@
#ifdef CONFIG_CMA
#include <linux/cma.h>
+#include <linux/exynos_mem.h>
void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
struct cma_region *regions_secure,
size_t align_secure, const char *map)
@@ -69,6 +70,9 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
pr_debug("S5P/CMA: "
"Reserved 0x%08x/0x%08x for '%s'\n",
reg->start, reg->size, reg->name);
+
+ cma_region_descriptor_add(reg->name, reg->start, reg->size);
+
paddr = reg->start;
} else {
paddr = memblock_find_in_range(0,
@@ -88,6 +92,8 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
pr_info("S5P/CMA: Reserved 0x%08x/0x%08x for '%s'\n",
reg->start, reg->size, reg->name);
+
+ cma_region_descriptor_add(reg->name, reg->start, reg->size);
} else {
pr_err("S5P/CMA: No free space in memory for '%s'\n",
reg->name);
@@ -155,6 +161,9 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
}
if (paddr_last) {
+ pr_info("S5P/CMA: "
+ "Reserved 0x%08x/0x%08x for 'secure_region'\n",
+ paddr_last, size_secure);
#ifndef CONFIG_DMA_CMA
while (memblock_reserve(paddr_last, size_secure))
paddr_last -= align_secure;
@@ -165,7 +174,6 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
paddr_last -= align_secure;
}
#endif
-
do {
#ifndef CONFIG_DMA_CMA
reg->start = paddr_last;
@@ -191,6 +199,7 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
if (memblock_reserve(reg->start,
reg->size))
panic("memblock\n");
+
#endif
} else {
reg->start = paddr_last;
@@ -201,6 +210,9 @@ void __init s5p_cma_region_reserve(struct cma_region *regions_normal,
pr_info("S5P/CMA: "
"Reserved 0x%08x/0x%08x for '%s'\n",
reg->start, reg->size, reg->name);
+
+ cma_region_descriptor_add(reg->name, reg->start, reg->size);
+
if (cma_early_region_register(reg)) {
memblock_free(reg->start, reg->size);
pr_err("S5P/CMA: "
diff --git a/drivers/char/exynos_mem.c b/drivers/char/exynos_mem.c
index 85c7a29..707044a 100644
--- a/drivers/char/exynos_mem.c
+++ b/drivers/char/exynos_mem.c
@@ -241,14 +241,47 @@ static struct vm_operations_struct exynos_mem_ops = {
.close = exynos_mem_mmap_close,
};
+static struct simple_cma_descriptor cmad_container[CMA_REGION_COUNT];
+static int cmad_container_stored = 0;
+
+void cma_region_descriptor_add(const char *name, int start, int size)
+{
+ int i;
+
+ pr_info("[%s] adding [%s] (0x%08x)-(0x%08x)\n",
+ __func__, name, start, size);
+
+ if(cmad_container_stored == CMA_REGION_COUNT - 1)
+ return;
+
+ i = cmad_container_stored;
+
+ cmad_container[i].name = name;
+ cmad_container[i].start = start;
+ cmad_container[i].size = size;
+
+ cmad_container_stored++;
+
+}
+
int exynos_mem_mmap(struct file *filp, struct vm_area_struct *vma)
{
+
+/* Devices not having DMA CMA acess shouldn't be using this in any case at all */
+#ifndef CONFIG_DMA_CMA
+ return -EINVAL;
+#endif
+
struct exynos_mem *mem = (struct exynos_mem *)filp->private_data;
bool cacheable = mem->cacheable;
dma_addr_t start = 0;
u32 pfn = 0;
u32 size = vma->vm_end - vma->vm_start;
+ int i, allowed;
+ struct simple_cma_descriptor *b;
+ allowed = false;
+
if (vma->vm_pgoff) {
start = vma->vm_pgoff << PAGE_SHIFT;
pfn = vma->vm_pgoff;
@@ -257,6 +290,46 @@ int exynos_mem_mmap(struct file *filp, struct vm_area_struct *vma)
pfn = mem->phybase;
}
+ pr_info("[%s] requesting access to (0x%08x)-(0x%08x)\n",
+ __func__, start, (start + size));
+
+ b = (struct simple_cma_descriptor*)&cmad_container;
+
+ /* Go over all of the defined CMA blocks */
+ for(i = 0; i < cmad_container_stored; i++) {
+
+ pr_info("[%s] Checking space paddr(0x%08x)-(0x%08x) from '%s'\n",
+ __func__, b->start, (b->start + b->size), b->name);
+
+ /* Check if the requested space is within this current CMA block */
+ if(start >= b->start && (start + size) <= (b->start + b->size)){
+
+ /* Further only conditionally whitelist spaces that we know
+ * break device functionality if we don't allow access.
+ *
+ * Add exceptions as we go.
+ */
+
+ if(strcmp(b->name, "s3c-fimc") == 0) {
+ allowed = true;
+ pr_info("[%s] Accessing space 0x%08x/0x%08x for '%s'\n",
+ __func__, b->start, b->size, b->name);
+ }
+
+ }
+
+ b++;
+ }
+
+ if (!allowed) {
+ /* The requested memory space isn't in any CMA block, deny access */
+ pr_err("[%s] invalid paddr(0x%08x)-(0x%08x), accessing outside of DMA spaces\n",
+ __func__, start, (start + size));
+ return -EINVAL;
+ }
+
+ /* The check below doesn't matter anymore */
+
/* TODO: currently lowmem is only avaiable */
if ((phys_to_virt(start) < (void *)PAGE_OFFSET) ||
(phys_to_virt(start) >= high_memory)) {
diff --git a/include/linux/exynos_mem.h b/include/linux/exynos_mem.h
index 9c7bed9..3ed0bd6 100644
--- a/include/linux/exynos_mem.h
+++ b/include/linux/exynos_mem.h
@@ -22,4 +22,13 @@ struct exynos_mem_flush_range {
size_t length;
};
+#define CMA_REGION_COUNT (12)
+struct simple_cma_descriptor {
+ const char *name;
+ int start;
+ int size;
+};
+
+extern void cma_region_descriptor_add(const char *name, int start, int size);
+
#endif /* __INCLUDE_EXYNOS_MEM_H */