From: Mahesh Lagadapati Date: Fri, 31 Oct 2014 03:36:17 +0000 (-0700) Subject: security: tlk_driver: lock/fault userspace pages X-Git-Tag: daily-2014.11.28.0_rel-st8-l-r1-partner-shieldtablet8~6 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/sojka/nv-tegra/linux-3.10.git/commitdiff_plain/0d124dbefd6b0f67d64d29669d4685b267083d99 security: tlk_driver: lock/fault userspace pages When AF bit is zero for an user space buffer, access faults are observed during address translation in the secure world. Lock userspace buffers using sys_mlock to ensure that AF bit is not cleared once they are passed to the secure world. Also invoke fixup_user_fault API to set AF bit to 1 to handle the case when AF bit is zero even before calling sys_mlock. Bug 1566742 Change-Id: If1a70f7e9d8fb0e1d03a837d277e068c8ce22042 Signed-off-by: Mahesh Lagadapati Reviewed-on: http://git-master/r/591946 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Krishna Reddy Reviewed-by: Chris Johnson Reviewed-by: Todd Poynter --- diff --git a/security/tlk_driver/ote_comms.c b/security/tlk_driver/ote_comms.c index 8a0c4ece66b..b6460470ded 100644 --- a/security/tlk_driver/ote_comms.c +++ b/security/tlk_driver/ote_comms.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "ote_protocol.h" @@ -36,12 +37,16 @@ core_param(verbose_smc, verbose_smc, bool, 0644); #define SET_RESULT(req, r, ro) { req->result = r; req->result_origin = ro; } static int te_pin_user_pages(void *buffer, size_t size, - unsigned long *pages_ptr, uint32_t buf_type) + unsigned long *pages_ptr, uint32_t buf_type, bool *is_locked) { int ret = 0; unsigned int nr_pages; struct page **pages = NULL; bool writable; + struct vm_area_struct *vma = NULL; + unsigned int flags; + int i; + bool is_locked_prev; nr_pages = (((uintptr_t)buffer & (PAGE_SIZE - 1)) + (size + PAGE_SIZE - 1)) >> PAGE_SHIFT; @@ -60,9 +65,68 @@ static int te_pin_user_pages(void *buffer, size_t size, up_read(¤t->mm->mmap_sem); + if (ret <= 0) { + pr_err("%s: Error %d in get_user_pages\n", __func__, ret); + return ret; + } + *pages_ptr = (unsigned long) pages; + nr_pages = ret; - return ret; + down_read(¤t->mm->mmap_sem); + + is_locked_prev = false; + vma = find_extend_vma(current->mm, (unsigned long)buffer); + if (vma && (vma->vm_flags & VM_LOCKED)) + is_locked_prev = true; + + up_read(¤t->mm->mmap_sem); + + /* + * Lock the pages if they are not already locked to ensure that + * AF bit is not set to zero. + */ + *is_locked = false; + if (!is_locked_prev) { + ret = sys_mlock((unsigned long)buffer, size); + if (!ret) + *is_locked = true; + else + /* + * Follow through even if mlock failed as it can be + * failed due to memory restrictions or invalid + * capabilities + */ + pr_warn("%s: Error %d in mlock, continuing session\n", + __func__, ret); + } + + down_read(¤t->mm->mmap_sem); + + /* Fault pages to set the AF bit in PTE */ + flags = FAULT_FLAG_USER; + if (writable) + flags |= FAULT_FLAG_WRITE; + for (i = 0; i < nr_pages; i++) { + ret = fixup_user_fault(current, current->mm, + (unsigned long)(buffer + (i * PAGE_SIZE)), flags); + if (ret) { + pr_err("%s: Error %d in fixup_user_fault\n", + __func__, ret); + break; + } + } + + up_read(¤t->mm->mmap_sem); + + if (ret) { + if (*is_locked) + sys_munlock((unsigned long)buffer, size); + return ret; + } + + /* Return the number of pages pinned */ + return nr_pages; } static int te_prep_mem_buffer(uint32_t session_id, @@ -72,6 +136,7 @@ static int te_prep_mem_buffer(uint32_t session_id, unsigned long pages = 0; struct te_shmem_desc *shmem_desc = NULL; int ret = 0, nr_pages = 0; + bool is_locked = false; /* allocate new shmem descriptor */ shmem_desc = kzalloc(sizeof(struct te_shmem_desc), GFP_KERNEL); @@ -82,7 +147,8 @@ static int te_prep_mem_buffer(uint32_t session_id, } /* pin pages */ - nr_pages = te_pin_user_pages(buffer, size, &pages, buf_type); + nr_pages = te_pin_user_pages(buffer, size, &pages, + buf_type, &is_locked); if (nr_pages <= 0) { pr_err("%s: te_pin_user_pages failed (%d)\n", __func__, nr_pages); @@ -98,6 +164,7 @@ static int te_prep_mem_buffer(uint32_t session_id, shmem_desc->size = size; shmem_desc->nr_pages = nr_pages; shmem_desc->pages = (struct page **)(uintptr_t)pages; + shmem_desc->is_locked = is_locked; /* add shmem descriptor to proper list */ if ((buf_type == TE_PARAM_TYPE_MEM_RO) || @@ -192,6 +259,7 @@ static int te_prep_mem_buffers_compat(struct te_request_compat *request, static void te_release_mem_buffer(struct te_shmem_desc *shmem_desc) { uint32_t i; + int status; list_del(&shmem_desc->list); for (i = 0; i < shmem_desc->nr_pages; i++) { @@ -201,6 +269,14 @@ static void te_release_mem_buffer(struct te_shmem_desc *shmem_desc) page_cache_release(shmem_desc->pages[i]); } kfree(shmem_desc->pages); + + if (shmem_desc->is_locked) { + status = sys_munlock((unsigned long)shmem_desc->buffer, + shmem_desc->size); + if (status) + pr_err("%s:Error %d in munlock\n", __func__, status); + } + kfree(shmem_desc); } diff --git a/security/tlk_driver/ote_protocol.h b/security/tlk_driver/ote_protocol.h index 145febb5672..101a3e5dcb2 100644 --- a/security/tlk_driver/ote_protocol.h +++ b/security/tlk_driver/ote_protocol.h @@ -120,6 +120,7 @@ struct te_shmem_desc { size_t size; struct page **pages; unsigned int nr_pages; + bool is_locked; }; struct tlk_context {