DivestOS/Patches/Linux_CVEs/CVE-2017-7366/0.patch

458 lines
16 KiB
Diff

From f4c9ffd6cd7960265f38e285ac43cbecf2459e45 Mon Sep 17 00:00:00 2001
From: Jordan Crouse <jcrouse@codeaurora.org>
Date: Tue, 31 May 2016 11:24:23 -0600
Subject: msm: kgsl: Fix pagetable member of struct kgsl_memdesc
memdesc->pagetable is supposed to help ensure that memory gets
unmapped before it is freed, but the pagetable member is being
populated at create time not when the buffer gets mapped. This
forces the developer to ensure that the same pagetable is
used for both the create and map step. Instead, assign the
pagetable member when it is first used (to get a GPU address)
and put it away when the GPU address is released.
Change-Id: Ic0dedbad372fd9029b932dd99633a650049751ed
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: Sudeep Yedalapure <sudeepy@codeaurora.org>
---
drivers/gpu/msm/adreno_a5xx.c | 4 ++--
drivers/gpu/msm/kgsl.c | 33 +++++++--------------------------
drivers/gpu/msm/kgsl_iommu.c | 35 +++++++++++++++++++----------------
drivers/gpu/msm/kgsl_mmu.c | 29 +++++++++++++++++++++++------
drivers/gpu/msm/kgsl_mmu.h | 7 +++----
drivers/gpu/msm/kgsl_sharedmem.c | 27 ++++++++-------------------
drivers/gpu/msm/kgsl_sharedmem.h | 11 ++++-------
7 files changed, 66 insertions(+), 80 deletions(-)
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index f770972..bcef03e 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -250,7 +250,7 @@ static int a5xx_critical_packet_construct(struct adreno_device *adreno_dev)
return ret;
ret = kgsl_allocate_user(&adreno_dev->dev, &crit_pkts_refbuf0,
- NULL, PAGE_SIZE, KGSL_MEMFLAGS_SECURE);
+ PAGE_SIZE, KGSL_MEMFLAGS_SECURE);
if (ret)
return ret;
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index d06eebb..bd022f1 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -370,24 +370,6 @@ kgsl_mem_entry_track_gpuaddr(struct kgsl_process_private *process,
return kgsl_mmu_get_gpuaddr(pagetable, &entry->memdesc);
}
-/**
- * kgsl_mem_entry_untrack_gpuaddr() - Untrack memory that is previously tracked
- * process - Pointer to process private to which memory belongs
- * entry - Memory entry to untrack
- *
- * Function just does the opposite of kgsl_mem_entry_track_gpuaddr. Needs to be
- * called with processes spin lock held
- */
-static void
-kgsl_mem_entry_untrack_gpuaddr(struct kgsl_process_private *process,
- struct kgsl_mem_entry *entry)
-{
- struct kgsl_pagetable *pagetable = entry->memdesc.pagetable;
-
- if (entry->memdesc.gpuaddr)
- kgsl_mmu_put_gpuaddr(pagetable, &entry->memdesc);
-}
-
/* Commit the entry to the process so it can be accessed by other operations */
static void kgsl_mem_entry_commit_process(struct kgsl_mem_entry *entry)
{
@@ -436,7 +418,7 @@ kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
if (id < 0) {
ret = id;
- kgsl_mem_entry_untrack_gpuaddr(process, entry);
+ kgsl_mmu_put_gpuaddr(&entry->memdesc);
goto err_put_proc_priv;
}
@@ -472,6 +454,7 @@ err_put_proc_priv:
static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
{
unsigned int type;
+
if (entry == NULL)
return;
@@ -488,9 +471,7 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
entry->priv->stats[type].cur -= entry->memdesc.size;
spin_unlock(&entry->priv->mem_lock);
- kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
-
- kgsl_mem_entry_untrack_gpuaddr(entry->priv, entry);
+ kgsl_mmu_put_gpuaddr(&entry->memdesc);
kgsl_process_private_put(entry->priv);
@@ -3021,7 +3002,7 @@ static struct kgsl_mem_entry *gpumem_alloc_entry(
entry->memdesc.priv |= KGSL_MEMDESC_SECURE;
ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc,
- private->pagetable, size, flags);
+ size, flags);
if (ret != 0)
goto err;
@@ -3442,11 +3423,11 @@ static unsigned long _gpu_set_svm_region(struct kgsl_process_private *private,
return ret;
entry->memdesc.gpuaddr = (uint64_t) addr;
+ entry->memdesc.pagetable = private->pagetable;
ret = kgsl_mmu_map(private->pagetable, &entry->memdesc);
if (ret) {
- kgsl_mmu_put_gpuaddr(private->pagetable,
- &entry->memdesc);
+ kgsl_mmu_put_gpuaddr(&entry->memdesc);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0d207494c..724d129 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1402,17 +1402,16 @@ static int _setstate_alloc(struct kgsl_device *device,
{
int ret;
- ret = kgsl_sharedmem_alloc_contig(device, &iommu->setstate, NULL,
- PAGE_SIZE);
- if (ret)
- return ret;
+ ret = kgsl_sharedmem_alloc_contig(device, &iommu->setstate, PAGE_SIZE);
- /* Mark the setstate memory as read only */
- iommu->setstate.flags |= KGSL_MEMFLAGS_GPUREADONLY;
+ if (!ret) {
+ /* Mark the setstate memory as read only */
+ iommu->setstate.flags |= KGSL_MEMFLAGS_GPUREADONLY;
- kgsl_sharedmem_set(device, &iommu->setstate, 0, 0, PAGE_SIZE);
+ kgsl_sharedmem_set(device, &iommu->setstate, 0, 0, PAGE_SIZE);
+ }
- return 0;
+ return ret;
}
static int kgsl_iommu_init(struct kgsl_mmu *mmu)
@@ -1663,7 +1662,7 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt,
if (!kgsl_secure_guard_page_memdesc.sgt) {
if (kgsl_allocate_user(KGSL_MMU_DEVICE(pt->mmu),
- &kgsl_secure_guard_page_memdesc, pt,
+ &kgsl_secure_guard_page_memdesc,
sgp_size, KGSL_MEMFLAGS_SECURE)) {
KGSL_CORE_ERR(
"Secure guard page alloc failed\n");
@@ -2264,23 +2263,27 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
}
ret = _insert_gpuaddr(pagetable, addr, size);
- if (ret == 0)
+ if (ret == 0) {
memdesc->gpuaddr = addr;
+ memdesc->pagetable = pagetable;
+ }
out:
spin_unlock(&pagetable->lock);
return ret;
}
-static void kgsl_iommu_put_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
+static void kgsl_iommu_put_gpuaddr(struct kgsl_memdesc *memdesc)
{
- spin_lock(&pagetable->lock);
+ if (memdesc->pagetable == NULL)
+ return;
+
+ spin_lock(&memdesc->pagetable->lock);
- if (_remove_gpuaddr(pagetable, memdesc->gpuaddr))
+ if (_remove_gpuaddr(memdesc->pagetable, memdesc->gpuaddr))
BUG();
- spin_unlock(&pagetable->lock);
+ spin_unlock(&memdesc->pagetable->lock);
}
static int kgsl_iommu_svm_range(struct kgsl_pagetable *pagetable,
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index d0c5dc7..99dff79 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -419,17 +419,29 @@ EXPORT_SYMBOL(kgsl_mmu_map);
* @pagetable: Pagetable to release the memory from
* @memdesc: Memory descriptor containing the GPU address to free
*/
-void kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
+void kgsl_mmu_put_gpuaddr(struct kgsl_memdesc *memdesc)
{
+ struct kgsl_pagetable *pagetable = memdesc->pagetable;
+ int unmap_fail = 0;
+
if (memdesc->size == 0 || memdesc->gpuaddr == 0)
return;
- if (PT_OP_VALID(pagetable, put_gpuaddr))
- pagetable->pt_ops->put_gpuaddr(pagetable, memdesc);
+ if (!kgsl_memdesc_is_global(memdesc))
+ unmap_fail = kgsl_mmu_unmap(pagetable, memdesc);
+
+ /*
+ * Do not free the gpuaddr/size if unmap fails. Because if we
+ * try to map this range in future, the iommu driver will throw
+ * a BUG_ON() because it feels we are overwriting a mapping.
+ */
+ if (PT_OP_VALID(pagetable, put_gpuaddr) && (unmap_fail == 0))
+ pagetable->pt_ops->put_gpuaddr(memdesc);
if (!kgsl_memdesc_is_global(memdesc))
memdesc->gpuaddr = 0;
+
+ memdesc->pagetable = NULL;
}
EXPORT_SYMBOL(kgsl_mmu_put_gpuaddr);
@@ -580,7 +592,12 @@ static int nommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
memdesc->gpuaddr = (uint64_t) sg_phys(memdesc->sgt->sgl);
- return memdesc->gpuaddr != 0 ? 0 : -ENOMEM;
+ if (memdesc->gpuaddr) {
+ memdesc->pagetable = pagetable;
+ return 0;
+ }
+
+ return -ENOMEM;
}
static struct kgsl_mmu_pt_ops nommu_pt_ops = {
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 93b1f9d..d191b1c 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -92,7 +92,7 @@ struct kgsl_mmu_pt_ops {
u64 (*get_ttbr0)(struct kgsl_pagetable *);
u32 (*get_contextidr)(struct kgsl_pagetable *);
int (*get_gpuaddr)(struct kgsl_pagetable *, struct kgsl_memdesc *);
- void (*put_gpuaddr)(struct kgsl_pagetable *, struct kgsl_memdesc *);
+ void (*put_gpuaddr)(struct kgsl_memdesc *);
uint64_t (*find_svm_region)(struct kgsl_pagetable *, uint64_t, uint64_t,
uint64_t, uint64_t);
int (*set_svm_region)(struct kgsl_pagetable *, uint64_t, uint64_t);
@@ -180,8 +180,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc);
int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc);
-void kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc);
+void kgsl_mmu_put_gpuaddr(struct kgsl_memdesc *memdesc);
unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr);
unsigned int kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu,
u64 ttbr0, uint64_t addr);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 941e4c4..7d7fec7 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -318,12 +318,11 @@ static int kgsl_cma_alloc_secure(struct kgsl_device *device,
static int kgsl_allocate_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
uint64_t size) {
int ret;
if (MMU_FEATURE(&device->mmu, KGSL_MMU_HYP_SECURE_ALLOC))
- ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size);
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, size);
else
ret = kgsl_cma_alloc_secure(device, memdesc, size);
@@ -332,7 +331,6 @@ static int kgsl_allocate_secure(struct kgsl_device *device,
int kgsl_allocate_user(struct kgsl_device *device,
struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
uint64_t size, uint64_t flags)
{
int ret;
@@ -340,12 +338,11 @@ int kgsl_allocate_user(struct kgsl_device *device,
memdesc->flags = flags;
if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)
- ret = kgsl_sharedmem_alloc_contig(device, memdesc,
- pagetable, size);
+ ret = kgsl_sharedmem_alloc_contig(device, memdesc, size);
else if (flags & KGSL_MEMFLAGS_SECURE)
- ret = kgsl_allocate_secure(device, memdesc, pagetable, size);
+ ret = kgsl_allocate_secure(device, memdesc, size);
else
- ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size);
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, size);
return ret;
}
@@ -637,7 +634,6 @@ static inline int get_page_size(size_t size, unsigned int align)
int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
uint64_t size)
{
int ret = 0;
@@ -671,7 +667,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
len_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT;
- memdesc->pagetable = pagetable;
memdesc->ops = &kgsl_page_alloc_ops;
/*
@@ -805,10 +800,8 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
if (memdesc == NULL || memdesc->size == 0)
return;
- if (memdesc->gpuaddr) {
- kgsl_mmu_unmap(memdesc->pagetable, memdesc);
- kgsl_mmu_put_gpuaddr(memdesc->pagetable, memdesc);
- }
+ /* Make sure the memory object has been unmapped */
+ kgsl_mmu_put_gpuaddr(memdesc);
if (memdesc->ops && memdesc->ops->free)
memdesc->ops->free(memdesc);
@@ -988,8 +981,7 @@ void kgsl_get_memory_usage(char *name, size_t name_size, uint64_t memflags)
EXPORT_SYMBOL(kgsl_get_memory_usage);
int kgsl_sharedmem_alloc_contig(struct kgsl_device *device,
- struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable, uint64_t size)
+ struct kgsl_memdesc *memdesc, uint64_t size)
{
int result = 0;
@@ -998,7 +990,6 @@ int kgsl_sharedmem_alloc_contig(struct kgsl_device *device,
return -EINVAL;
memdesc->size = size;
- memdesc->pagetable = pagetable;
memdesc->ops = &kgsl_cma_ops;
memdesc->dev = device->dev->parent;
@@ -1089,7 +1080,6 @@ static int kgsl_cma_alloc_secure(struct kgsl_device *device,
{
struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device);
int result = 0;
- struct kgsl_pagetable *pagetable = device->mmu.securepagetable;
size_t aligned;
/* Align size to 1M boundaries */
@@ -1109,7 +1099,6 @@ static int kgsl_cma_alloc_secure(struct kgsl_device *device,
memdesc->priv &= ~KGSL_MEMDESC_GUARD_PAGE;
memdesc->size = aligned;
- memdesc->pagetable = pagetable;
memdesc->ops = &kgsl_cma_ops;
memdesc->dev = iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE].dev;
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index aae79ad..19477f5 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,7 +26,7 @@ struct kgsl_process_private;
int kgsl_sharedmem_alloc_contig(struct kgsl_device *device,
struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable, uint64_t size);
+ uint64_t size);
void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc);
@@ -66,13 +66,11 @@ void kgsl_sharedmem_uninit_sysfs(void);
int kgsl_allocate_user(struct kgsl_device *device,
struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
uint64_t size, uint64_t flags);
void kgsl_get_memory_usage(char *str, size_t len, uint64_t memflags);
int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
uint64_t size);
#define MEMFLAGS(_flags, _mask, _shift) \
@@ -271,11 +269,10 @@ static inline int kgsl_allocate_global(struct kgsl_device *device,
memdesc->priv = priv;
if ((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0)
- ret = kgsl_sharedmem_alloc_contig(device, memdesc, NULL,
+ ret = kgsl_sharedmem_alloc_contig(device, memdesc,
(size_t) size);
else {
- ret = kgsl_sharedmem_page_alloc_user(memdesc, NULL,
- (size_t) size);
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, (size_t) size);
if (ret == 0)
kgsl_memdesc_map(memdesc);
}
--
cgit v1.1