From b2dd36938c30f62a1a1dfbd74a3bd61b9e634990 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 4 Nov 2016 14:35:58 +0800 Subject: [PATCH 01/44] ASoC: soc: prevent risk of buffer overflow In case of large value for bufcnt_t or bufcnt, cmd_size may overflow. Buffer size allocated by cmd_size might be not as expected. Possible buffer overflow could happen. CRs-Fixed: 1084210 Change-Id: I9556f18dd6a9fdf3f76c133ae75c04ecce171f08 Signed-off-by: Xiaojun Sang --- sound/soc/msm/qdsp6v2/q6asm.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index c960c98f5e9..fd05a2e5e51 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -4837,7 +4837,7 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, struct asm_buffer_node *buffer_node = NULL; int rc = 0; int i = 0; - int cmd_size = 0; + uint32_t cmd_size = 0; uint32_t bufcnt_t; uint32_t bufsz_t; @@ -4859,10 +4859,25 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, bufsz_t = PAGE_ALIGN(bufsz_t); } + if (bufcnt_t > (UINT_MAX + - sizeof(struct avs_cmd_shared_mem_map_regions)) + / sizeof(struct avs_shared_map_region_payload)) { + pr_err("%s: Unsigned Integer Overflow. bufcnt_t = %u\n", + __func__, bufcnt_t); + return -EINVAL; + } + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + (sizeof(struct avs_shared_map_region_payload) * bufcnt_t); + + if (bufcnt > (UINT_MAX / sizeof(struct asm_buffer_node))) { + pr_err("%s: Unsigned Integer Overflow. bufcnt = %u\n", + __func__, bufcnt); + return -EINVAL; + } + buffer_node = kzalloc(sizeof(struct asm_buffer_node) * bufcnt, GFP_KERNEL); if (!buffer_node) { From 351922035c22c0f56a744ab4c41ca08e325d6261 Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Mon, 24 Oct 2016 10:06:45 -0700 Subject: [PATCH 02/44] qseecom: improve error checks in qseecom_probe() Make change in qseecom_probe() to improve the error return value checks on some subfunctions, and free memory allocated within qseecom_retrieve_ce_data. CRs-fixed: 1075082 Change-Id: I971e555ec8d02ccf4382e83132a696b065a8ff12 Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index c543216f1b2..691298852d6 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -5540,7 +5540,6 @@ static void __qseecom_deinit_clk(enum qseecom_ce_hw_instance ce) static int qseecom_probe(struct platform_device *pdev) { int rc; - int ret = 0; struct device *class_dev; char qsee_not_legacy = 0; struct msm_bus_scale_pdata *qseecom_platform_support = NULL; @@ -5588,7 +5587,7 @@ static int qseecom_probe(struct platform_device *pdev) class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL, QSEECOM_DEV); - if (!class_dev) { + if (IS_ERR(class_dev)) { pr_err("class_device_create failed %d\n", rc); rc = -ENOMEM; goto exit_destroy_class; @@ -5640,7 +5639,7 @@ static int qseecom_probe(struct platform_device *pdev) qseecom.pdev = class_dev; /* Create ION msm client */ qseecom.ion_clnt = msm_ion_client_create("qseecom-kernel"); - if (qseecom.ion_clnt == NULL) { + if (IS_ERR_OR_NULL(qseecom.ion_clnt)) { pr_err("Ion client cannot be created\n"); rc = -ENOMEM; goto exit_del_cdev; @@ -5778,14 +5777,14 @@ static int qseecom_probe(struct platform_device *pdev) pr_info("CE operating frequency is not defined, setting to default 100MHZ\n"); qseecom.ce_opp_freq_hz = QSEE_CE_CLK_100MHZ; } - ret = __qseecom_init_clk(CLK_QSEE); - if (ret) + rc = __qseecom_init_clk(CLK_QSEE); + if (rc) goto exit_destroy_hw_instance_list; if ((qseecom.qsee.instance != qseecom.ce_drv.instance) && (qseecom.support_pfe || qseecom.support_fde)) { - ret = __qseecom_init_clk(CLK_CE_DRV); - if (ret) { + rc = __qseecom_init_clk(CLK_CE_DRV); + if (rc) { __qseecom_deinit_clk(CLK_QSEE); goto exit_destroy_hw_instance_list; } @@ -5818,7 +5817,7 @@ static int qseecom_probe(struct platform_device *pdev) } else { pr_err("Fail to get secure app region info\n"); rc = -EINVAL; - goto exit_destroy_hw_instance_list; + goto exit_deinit_clock; } rc = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req), @@ -5827,7 +5826,7 @@ static int qseecom_probe(struct platform_device *pdev) pr_err("send secapp reg fail %d resp.res %d\n", rc, resp.result); rc = -EINVAL; - goto exit_destroy_hw_instance_list; + goto exit_deinit_clock; } } } else { @@ -5849,6 +5848,11 @@ static int qseecom_probe(struct platform_device *pdev) pr_err("Unable to register bus client\n"); return 0; +exit_deinit_clock: + __qseecom_deinit_clk(CLK_QSEE); + if ((qseecom.qsee.instance != qseecom.ce_drv.instance) && + (qseecom.support_pfe || qseecom.support_fde)) + __qseecom_deinit_clk(CLK_CE_DRV); exit_destroy_hw_instance_list: kzfree(qseecom.ce_info.hlos_ce_hw_instance); exit_destroy_ion_client: From 8793e155127cb21ba548ca8d5be0a2e4aa938db5 Mon Sep 17 00:00:00 2001 From: Sanjay Singh Date: Mon, 28 Nov 2016 16:57:13 +0530 Subject: [PATCH 03/44] msm: vidc: WARN_ON() reveals fuction addresses There is a security vulnerability where function addresses are printed in kernel message if WARN_ON() is invoked implicitly. WARN_ON() call is made explicit to avoid this issue. Change-Id: If75581803adf62cb9bda3784ad1d4f4088e0d797 Signed-off-by: Sanjay Singh --- drivers/media/platform/msm/vidc/msm_vidc.c | 3 ++- drivers/media/platform/msm/vidc/venus_hfi.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 60133e8b6b9..57e12629ed1 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -1424,7 +1424,8 @@ static void cleanup_instance(struct msm_vidc_inst *inst) debugfs_remove_recursive(inst->debugfs_root); mutex_lock(&inst->pending_getpropq.lock); - WARN_ON(!list_empty(&inst->pending_getpropq.list)); + WARN_ON(!list_empty(&inst->pending_getpropq.list) + && (msm_vidc_debug & VIDC_INFO)); mutex_unlock(&inst->pending_getpropq.lock); } } diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 84c42374aec..4df4f35701f 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -262,7 +262,7 @@ static int venus_hfi_acquire_regulator(struct regulator_info *rinfo) rinfo->name); } } - WARN_ON(!regulator_is_enabled(rinfo->regulator)); + WARN_ON(!regulator_is_enabled(rinfo->regulator) && (msm_vidc_debug & VIDC_INFO)); return rc; } @@ -3954,7 +3954,7 @@ static int venus_hfi_disable_regulator(struct regulator_info *rinfo) disable_regulator_failed: /* Bring attention to this issue */ - WARN_ON(1); + WARN_ON(msm_vidc_debug & VIDC_INFO); return rc; } From 4d37e248171ae6e63abff8bad4fa9effa3e66ec0 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 6 Jul 2015 23:18:37 +0300 Subject: [PATCH 04/44] mm: avoid setting up anonymous pages into file mapping commit 6b7339f4c31ad69c8e9c0b2859276e22cf72176d upstream. Reading page fault handler code I've noticed that under right circumstances kernel would map anonymous pages into file mappings: if the VMA doesn't have vm_ops->fault() and the VMA wasn't fully populated on ->mmap(), kernel would handle page fault to not populated pte with do_anonymous_page(). Let's change page fault handler to use do_anonymous_page() only on anonymous VMA (->vm_ops == NULL) and make sure that the VMA is not shared. For file mappings without vm_ops->fault() or shred VMA without vm_ops, page fault on pte_none() entry would lead to SIGBUS. Signed-off-by: Kirill A. Shutemov Acked-by: Oleg Nesterov Cc: Andrew Morton Cc: Willy Tarreau Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman Git-repo: https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable Git-commit: efcbc94afe6dd0f8a4b112a0f3385cdc89ea58ba Change-Id: Ifab61dd2e0f503811ff428ab6b1d8ba62436e920 Signed-off-by: Srinivasa Rao Kuppala --- mm/memory.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index b847e769ccb..55510ddb2f2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3269,6 +3269,10 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, pte_unmap(page_table); + /* File mapping without ->vm_ops ? */ + if (vma->vm_flags & VM_SHARED) + return VM_FAULT_SIGBUS; + /* Check if we need to add a guard page to the stack */ if (check_stack_guard_page(vma, address) < 0) return VM_FAULT_SIGSEGV; @@ -3534,6 +3538,9 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; pte_unmap(page_table); + /* The VMA was not fully populated on mmap() or missing VM_DONTEXPAND */ + if (!vma->vm_ops->fault) + return VM_FAULT_SIGBUS; return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); } @@ -3745,11 +3752,9 @@ int handle_pte_fault(struct mm_struct *mm, entry = *pte; if (!pte_present(entry)) { if (pte_none(entry)) { - if (vma->vm_ops) { - if (likely(vma->vm_ops->fault)) - return do_linear_fault(mm, vma, address, + if (vma->vm_ops) + return do_linear_fault(mm, vma, address, pte, pmd, flags, entry); - } return do_anonymous_page(mm, vma, address, pte, pmd, flags); } From aaca6229558ea725e0c0f5f9ad939a502e7d8c00 Mon Sep 17 00:00:00 2001 From: John Dias Date: Wed, 9 Nov 2016 11:03:57 -0800 Subject: [PATCH 05/44] perf: don't leave group_entry on sibling list (use-after-free) When perf_group_detach is called on a group leader, it should empty its sibling list. Otherwise, when a sibling is later deallocated, list_del_event() removes the sibling's group_entry from its current list, which can be the now-deallocated group leader's sibling list (use-after-free bug). Bug: 32402548 Change-Id: I99f6bc97c8518df1cb0035814368012ba72ab1f1 Signed-off-by: John Dias Git-repo: https://android.googlesource.com/kernel/msm Git-commit: 6b6cfb2362f09553b46b3b7e5684b16b6e53e373 Signed-off-by: Dennis Cagle --- kernel/events/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index 5c4b7f7ae69..a8bf970e8f6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1324,10 +1324,17 @@ static void perf_group_detach(struct perf_event *event) * If this was a group event with sibling events then * upgrade the siblings to singleton events by adding them * to whatever list we are on. + * If this isn't on a list, make sure we still remove the sibling's + * group_entry from this sibling_list; otherwise, when that sibling + * is later deallocated, it will try to remove itself from this + * sibling_list, which may well have been deallocated already, + * resulting in a use-after-free. */ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, group_entry) { if (list) list_move_tail(&sibling->group_entry, list); + else + list_del_init(&sibling->group_entry); sibling->group_leader = sibling; /* Inherit group flags from the previous leader */ From 32d12dc60f35dd2c223c917a26c1b091484ca393 Mon Sep 17 00:00:00 2001 From: Siqi Lin Date: Wed, 2 Nov 2016 16:51:08 -0700 Subject: [PATCH 06/44] ALSA: info: Check for integer overflow in snd_info_entry_write() snd_info_entry_write() resizes the buffer with an unsigned long size argument that gets truncated because resize_info_buffer() takes the size parameter as an unsigned int. On 64-bit kernels, this causes the following copy_to_user() to write out-of-bounds if (pos + count) can't be represented by an unsigned int. Bug: 32510733 Change-Id: I9e8b55f93f2bd606b4a73b5a4525b71ee88c7c23 Signed-off-by: Siqi Lin Git-repo: https://android.googlesource.com/kernel/msm Git-commit: 600c59f1f58bf30716d4a072add85f8da77c145c Signed-off-by: Dennis Cagle --- sound/core/info.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/info.c b/sound/core/info.c index 08070e1eefe..332c9a1ef17 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -253,6 +253,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer struct snd_info_buffer *buf; ssize_t size = 0; loff_t pos; + unsigned long realloc_size; data = file->private_data; if (snd_BUG_ON(!data)) @@ -261,7 +262,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer pos = *offset; if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) return -EIO; - if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) + realloc_size = (unsigned long) pos + (unsigned long) count; + if (realloc_size < (unsigned long) pos || realloc_size > UINT_MAX) return -EIO; switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: From e216b3be6d57eb82cdec1681183dcdd759853849 Mon Sep 17 00:00:00 2001 From: Andrew Chant Date: Sun, 13 Nov 2016 17:03:13 -0800 Subject: [PATCH 07/44] input: synaptics_dsx: add update bounds checks. Firmware updates contain offsets that are parsed by the kernel driver. Ensure all offsets are within the bounds of the firmware update. TESTED: successfully parsed update firmware on device boot. Bug: 31525965 Bug: 31968442 Change-Id: I074d3f26d33f723a0b239b59ebc3a680d991cdae Signed-off-by: Andrew Chant Git-repo: https://android.googlesource.com/kernel/msm Git-commit: ac2afe08221e0d077befd2e6f216598640af2d51 Signed-off-by: Dennis Cagle --- .../input/touchscreen/synaptics_fw_update.c | 70 ++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c index c25a47528c4..f50378046f5 100644 --- a/drivers/input/touchscreen/synaptics_fw_update.c +++ b/drivers/input/touchscreen/synaptics_fw_update.c @@ -258,6 +258,7 @@ struct synaptics_rmi4_fwu_handle { bool interrupt_flag; bool polling_mode; char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned int full_update_size; unsigned int image_size; unsigned int data_pos; unsigned char intr_mask; @@ -297,6 +298,22 @@ static struct synaptics_rmi4_fwu_handle *fwu; DECLARE_COMPLETION(fwu_remove_complete); DEFINE_MUTEX(fwu_sysfs_mutex); +/* Check offset + size <= bound. 1 if in bounds, 0 otherwise. */ +static bool in_bounds(unsigned long offset, + unsigned long size, + unsigned long bound) +{ + if (offset > bound || size > bound) { + pr_err("%s: %lu or %lu > %lu\n", __func__, offset, size, bound); + return 0; + } + if (offset > (bound - size)) { + pr_err("%s: %lu > %lu - %lu\n", __func__, offset, size, bound); + return 0; + } + return 1; +} + static unsigned int extract_uint(const unsigned char *ptr) { return (unsigned int)ptr[0] + @@ -333,11 +350,17 @@ static void synaptics_rmi4_update_debug_info(void) pkg_id[3] << 8 | pkg_id[2], build_id); } -static void parse_header(void) +static int parse_header(void) { struct image_content *img = &fwu->image_content; struct image_header_data *data = (struct image_header_data *)fwu->data_buffer; + if (fwu->full_update_size < sizeof(*data)) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "Provided update too small"); + return -EINVAL; + } + img->checksum = extract_uint(data->file_checksum); img->bootloader_version = data->bootloader_version; img->image_size = extract_uint(data->firmware_size); @@ -374,12 +397,29 @@ static void parse_header(void) img->config_size); /* get UI firmware offset */ - if (img->image_size) + if (img->image_size) { + if (!in_bounds(FW_IMAGE_OFFSET, img->image_size, + fwu->full_update_size)) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: image size out of bounds\n", + __func__); + return -EINVAL; + } img->firmware_data = fwu->data_buffer + FW_IMAGE_OFFSET; + } /* get config offset*/ - if (img->config_size) + if (img->config_size) { + // FW_IMAGE_OFFSET + image_size was ok as above + if (!in_bounds(FW_IMAGE_OFFSET + img->image_size, + img->config_size, fwu->full_update_size)) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: config size out of bounds\n", + __func__); + return -EINVAL; + } img->config_data = fwu->data_buffer + FW_IMAGE_OFFSET + img->image_size; + } /* get lockdown offset*/ switch (img->bootloader_version) { case 3: @@ -398,6 +438,14 @@ static void parse_header(void) img->lockdown_data = NULL; } + if (img->lockdown_block_count * fwu->block_size > FW_IMAGE_OFFSET) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: lockdown size too big\n", + __func__); + return -EINVAL; + } + if (fwu->full_update_size < FW_IMAGE_OFFSET) + return -EINVAL; img->lockdown_data = fwu->data_buffer + FW_IMAGE_OFFSET - img->lockdown_block_count * fwu->block_size; @@ -406,7 +454,7 @@ static void parse_header(void) fwu->lockdown_data = img->lockdown_data; fwu->config_data = img->config_data; fwu->firmware_data = img->firmware_data; - return; + return 0; } static int fwu_read_f01_device_status(struct f01_device_status *status) @@ -1290,8 +1338,8 @@ static int fwu_start_write_config(void) "%s: write config from config file\n", __func__); fwu->config_data = fwu->data_buffer; - } else { - parse_header(); + } else if (parse_header()) { + return -EINVAL; } pr_notice("%s: Start of write config process\n", __func__); @@ -1358,7 +1406,8 @@ exit: static int fwu_start_write_lockdown(void) { - parse_header(); + if (parse_header()) + return -EINVAL; return fwu_do_write_lockdown(true); } @@ -1580,9 +1629,13 @@ static int fwu_start_reflash(void) __func__, fw_entry->size); fwu->data_buffer = fw_entry->data; + fwu->full_update_size = fw_entry->size; } - parse_header(); + if (parse_header()) { + retval = -EINVAL; + goto exit; + } flash_area = fwu_go_nogo(); if (fwu->rmi4_data->sensor_sleep) { @@ -2021,6 +2074,7 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev, if (!mutex_trylock(&fwu_sysfs_mutex)) return -EBUSY; + fwu->full_update_size = size; fwu->image_size = size; fwu->data_pos = 0; From 6db3b74ea532432bf10d0f90a5c0620a1355b968 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 13 May 2016 09:34:12 -0400 Subject: [PATCH 08/44] ring-buffer: Prevent overflow of size in ring_buffer_resize() If the size passed to ring_buffer_resize() is greater than MAX_LONG - BUF_PAGE_SIZE then the DIV_ROUND_UP() will return zero. Here's the details: # echo 18014398509481980 > /sys/kernel/debug/tracing/buffer_size_kb tracing_entries_write() processes this and converts kb to bytes. 18014398509481980 << 10 = 18446744073709547520 and this is passed to ring_buffer_resize() as unsigned long size. size = DIV_ROUND_UP(size, BUF_PAGE_SIZE); Where DIV_ROUND_UP(a, b) is (a + b - 1)/b BUF_PAGE_SIZE is 4080 and here 18446744073709547520 + 4080 - 1 = 18446744073709551599 where 18446744073709551599 is still smaller than 2^64 2^64 - 18446744073709551599 = 17 But now 18446744073709551599 / 4080 = 4521260802379792 and size = size * 4080 = 18446744073709551360 This is checked to make sure its still greater than 2 * 4080, which it is. Then we convert to the number of buffer pages needed. nr_page = DIV_ROUND_UP(size, BUF_PAGE_SIZE) but this time size is 18446744073709551360 and 2^64 - (18446744073709551360 + 4080 - 1) = -3823 Thus it overflows and the resulting number is less than 4080, which makes 3823 / 4080 = 0 an nr_pages is set to this. As we already checked against the minimum that nr_pages may be, this causes the logic to fail as well, and we crash the kernel. There's no reason to have the two DIV_ROUND_UP() (that's just result of historical code changes), clean up the code and fix this bug. Cc: stable@vger.kernel.org # 3.5+ Fixes: 83f40318dab00 ("ring-buffer: Make removal of ring buffer pages atomic") Signed-off-by: Steven Rostedt (cherry picked from commit 59643d1535eb220668692a5359de22545af579f6) Git-repo: http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git Git-commit: 59643d1535eb220668692a5359de22545af579f6 Change-Id: If773b9678be6cbcd1a8821bbd23be798a3289948 Signed-off-by: Srinivasa Rao Kuppala --- kernel/trace/ring_buffer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index af365ed7878..2fdfff0d2a3 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1646,14 +1646,13 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, !cpumask_test_cpu(cpu_id, buffer->cpumask)) return size; - size = DIV_ROUND_UP(size, BUF_PAGE_SIZE); - size *= BUF_PAGE_SIZE; + nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); /* we need a minimum of two pages */ - if (size < BUF_PAGE_SIZE * 2) - size = BUF_PAGE_SIZE * 2; + if (nr_pages < 2) + nr_pages = 2; - nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); + size = nr_pages * BUF_PAGE_SIZE; /* * Don't succeed if resizing is disabled, as a reader might be From 5e97606c123b2633107fddd14a295205ad9f5ce1 Mon Sep 17 00:00:00 2001 From: EunTaik Lee Date: Wed, 24 Feb 2016 04:38:06 +0000 Subject: [PATCH 09/44] staging/android/ion : fix a race condition in the ion driver There is a use-after-free problem in the ion driver. This is caused by a race condition in the ion_ioctl() function. A handle has ref count of 1 and two tasks on different cpus calls ION_IOC_FREE simultaneously. cpu 0 cpu 1 ------------------------------------------------------- ion_handle_get_by_id() (ref == 2) ion_handle_get_by_id() (ref == 3) ion_free() (ref == 2) ion_handle_put() (ref == 1) ion_free() (ref == 0 so ion_handle_destroy() is called and the handle is freed.) ion_handle_put() is called and it decreases the slub's next free pointer The problem is detected as an unaligned access in the spin lock functions since it uses load exclusive instruction. In some cases it corrupts the slub's free pointer which causes a mis-aligned access to the next free pointer.(kmalloc returns a pointer like ffffc0745b4580aa). And it causes lots of other hard-to-debug problems. This symptom is caused since the first member in the ion_handle structure is the reference count and the ion driver decrements the reference after it has been freed. To fix this problem client->lock mutex is extended to protect all the codes that uses the handle. Change-Id: Ia1a36ad6336305fe8383863cfab066a56525fd9f Signed-off-by: Eun Taik Lee Reviewed-by: Laura Abbott Signed-off-by: Greg Kroah-Hartman Git-commit: 9590232bb4f4cc824f3425a6e1349afbe6d6d2b7 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [shashim@codeaurora.org: Resolved minor merge conflicts] Signed-off-by: Shiraz Hashim --- drivers/staging/android/ion/ion.c | 55 +++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 13 deletions(-) mode change 100644 => 100755 drivers/staging/android/ion/ion.c diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c old mode 100644 new mode 100755 index 1e27e3dff41..871f216e510 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -402,13 +402,22 @@ static void ion_handle_get(struct ion_handle *handle) kref_get(&handle->ref); } +static int ion_handle_put_nolock(struct ion_handle *handle) +{ + int ret; + + ret = kref_put(&handle->ref, ion_handle_destroy); + + return ret; +} + int ion_handle_put(struct ion_handle *handle) { struct ion_client *client = handle->client; int ret; mutex_lock(&client->lock); - ret = kref_put(&handle->ref, ion_handle_destroy); + ret = ion_handle_put_nolock(handle); mutex_unlock(&client->lock); return ret; @@ -432,18 +441,28 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client, return ERR_PTR(-EINVAL); } +static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client, + int id) +{ + struct ion_handle *handle; + + handle = idr_find(&client->idr, id); + if (handle) + ion_handle_get(handle); + + return handle ? handle : ERR_PTR(-EINVAL); +} + struct ion_handle *ion_handle_get_by_id(struct ion_client *client, int id) { struct ion_handle *handle; mutex_lock(&client->lock); - handle = idr_find(&client->idr, id); - if (handle) - ion_handle_get(handle); + handle = ion_handle_get_by_id_nolock(client, id); mutex_unlock(&client->lock); - return handle ? handle : ERR_PTR(-EINVAL); + return handle; } static bool ion_handle_validate(struct ion_client *client, @@ -597,21 +616,27 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, } EXPORT_SYMBOL(ion_alloc); -void ion_free(struct ion_client *client, struct ion_handle *handle) +static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle) { bool valid_handle; BUG_ON(client != handle->client); - mutex_lock(&client->lock); valid_handle = ion_handle_validate(client, handle); if (!valid_handle) { WARN(1, "%s: invalid handle passed to free.\n", __func__); - mutex_unlock(&client->lock); return; } + ion_handle_put_nolock(handle); +} + +void ion_free(struct ion_client *client, struct ion_handle *handle) +{ + BUG_ON(client != handle->client); + + mutex_lock(&client->lock); + ion_free_nolock(client, handle); mutex_unlock(&client->lock); - ion_handle_put(handle); } EXPORT_SYMBOL(ion_free); @@ -1441,11 +1466,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct ion_handle *handle; - handle = ion_handle_get_by_id(client, data.handle.handle); - if (IS_ERR(handle)) + mutex_lock(&client->lock); + handle = ion_handle_get_by_id_nolock(client, data.handle.handle); + if (IS_ERR(handle)) { + mutex_unlock(&client->lock); return PTR_ERR(handle); - ion_free(client, handle); - ion_handle_put(handle); + } + ion_free_nolock(client, handle); + ion_handle_put_nolock(handle); + mutex_unlock(&client->lock); break; } case ION_IOC_SHARE: From cc7b941d724a0cf02b64c8c252baa8e63f84f386 Mon Sep 17 00:00:00 2001 From: Divya Narayanan Poojary Date: Mon, 16 Jan 2017 18:47:31 +0530 Subject: [PATCH 10/44] ASoC: msm-lsm-client: cleanup ioctl functions Some of the ioctl command handling is not properly using the copy_from_user interface. Fix these issues and cleanup the ioctl functions to make sure there is no illegal memory access. CRs-Fixed: 1087469 Change-Id: Ieb1beb92e7854a05b8045de0ce179d12c9a6da74 Signed-off-by: Bhalchandra Gajare Signed-off-by: Divya Narayanan Poojary --- sound/soc/msm/qdsp6v2/msm-lsm-client.c | 103 +++++++++---------------- 1 file changed, 38 insertions(+), 65 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c index 8e48dad0c79..717a071a99f 100644 --- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -352,6 +352,7 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, u32 size = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_lsm_event_status *user = arg; struct snd_lsm_detection_params det_params; uint8_t *confidence_level = NULL; @@ -360,8 +361,12 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_LSM_SET_SESSION_DATA: pr_debug("%s: set Session data\n", __func__); - memcpy(&session_data, arg, - sizeof(struct snd_lsm_session_data)); + if (copy_from_user(&session_data, arg, + sizeof(session_data))) { + dev_err(rtd->dev, "%s: %s: copy_from_user failed\n", + __func__, "LSM_SET_SESSION_DATA"); + return -EFAULT; + } if (prtd) { if (session_data.app_id <= LSM_VOICE_WAKEUP_APP_ID_V2 && session_data.app_id > 0) { @@ -448,12 +453,6 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, break; case SNDRV_LSM_SET_PARAMS: - if (!arg) { - pr_err("%s: %s Invalid argument\n", - __func__, "SNDRV_LSM_SET_PARAMS"); - return -EINVAL; - } - memcpy(&det_params, arg, sizeof(det_params)); if (det_params.num_confidence_levels > @@ -645,40 +644,37 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, break; } case SNDRV_LSM_LAB_CONTROL: { - u32 *enable = NULL; - pr_debug("%s: ioctl %s\n", __func__, "SNDRV_LSM_LAB_CONTROL"); - if (!arg) { - pr_err("%s: Invalid param arg for ioctl %s session %d\n", - __func__, "SNDRV_LSM_LAB_CONTROL", - prtd->lsm_client->session); - rc = -EINVAL; - break; + u32 enable; + + if (copy_from_user(&enable, arg, sizeof(enable))) { + dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n", + __func__, "LSM_LAB_CONTROL"); + return -EFAULT; } - enable = (int *)arg; if (!prtd->lsm_client->started) { - if (prtd->lsm_client->lab_enable == *enable) { + if (prtd->lsm_client->lab_enable == enable) { pr_info("%s: Lab for session %d already %s\n", __func__, prtd->lsm_client->session, - ((*enable) ? "enabled" : "disabled")); + (enable ? "enabled" : "disabled")); rc = 0; break; } - rc = q6lsm_lab_control(prtd->lsm_client, *enable); + rc = q6lsm_lab_control(prtd->lsm_client, enable); if (rc) pr_err("%s: ioctl %s failed rc %d to %s lab for session %d\n", __func__, "SNDRV_LAB_CONTROL", rc, - ((*enable) ? "enable" : "disable"), + (enable ? "enable" : "disable"), prtd->lsm_client->session); else { rc = msm_lsm_lab_buffer_alloc(prtd, - ((*enable) ? LAB_BUFFER_ALLOC + (enable ? LAB_BUFFER_ALLOC : LAB_BUFFER_DEALLOC)); if (rc) pr_err("%s: msm_lsm_lab_buffer_alloc failed rc %d for %s", __func__, rc, - ((*enable) ? "ALLOC" : "DEALLOC")); + (enable ? "ALLOC" : "DEALLOC")); if (!rc) - prtd->lsm_client->lab_enable = *enable; + prtd->lsm_client->lab_enable = enable; } } else { pr_err("%s: ioctl %s issued after start", __func__ @@ -722,11 +718,6 @@ struct snd_lsm_sound_model32 { bool detect_failure; }; -struct snd_lsm_event_status32 { - u16 status; - u16 payload_size; - u8 payload[0]; -}; struct snd_lsm_sound_model_v2_32 { compat_uptr_t data; @@ -747,8 +738,6 @@ struct snd_lsm_detection_params_32 { enum { SNDRV_LSM_REG_SND_MODEL32 = _IOW('U', 0x00, struct snd_lsm_sound_model32), - SNDRV_LSM_EVENT_STATUS32 = - _IOW('U', 0x02, struct snd_lsm_event_status32), SNDRV_LSM_REG_SND_MODEL_V2_32 = _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), SNDRV_LSM_SET_PARAMS_32 = @@ -759,12 +748,14 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; int err = 0; u32 size = 0; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + rtd = substream->private_data; switch (cmd) { case SNDRV_LSM_REG_SND_MODEL32: { struct snd_lsm_sound_model32 snd_model32; @@ -792,12 +783,12 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, } break; } - case SNDRV_LSM_EVENT_STATUS32: { - struct snd_lsm_event_status32 userarg32, *user32 = NULL; - struct snd_lsm_event_status *user = NULL; + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status *user = NULL, userarg32; + struct snd_lsm_event_status *user32 = NULL; if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { pr_err("%s: err copyuser ioctl %s\n", - __func__, "SNDRV_LSM_EVENT_STATUS32"); + __func__, "SNDRV_LSM_EVENT_STATUS"); return -EFAULT; } @@ -907,6 +898,18 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, } break; } + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + break; default: err = msm_lsm_ioctl_shared(substream, cmd, arg); break; @@ -922,35 +925,14 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, { int err = 0; u32 size = 0; - struct snd_lsm_session_data session_data; if (!substream) { pr_err("%s: Invalid params\n", __func__); return -EINVAL; } switch (cmd) { - case SNDRV_LSM_SET_SESSION_DATA: - pr_debug("%s: SNDRV_LSM_SET_SESSION_DATA\n", __func__); - if (copy_from_user(&session_data, (void *)arg, - sizeof(struct snd_lsm_session_data))) { - err = -EFAULT; - pr_err("%s: copy from user failed, size %zd\n", - __func__, sizeof(struct snd_lsm_session_data)); - break; - } - if (!err) - err = msm_lsm_ioctl_shared(substream, - cmd, &session_data); - if (err) - pr_err("%s REG_SND_MODEL failed err %d\n", - __func__, err); - break; case SNDRV_LSM_REG_SND_MODEL_V2: { struct snd_lsm_sound_model_v2 snd_model_v2; - if (!arg) { - pr_err("%s: Invalid params snd_model\n", __func__); - return -EINVAL; - } if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) { err = -EFAULT; pr_err("%s: copy from user failed, size %zd\n", @@ -988,11 +970,6 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, struct snd_lsm_detection_params det_params; pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__); - if (!arg) { - pr_err("%s: %s, Invalid params\n", - __func__, "SNDRV_LSM_SET_PARAMS"); - return -EINVAL; - } if (copy_from_user(&det_params, arg, sizeof(det_params))) { @@ -1014,10 +991,6 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, case SNDRV_LSM_EVENT_STATUS: { struct snd_lsm_event_status *user = NULL, userarg; pr_debug("%s: SNDRV_LSM_EVENT_STATUS\n", __func__); - if (!arg) { - pr_err("%s: Invalid params event status\n", __func__); - return -EINVAL; - } if (copy_from_user(&userarg, arg, sizeof(userarg))) { pr_err("%s: err copyuser event_status\n", __func__); From ba687fdf399cdebad80e7743f9ec371cf1fbe911 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Wed, 11 Jan 2017 10:34:46 +0800 Subject: [PATCH 11/44] ASoC: qdsp6v2: fix potential bug of infinite loop When variable bufsz equals to 0, there would be infinite loop at q6asm_audio_client_buf_alloc. Fix the potential bug by checking bufsz beforehand. CRs-Fixed: 1072280 Change-Id: I13850031e621dff92b9ad759375964b8cc5cce67 Signed-off-by: Xiaojun Sang --- sound/soc/msm/qdsp6v2/q6afe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index ac8d290a3d6..39284040187 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 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 @@ -2528,8 +2528,9 @@ int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir, struct afe_audio_buffer *buf; size_t len; - if (!(ac) || ((dir != IN) && (dir != OUT))) { - pr_err("%s: ac %p dir %d\n", __func__, ac, dir); + if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %p bufsz %d dir %d\n", __func__, ac, bufsz, + dir); return -EINVAL; } From bb851289bc715769784d74bfa0e486b80118edf0 Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Thu, 3 Nov 2016 19:18:07 -0700 Subject: [PATCH 12/44] ASoC: msm: q6dspv2: use correct variable type to store ION buff size The size of the physical memory allocated for ION buffers are of type size_t. Change updates the type of variables sent to ION drivers to size_t to avoid any mismatch. Change-Id: I3d33ed922b979652c64027e6f1c6f0a8ed4850a3 Signed-off-by: Banajit Goswami --- sound/soc/msm/qdsp6v2/q6asm.c | 10 ++++++---- sound/soc/msm/qdsp6v2/q6voice.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 8381b0fbfe8..b748257d7cf 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -2713,7 +2713,8 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, int dir) { struct audio_buffer *buf_circ; - int bytes_to_alloc, rc, len; + int bytes_to_alloc, rc; + size_t len; buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL); @@ -2732,7 +2733,7 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, rc = msm_audio_ion_alloc("audio_client", &buf_circ->client, &buf_circ->handle, bytes_to_alloc, (ion_phys_addr_t *)&buf_circ->phys, - (size_t *)&len, &buf_circ->data); + &len, &buf_circ->data); if (rc) { pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, @@ -2774,7 +2775,8 @@ int q6asm_set_shared_pos_buff(struct audio_client *ac, int dir) { struct audio_buffer *buf_pos = &ac->shared_pos_buf; - int len, rc; + int rc; + size_t len; int bytes_to_alloc = sizeof(struct asm_shared_position_buffer); mutex_lock(&ac->cmd_lock); @@ -2783,7 +2785,7 @@ int q6asm_set_shared_pos_buff(struct audio_client *ac, rc = msm_audio_ion_alloc("audio_client", &buf_pos->client, &buf_pos->handle, bytes_to_alloc, - (ion_phys_addr_t *)&buf_pos->phys, (size_t *)&len, + (ion_phys_addr_t *)&buf_pos->phys, &len, &buf_pos->data); if (rc) { diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h index 99bf206455f..382a71d92b7 100644 --- a/sound/soc/msm/qdsp6v2/q6voice.h +++ b/sound/soc/msm/qdsp6v2/q6voice.h @@ -126,7 +126,7 @@ struct share_mem_buf { struct mem_map_table { dma_addr_t phys; void *data; - uint32_t size; /* size of buffer */ + size_t size; /* size of buffer */ struct ion_handle *handle; struct ion_client *client; }; From 3f593f97627dca80eafa5c0825fbac19f0760cb4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 2 Nov 2016 17:43:51 -0700 Subject: [PATCH 13/44] ion: Fix use after free during ION_IOC_ALLOC If a user happens to call ION_IOC_FREE during an ION_IOC_ALLOC on the just allocated id, and the copy_to_user fails, the cleanup code will attempt to free an already freed handle. This adds a wrapper for ion_alloc that adds an ion_handle_get to avoid this. Bug: 31568617 Change-Id: I476e5bd5372b5178a213f1fea143d270cf9361ed Signed-off-by: Daniel Rosenberg Git-repo: https://android.googlesource.com/kernel/msm/ Git-commit: 20a5411d0115b16826f3d327b6abb0192c8a2001 Signed-off-by: Dennis Cagle --- drivers/staging/android/ion/ion.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 871f216e510..8e235e493f2 100755 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -503,9 +503,9 @@ static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) return 0; } -struct ion_handle *ion_alloc(struct ion_client *client, size_t len, +static struct ion_handle *__ion_alloc(struct ion_client *client, size_t len, size_t align, unsigned int heap_id_mask, - unsigned int flags) + unsigned int flags, bool grab_handle) { struct ion_handle *handle; struct ion_device *dev = client->dev; @@ -605,6 +605,8 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, return handle; mutex_lock(&client->lock); + if (grab_handle) + ion_handle_get(handle); ret = ion_handle_add(client, handle); mutex_unlock(&client->lock); if (ret) { @@ -614,6 +616,13 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, return handle; } + +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int heap_id_mask, + unsigned int flags) +{ + return __ion_alloc(client, len, align, heap_id_mask, flags, false); +} EXPORT_SYMBOL(ion_alloc); static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle) @@ -1450,10 +1459,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct ion_handle *handle; - handle = ion_alloc(client, data.allocation.len, + handle = __ion_alloc(client, data.allocation.len, data.allocation.align, data.allocation.heap_id_mask, - data.allocation.flags); + data.allocation.flags, true); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1530,11 +1539,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (dir & _IOC_READ) { if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { - if (cleanup_handle) + if (cleanup_handle) { ion_free(client, cleanup_handle); + ion_handle_put(cleanup_handle); + } return -EFAULT; } } + if (cleanup_handle) + ion_handle_put(cleanup_handle); return ret; } From d00c57c512e320bbcd569a53cb924fce5a984c8b Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Fri, 8 Jul 2016 14:40:45 -0700 Subject: [PATCH 14/44] qseecom: support whitelist memory for qseecom_send_modfd_cmd qseecom_send_modfd_cmd converts ION buffer's virtual address to scatter gather(SG) list and then sends them to TA by populating SG list into message buffer. As the physical memory address in SG list is used directly by TA, this allows a malicious TA to access/corrupt arbitrary physical memory and may lead to the process gaining kernel/root privileges. Thus, make changes to have the QSEEComm driver passing a list of whitelist buffers that is allowed to be mapped by TA, and the QSEE kernel, in turn, should add checks to the register_shared_buffer syscall to make sure the shared buffers an application is mapping falls within one of these whitelist buffers. CRs-fixed: 1021945 Change-Id: I776ead0030cad167afcf41ab985db7151a42d126 Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 278 ++++++++++++++++++++++++++++++++---- include/soc/qcom/qseecomi.h | 43 +++++- 2 files changed, 292 insertions(+), 29 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index b33e475c386..24c8e4209ba 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -168,6 +168,7 @@ struct qseecom_control { uint32_t qseos_version; uint32_t qsee_version; struct device *pdev; + bool whitelist_support; bool commonlib_loaded; struct ion_handle *cmnlib_ion_handle; struct ce_hw_usage_info ce_info; @@ -210,6 +211,30 @@ struct qseecom_listener_handle { static struct qseecom_control qseecom; +struct sglist_info { + uint32_t indexAndFlags; + uint32_t sizeOrCount; +}; + +/* + * The 31th bit indicates only one or multiple physical address inside + * the request buffer. If it is set, the index locates a single physical addr + * inside the request buffer, and `sizeOrCount` is the size of the memory being + * shared at that physical address. + * Otherwise, the index locates an array of {start, len} pairs (a + * "scatter/gather list"), and `sizeOrCount` gives the number of entries in + * that array. + * + * The 30th bit indicates 64 or 32bit address; when it is set, physical addr + * and scatter gather entry sizes are 64-bit values. Otherwise, 32-bit values. + * + * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer. + */ +#define SGLISTINFO_SET_INDEX_FLAG(c, s, i) \ + ((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff))) + +#define SGLISTINFO_TABLE_SIZE (sizeof(struct sglist_info) * MAX_ION_FD) + struct qseecom_dev_handle { enum qseecom_client_handle_type type; union { @@ -223,6 +248,8 @@ struct qseecom_dev_handle { bool perf_enabled; bool fast_load_enabled; enum qseecom_bandwidth_request_mode mode; + struct sglist_info *sglistinfo_ptr; + uint32_t sglist_cnt; }; struct qseecom_sg_entry { @@ -467,6 +494,22 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, ret = scm_call2(smc_id, &desc); break; } + case QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST: { + struct qseecom_client_send_data_ireq *req; + req = (struct qseecom_client_send_data_ireq *)req_buf; + smc_id = TZ_APP_QSAPP_SEND_DATA_WITH_WHITELIST_ID; + desc.arginfo = + TZ_APP_QSAPP_SEND_DATA_WITH_WHITELIST_ID_PARAM_ID; + desc.args[0] = req->app_id; + desc.args[1] = req->req_ptr; + desc.args[2] = req->req_len; + desc.args[3] = req->rsp_ptr; + desc.args[4] = req->rsp_len; + desc.args[5] = req->sglistinfo_ptr; + desc.args[6] = req->sglistinfo_len; + ret = scm_call2(smc_id, &desc); + break; + } case QSEOS_RPMB_PROVISION_KEY_COMMAND: { struct qseecom_client_send_service_ireq *req; req = (struct qseecom_client_send_service_ireq *) @@ -577,6 +620,22 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, ret = scm_call2(smc_id, &desc); break; } + case QSEOS_TEE_OPEN_SESSION_WHITELIST: { + struct qseecom_qteec_ireq *req; + req = (struct qseecom_qteec_ireq *)req_buf; + smc_id = TZ_APP_GPAPP_OPEN_SESSION_WITH_WHITELIST_ID; + desc.arginfo = + TZ_APP_GPAPP_OPEN_SESSION_WITH_WHITELIST_ID_PARAM_ID; + desc.args[0] = req->app_id; + desc.args[1] = req->req_ptr; + desc.args[2] = req->req_len; + desc.args[3] = req->resp_ptr; + desc.args[4] = req->resp_len; + desc.args[5] = req->sglistinfo_ptr; + desc.args[6] = req->sglistinfo_len; + ret = scm_call2(smc_id, &desc); + break; + } case QSEOS_TEE_INVOKE_COMMAND: { struct qseecom_qteec_ireq *req; req = (struct qseecom_qteec_ireq *)req_buf; @@ -590,6 +649,22 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, ret = scm_call2(smc_id, &desc); break; } + case QSEOS_TEE_INVOKE_COMMAND_WHITELIST: { + struct qseecom_qteec_ireq *req; + req = (struct qseecom_qteec_ireq *)req_buf; + smc_id = TZ_APP_GPAPP_INVOKE_COMMAND_WITH_WHITELIST_ID; + desc.arginfo = + TZ_APP_GPAPP_INVOKE_COMMAND_WITH_WHITELIST_ID_PARAM_ID; + desc.args[0] = req->app_id; + desc.args[1] = req->req_ptr; + desc.args[2] = req->req_len; + desc.args[3] = req->resp_ptr; + desc.args[4] = req->resp_len; + desc.args[5] = req->sglistinfo_ptr; + desc.args[6] = req->sglistinfo_len; + ret = scm_call2(smc_id, &desc); + break; + } case QSEOS_TEE_CLOSE_SESSION: { struct qseecom_qteec_ireq *req; req = (struct qseecom_qteec_ireq *)req_buf; @@ -1996,12 +2071,13 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, { int ret = 0; u32 reqd_len_sb_in = 0; - struct qseecom_client_send_data_ireq send_data_req; + struct qseecom_client_send_data_ireq send_data_req = {0}; struct qseecom_command_scm_resp resp; unsigned long flags; struct qseecom_registered_app_list *ptr_app; bool found_app = false; int name_len = 0; + struct sglist_info *table = data->sglistinfo_ptr; reqd_len_sb_in = req->cmd_req_len + req->resp_len; /* find app_id & img_name from list */ @@ -2025,7 +2101,11 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, return -EINVAL; } - send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND; + if (qseecom.whitelist_support == false) + send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND; + else + send_data_req.qsee_cmd_id = + QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; send_data_req.app_id = data->client.app_id; send_data_req.req_ptr = (uint32_t)(__qseecom_uvirt_to_kphys(data, (uintptr_t)req->cmd_req_buf)); @@ -2033,6 +2113,11 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, send_data_req.rsp_ptr = (uint32_t)(__qseecom_uvirt_to_kphys(data, (uintptr_t)req->resp_buf)); send_data_req.rsp_len = req->resp_len; + send_data_req.sglistinfo_ptr = + (uint32_t)virt_to_phys(table); + send_data_req.sglistinfo_len = SGLISTINFO_TABLE_SIZE; + dmac_flush_range((void *)table, + (void *)table + SGLISTINFO_TABLE_SIZE); msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, @@ -2245,14 +2330,25 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, sg = sg_next(sg); } } - if (cleanup) + if (cleanup) { msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_INV_CACHES); - else + } else { msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_CLEAN_INV_CACHES); + if (data->type == QSEECOM_CLIENT_APP) { + data->sglistinfo_ptr[i].indexAndFlags = + SGLISTINFO_SET_INDEX_FLAG( + (sg_ptr->nents == 1), 0, + req->ifd_data[i].cmd_buf_offset); + data->sglistinfo_ptr[i].sizeOrCount = + (sg_ptr->nents == 1) ? + sg->length : sg_ptr->nents; + data->sglist_cnt = i + 1; + } + } /* Deallocate the handle */ if (!IS_ERR_OR_NULL(ihandle)) ion_free(qseecom.ion_clnt, ihandle); @@ -4496,14 +4592,23 @@ static int __qseecom_update_qteec_req_buf(struct qseecom_qteec_modfd_req *req, else *update = (uint32_t)sg_dma_address(sg_ptr->sgl); - if (cleanup) + if (cleanup) { msm_ion_do_cache_op(qseecom.ion_clnt, - ihandle, NULL, sg->length, - ION_IOC_INV_CACHES); - else + ihandle, NULL, sg->length, + ION_IOC_INV_CACHES); + } else { msm_ion_do_cache_op(qseecom.ion_clnt, - ihandle, NULL, sg->length, - ION_IOC_CLEAN_INV_CACHES); + ihandle, NULL, sg->length, + ION_IOC_CLEAN_INV_CACHES); + data->sglistinfo_ptr[i].indexAndFlags = + SGLISTINFO_SET_INDEX_FLAG( + (sg_ptr->nents == 1), 0, + req->ifd_data[i].cmd_buf_offset); + data->sglistinfo_ptr[i].sizeOrCount = + (sg_ptr->nents == 1) ? + sg->length : sg_ptr->nents; + data->sglist_cnt = i + 1; + } /* Deallocate the handle */ if (!IS_ERR_OR_NULL(ihandle)) ion_free(qseecom.ion_clnt, ihandle); @@ -4522,18 +4627,11 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, struct qseecom_qteec_ireq ireq; int ret = 0; uint32_t reqd_len_sb_in = 0; + struct sglist_info *table = data->sglistinfo_ptr; ret = __qseecom_qteec_validate_msg(data, req); if (ret) return ret; - ireq.qsee_cmd_id = cmd_id; - ireq.app_id = data->client.app_id; - ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->req_ptr); - ireq.req_len = req->req_len; - ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->resp_ptr); - ireq.resp_len = req->resp_len; if ((cmd_id == QSEOS_TEE_OPEN_SESSION) || (cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) { @@ -4542,6 +4640,24 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, if (ret) return ret; } + + if (qseecom.whitelist_support == true + && cmd_id == QSEOS_TEE_OPEN_SESSION) + ireq.qsee_cmd_id = QSEOS_TEE_OPEN_SESSION_WHITELIST; + else + ireq.qsee_cmd_id = cmd_id; + ireq.app_id = data->client.app_id; + ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, + (uintptr_t)req->req_ptr); + ireq.req_len = req->req_len; + ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, + (uintptr_t)req->resp_ptr); + ireq.resp_len = req->resp_len; + ireq.sglistinfo_ptr = (uint32_t)virt_to_phys(table); + ireq.sglistinfo_len = SGLISTINFO_TABLE_SIZE; + dmac_flush_range((void *)table, + (void *)table + SGLISTINFO_TABLE_SIZE); + reqd_len_sb_in = req->req_len + req->resp_len; msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, @@ -4626,6 +4742,9 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data, int ret = 0; int i = 0; uint32_t reqd_len_sb_in = 0; + struct sglist_info *table = data->sglistinfo_ptr; + void *req_ptr = NULL; + void *resp_ptr = NULL; ret = copy_from_user(&req, argp, sizeof(struct qseecom_qteec_modfd_req)); @@ -4637,16 +4756,8 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data, (struct qseecom_qteec_req *)(&req)); if (ret) return ret; - - ireq.qsee_cmd_id = QSEOS_TEE_INVOKE_COMMAND; - ireq.app_id = data->client.app_id; - ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req.req_ptr); - ireq.req_len = req.req_len; - ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req.resp_ptr); - ireq.resp_len = req.resp_len; - reqd_len_sb_in = req.req_len + req.resp_len; + req_ptr = req.req_ptr; + resp_ptr = req.resp_ptr; /* validate offsets */ for (i = 0; i < MAX_ION_FD; i++) { @@ -4662,6 +4773,24 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data, ret = __qseecom_update_qteec_req_buf(&req, data, false); if (ret) return ret; + + if (qseecom.whitelist_support == true) + ireq.qsee_cmd_id = QSEOS_TEE_INVOKE_COMMAND_WHITELIST; + else + ireq.qsee_cmd_id = QSEOS_TEE_INVOKE_COMMAND; + ireq.app_id = data->client.app_id; + ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, + (uintptr_t)req_ptr); + ireq.req_len = req.req_len; + ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, + (uintptr_t)resp_ptr); + ireq.resp_len = req.resp_len; + ireq.sglistinfo_ptr = (uint32_t)virt_to_phys(table); + ireq.sglistinfo_len = SGLISTINFO_TABLE_SIZE; + dmac_flush_range((void *)table, + (void *)table + SGLISTINFO_TABLE_SIZE); + reqd_len_sb_in = req.req_len + req.resp_len; + msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, reqd_len_sb_in, @@ -4717,6 +4846,15 @@ static int qseecom_qteec_request_cancellation(struct qseecom_dev_handle *data, return ret; } +static void __qseecom_clean_data_sglistinfo(struct qseecom_dev_handle *data) +{ + if (data->sglist_cnt) { + memset(data->sglistinfo_ptr, 0, + SGLISTINFO_TABLE_SIZE); + data->sglist_cnt = 0; + } +} + long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) { int ret = 0; @@ -4888,6 +5026,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) mutex_unlock(&app_access_lock); if (ret) pr_err("failed qseecom_send_cmd: %d\n", ret); + __qseecom_clean_data_sglistinfo(data); break; } case QSEECOM_IOCTL_RECEIVE_REQ: { @@ -5266,6 +5405,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) mutex_unlock(&app_access_lock); if (ret) pr_err("failed open_session_cmd: %d\n", ret); + __qseecom_clean_data_sglistinfo(data); break; } case QSEECOM_QTEEC_IOCTL_CLOSE_SESSION_REQ: { @@ -5314,6 +5454,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) mutex_unlock(&app_access_lock); if (ret) pr_err("failed Invoke cmd: %d\n", ret); + __qseecom_clean_data_sglistinfo(data); break; } case QSEECOM_QTEEC_IOCTL_REQUEST_CANCELLATION_REQ: { @@ -5366,6 +5507,9 @@ static int qseecom_open(struct inode *inode, struct file *file) init_waitqueue_head(&data->abort_wq); atomic_set(&data->ioctl_count, 0); + data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL); + if (!(data->sglistinfo_ptr)) + return -ENOMEM; return ret; } @@ -5418,6 +5562,7 @@ static int qseecom_release(struct inode *inode, struct file *file) if (data->perf_enabled == true) qsee_disable_clock_vote(data, CLK_DFAB); } + kfree(data->sglistinfo_ptr); kfree(data); return ret; @@ -5571,6 +5716,74 @@ static void __qseecom_deinit_clk(enum qseecom_ce_hw_instance ce) qclk->instance = CLK_INVALID; } +/* + * Check if whitelist feature is supported by making a test scm_call + * to send a whitelist command to an invalid app ID 0 + */ +static int qseecom_check_whitelist_feature(void) +{ + struct qseecom_client_send_data_ireq send_data_req = {0}; + struct qseecom_client_send_data_64bit_ireq send_data_req_64bit = {0}; + struct qseecom_command_scm_resp resp; + uint32_t buf_size = 128; + void *buf = NULL; + void *cmd_buf = NULL; + size_t cmd_len; + int ret = 0; + phys_addr_t pa; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pa = virt_to_phys(buf); + if (qseecom.qsee_version < QSEE_VERSION_40) { + send_data_req.qsee_cmd_id = + QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; + send_data_req.app_id = 0; + send_data_req.req_ptr = (uint32_t)pa; + send_data_req.req_len = buf_size; + send_data_req.rsp_ptr = (uint32_t)pa; + send_data_req.rsp_len = buf_size; + send_data_req.sglistinfo_ptr = (uint32_t)pa; + send_data_req.sglistinfo_len = buf_size; + cmd_buf = (void *)&send_data_req; + cmd_len = sizeof(struct qseecom_client_send_data_ireq); + } else { + send_data_req_64bit.qsee_cmd_id = + QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; + send_data_req_64bit.app_id = 0; + send_data_req_64bit.req_ptr = (uint64_t)pa; + send_data_req_64bit.req_len = buf_size; + send_data_req_64bit.rsp_ptr = (uint64_t)pa; + send_data_req_64bit.rsp_len = buf_size; + send_data_req_64bit.sglistinfo_ptr = (uint64_t)pa; + send_data_req_64bit.sglistinfo_len = buf_size; + cmd_buf = (void *)&send_data_req_64bit; + cmd_len = sizeof(struct qseecom_client_send_data_64bit_ireq); + } + ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, + cmd_buf, cmd_len, + &resp, sizeof(resp)); +/* + * If this cmd exists and whitelist is supported, scm_call return -2 (scm + * driver remap it to -EINVAL) and resp.result 0xFFFFFFED(-19); Otherwise, + * scm_call return -1 (remap to -EIO). + */ + if (ret == -EIO) { + qseecom.whitelist_support = false; + ret = 0; + } else if (ret == -EINVAL && + resp.result == QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD) { + qseecom.whitelist_support = true; + ret = 0; + } else { + pr_err("Failed to check whitelist: ret = %d, result = 0x%x\n", + ret, resp.result); + } + kfree(buf); + return ret; +} + static int qseecom_probe(struct platform_device *pdev) { int rc; @@ -5605,6 +5818,7 @@ static int qseecom_probe(struct platform_device *pdev) qseecom.ce_ice.ce_core_src_clk = NULL; qseecom.ce_ice.ce_bus_clk = NULL; qseecom.ce_ice.clk_access_cnt = 0; + qseecom.whitelist_support = true; rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV); if (rc < 0) { @@ -5878,6 +6092,14 @@ static int qseecom_probe(struct platform_device *pdev) qseecom.qsee_perf_client = msm_bus_scale_register_client( qseecom_platform_support); + rc = qseecom_check_whitelist_feature(); + if (rc) { + rc = -EINVAL; + goto exit_destroy_ion_client; + } + pr_warn("qseecom.whitelist_support = %d\n", + qseecom.whitelist_support); + if (!qseecom.qsee_perf_client) pr_err("Unable to register bus client\n"); return 0; diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h index f380509a628..e0eb33c2504 100644 --- a/include/soc/qcom/qseecomi.h +++ b/include/soc/qcom/qseecomi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, 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 @@ -18,6 +18,7 @@ #define QSEECOM_KEY_ID_SIZE 32 +#define QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD -19 /*0xFFFFFFED*/ #define QSEOS_RESULT_FAIL_UNSUPPORTED_CE_PIPE -63 #define QSEOS_RESULT_FAIL_KS_OP -64 #define QSEOS_RESULT_FAIL_KEY_ID_EXISTS -65 @@ -61,6 +62,9 @@ enum qseecom_qceos_cmd_id { QSEOS_TEE_INVOKE_MODFD_COMMAND = QSEOS_TEE_INVOKE_COMMAND, QSEOS_TEE_CLOSE_SESSION, QSEOS_TEE_REQUEST_CANCELLATION, + QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST = 0x1C, + QSEOS_TEE_OPEN_SESSION_WHITELIST = 0x1D, + QSEOS_TEE_INVOKE_COMMAND_WHITELIST = 0x1E, QSEOS_FSM_LTE_INIT_DB = 0x100, QSEOS_FSM_LTE_STORE_KENB = 0x101, QSEOS_FSM_LTE_GEN_KEYS = 0x102, @@ -145,6 +149,8 @@ __packed struct qseecom_client_send_data_ireq { uint32_t req_len; uint32_t rsp_ptr;/* First 4 bytes should be the return status */ uint32_t rsp_len; + uint32_t sglistinfo_ptr; + uint32_t sglistinfo_len; }; __packed struct qseecom_reg_log_buf_ireq { @@ -233,6 +239,8 @@ __packed struct qseecom_qteec_ireq { uint32_t req_len; uint32_t resp_ptr; uint32_t resp_len; + uint32_t sglistinfo_ptr; + uint32_t sglistinfo_len; }; __packed struct qseecom_client_send_fsm_key_req { @@ -556,4 +564,37 @@ __packed struct qseecom_client_send_fsm_key_req { TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ TZ_SYSCALL_PARAM_TYPE_VAL) +#define TZ_APP_QSAPP_SEND_DATA_WITH_WHITELIST_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_TZ_APPS, \ + TZ_SVC_APP_ID_PLACEHOLDER, 0x06) + +#define TZ_APP_QSAPP_SEND_DATA_WITH_WHITELIST_ID_PARAM_ID \ + TZ_SYSCALL_CREATE_PARAM_ID_7( \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL) + +#define TZ_APP_GPAPP_OPEN_SESSION_WITH_WHITELIST_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_TZ_APPS, \ + TZ_SVC_APP_ID_PLACEHOLDER, 0x07) + +#define TZ_APP_GPAPP_OPEN_SESSION_WITH_WHITELIST_ID_PARAM_ID \ + TZ_SYSCALL_CREATE_PARAM_ID_7( \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL) + +#define TZ_APP_GPAPP_INVOKE_COMMAND_WITH_WHITELIST_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_TZ_APPS, \ + TZ_SVC_APP_ID_PLACEHOLDER, 0x09) + +#define TZ_APP_GPAPP_INVOKE_COMMAND_WITH_WHITELIST_ID_PARAM_ID \ + TZ_SYSCALL_CREATE_PARAM_ID_7( \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL) + #endif /* __QSEECOMI_H_ */ From 117facea28b08d03db21d5b06909985c4f6e0d8d Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Wed, 7 Sep 2016 16:22:11 +0530 Subject: [PATCH 15/44] qseecom: Change whitelist_support flag to false if TZ failed to check The whitelist status is set default as true though TZ failed to check, which in turn causing the send_command fail by passing whitelist commnd id. So updating the support status flag to false when TZ fails to check. Change-Id: I78a7600506b4d2457bb1c38f8a39888a9cf9467c Signed-off-by: Mallikarjuna Reddy Amireddy Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 24c8e4209ba..d4cb56906c1 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -5508,8 +5508,10 @@ static int qseecom_open(struct inode *inode, struct file *file) atomic_set(&data->ioctl_count, 0); data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL); - if (!(data->sglistinfo_ptr)) + if (!(data->sglistinfo_ptr)) { + kzfree(data); return -ENOMEM; + } return ret; } @@ -5777,8 +5779,10 @@ static int qseecom_check_whitelist_feature(void) qseecom.whitelist_support = true; ret = 0; } else { - pr_err("Failed to check whitelist: ret = %d, result = 0x%x\n", + pr_info("Check whitelist with ret = %d, result = 0x%x\n", ret, resp.result); + qseecom.whitelist_support = false; + ret = 0; } kfree(buf); return ret; From 7fc4dfd2fd895c60b029e55a8f78f548fe3348f0 Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Thu, 1 Sep 2016 10:20:50 -0700 Subject: [PATCH 16/44] qseecom: allocate sglistinfo buffer for kernel clients To support whitelist feature, sglistinfo table should also be allocated from qseecom kernel APIs used by kernel client. Besides, initialize sg in __qseecom_update_cmd_buf_64 to address a static analysis warning. Change-Id: I1f1967fd9e95444cca728f09e3e8f4914b2abb95 Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index d4cb56906c1..21e9fae7533 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -2908,6 +2908,14 @@ int qseecom_start_app(struct qseecom_handle **handle, data->client.user_virt_sb_base = 0; data->client.ihandle = NULL; + /* Allocate sglistinfo buffer for kernel client */ + data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL); + if (!(data->sglistinfo_ptr)) { + kfree(data); + kfree(*handle); + *handle = NULL; + return -ENOMEM; + } init_waitqueue_head(&data->abort_wq); atomic_set(&data->ioctl_count, 0); @@ -2915,6 +2923,7 @@ int qseecom_start_app(struct qseecom_handle **handle, ION_HEAP(ION_QSECOM_HEAP_ID), 0); if (IS_ERR_OR_NULL(data->client.ihandle)) { pr_err("Ion client could not retrieve the handle\n"); + kfree(data->sglistinfo_ptr); kfree(data); kfree(*handle); *handle = NULL; @@ -3024,6 +3033,7 @@ int qseecom_start_app(struct qseecom_handle **handle, return 0; err: + kfree(data->sglistinfo_ptr); kfree(data); kfree(*handle); *handle = NULL; @@ -3085,6 +3095,7 @@ int qseecom_shutdown_app(struct qseecom_handle **handle) atomic_dec(&data->ioctl_count); mutex_unlock(&app_access_lock); if (ret == 0) { + kzfree(data->sglistinfo_ptr); kzfree(data); kzfree(*handle); kzfree(kclient); From 0ce9fe05b85371c73369fdea621def1ca2bb9921 Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Fri, 2 Sep 2016 22:09:23 -0700 Subject: [PATCH 17/44] qseecom: whitelist support for kernel client and listener Add whitelist support for listener to send modified resp to TZ; also add whitelist support for kernel client; and change the method to check whitelist feature. Change-Id: I0030b0008d6224cda3fdc1f80308a7e9bcfe4405 Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 219 +++++++++++++++++------------------- include/soc/qcom/qseecomi.h | 11 ++ 2 files changed, 112 insertions(+), 118 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 21e9fae7533..8d134664157 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -109,6 +109,35 @@ static DEFINE_MUTEX(qsee_bw_mutex); static DEFINE_MUTEX(app_access_lock); static DEFINE_MUTEX(clk_access_lock); +struct sglist_info { + uint32_t indexAndFlags; + uint32_t sizeOrCount; +}; + +/* + * The 31th bit indicates only one or multiple physical address inside + * the request buffer. If it is set, the index locates a single physical addr + * inside the request buffer, and `sizeOrCount` is the size of the memory being + * shared at that physical address. + * Otherwise, the index locates an array of {start, len} pairs (a + * "scatter/gather list"), and `sizeOrCount` gives the number of entries in + * that array. + * + * The 30th bit indicates 64 or 32bit address; when it is set, physical addr + * and scatter gather entry sizes are 64-bit values. Otherwise, 32-bit values. + * + * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer. + */ +#define SGLISTINFO_SET_INDEX_FLAG(c, s, i) \ + ((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff))) + +#define SGLISTINFO_TABLE_SIZE (sizeof(struct sglist_info) * MAX_ION_FD) + +#define FEATURE_ID_WHITELIST 15 /*whitelist feature id*/ + +#define MAKE_WHITELIST_VERSION(major, minor, patch) \ + (((major & 0x3FF) << 22) | ((minor & 0x3FF) << 12) | (patch & 0xFFF)) + struct qseecom_registered_listener_list { struct list_head list; struct qseecom_register_listener_req svc; @@ -120,6 +149,8 @@ struct qseecom_registered_listener_list { wait_queue_head_t rcv_req_wq; int rcv_req_flag; + struct sglist_info sglistinfo_ptr[MAX_ION_FD]; + uint32_t sglist_cnt; }; struct qseecom_registered_app_list { @@ -211,30 +242,6 @@ struct qseecom_listener_handle { static struct qseecom_control qseecom; -struct sglist_info { - uint32_t indexAndFlags; - uint32_t sizeOrCount; -}; - -/* - * The 31th bit indicates only one or multiple physical address inside - * the request buffer. If it is set, the index locates a single physical addr - * inside the request buffer, and `sizeOrCount` is the size of the memory being - * shared at that physical address. - * Otherwise, the index locates an array of {start, len} pairs (a - * "scatter/gather list"), and `sizeOrCount` gives the number of entries in - * that array. - * - * The 30th bit indicates 64 or 32bit address; when it is set, physical addr - * and scatter gather entry sizes are 64-bit values. Otherwise, 32-bit values. - * - * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer. - */ -#define SGLISTINFO_SET_INDEX_FLAG(c, s, i) \ - ((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff))) - -#define SGLISTINFO_TABLE_SIZE (sizeof(struct sglist_info) * MAX_ION_FD) - struct qseecom_dev_handle { enum qseecom_client_handle_type type; union { @@ -248,8 +255,9 @@ struct qseecom_dev_handle { bool perf_enabled; bool fast_load_enabled; enum qseecom_bandwidth_request_mode mode; - struct sglist_info *sglistinfo_ptr; + struct sglist_info sglistinfo_ptr[MAX_ION_FD]; uint32_t sglist_cnt; + bool use_legacy_cmd; }; struct qseecom_sg_entry { @@ -463,6 +471,21 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, ret = scm_call2(smc_id, &desc); break; } + case QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST: { + struct qseecom_client_listener_data_irsp *req; + req = (struct qseecom_client_listener_data_irsp *) + req_buf; + smc_id = + TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID; + desc.arginfo = + TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_PARAM_ID; + desc.args[0] = req->listener_id; + desc.args[1] = req->status; + desc.args[2] = req->sglistinfo_ptr; + desc.args[3] = req->sglistinfo_len; + ret = scm_call2(smc_id, &desc); + break; + } case QSEOS_LOAD_EXTERNAL_ELF_COMMAND: { struct qseecom_load_app_ireq *req; req = (struct qseecom_load_app_ireq *)req_buf; @@ -849,7 +872,7 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data, return -EBUSY; } - new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); if (!new_entry) { pr_err("kmalloc failed\n"); return -ENOMEM; @@ -1261,6 +1284,16 @@ static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data) return ret || data->abort; } +static void __qseecom_clean_listener_sglistinfo( + struct qseecom_registered_listener_list *ptr_svc) +{ + if (ptr_svc->sglist_cnt) { + memset(ptr_svc->sglistinfo_ptr, 0, + SGLISTINFO_TABLE_SIZE); + ptr_svc->sglist_cnt = 0; + } +} + static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, struct qseecom_command_scm_resp *resp) { @@ -1272,6 +1305,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, struct qseecom_registered_listener_list *ptr_svc = NULL; sigset_t new_sigset; sigset_t old_sigset; + struct sglist_info *table = NULL; while (resp->result == QSEOS_RESULT_INCOMPLETE) { lstnr = resp->data; @@ -1330,8 +1364,21 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, } qseecom.send_resp_flag = 0; - send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND; + table = ptr_svc->sglistinfo_ptr; + if (qseecom.whitelist_support == false) + send_data_rsp.qsee_cmd_id = + QSEOS_LISTENER_DATA_RSP_COMMAND; + else + send_data_rsp.qsee_cmd_id = + QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST; + send_data_rsp.listener_id = lstnr; + send_data_rsp.sglistinfo_ptr = + (uint32_t)virt_to_phys(table); + send_data_rsp.sglistinfo_len = + SGLISTINFO_TABLE_SIZE; + dmac_flush_range((void *)table, + (void *)table + SGLISTINFO_TABLE_SIZE); if (ptr_svc) msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle, ptr_svc->sb_virt, ptr_svc->sb_length, @@ -1344,6 +1391,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, (const void *)&send_data_rsp, sizeof(send_data_rsp), resp, sizeof(*resp)); + __qseecom_clean_listener_sglistinfo(ptr_svc); if (ret) { pr_err("scm_call() failed with err: %d (app_id = %d)\n", ret, data->client.app_id); @@ -2101,11 +2149,11 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, return -EINVAL; } - if (qseecom.whitelist_support == false) + if (qseecom.whitelist_support == false || data->use_legacy_cmd == true) send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND; else send_data_req.qsee_cmd_id = - QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; + QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; send_data_req.app_id = data->client.app_id; send_data_req.req_ptr = (uint32_t)(__qseecom_uvirt_to_kphys(data, (uintptr_t)req->cmd_req_buf)); @@ -2213,6 +2261,8 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, struct qseecom_send_modfd_cmd_req *req = NULL; struct qseecom_send_modfd_listener_resp *lstnr_resp = NULL; struct qseecom_registered_listener_list *this_lstnr = NULL; + uint32_t offset; + struct sg_table *sg_ptr; if ((data->type != QSEECOM_LISTENER_SERVICE) && (data->type != QSEECOM_CLIENT_APP)) @@ -2234,7 +2284,6 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, } for (i = 0; i < MAX_ION_FD; i++) { - struct sg_table *sg_ptr = NULL; if ((data->type != QSEECOM_LISTENER_SERVICE) && (req->ifd_data[i].fd > 0)) { ihandle = ion_import_dma_buf(qseecom.ion_clnt, @@ -2339,14 +2388,25 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, ihandle, NULL, len, ION_IOC_CLEAN_INV_CACHES); if (data->type == QSEECOM_CLIENT_APP) { + offset = req->ifd_data[i].cmd_buf_offset; data->sglistinfo_ptr[i].indexAndFlags = SGLISTINFO_SET_INDEX_FLAG( - (sg_ptr->nents == 1), 0, - req->ifd_data[i].cmd_buf_offset); + (sg_ptr->nents == 1), 0, offset); data->sglistinfo_ptr[i].sizeOrCount = (sg_ptr->nents == 1) ? sg->length : sg_ptr->nents; data->sglist_cnt = i + 1; + } else { + offset = (lstnr_resp->ifd_data[i].cmd_buf_offset + + (uintptr_t)lstnr_resp->resp_buf_ptr - + (uintptr_t)this_lstnr->sb_virt); + this_lstnr->sglistinfo_ptr[i].indexAndFlags = + SGLISTINFO_SET_INDEX_FLAG( + (sg_ptr->nents == 1), 0, offset); + this_lstnr->sglistinfo_ptr[i].sizeOrCount = + (sg_ptr->nents == 1) ? + sg->length : sg_ptr->nents; + this_lstnr->sglist_cnt = i + 1; } } /* Deallocate the handle */ @@ -2908,14 +2968,6 @@ int qseecom_start_app(struct qseecom_handle **handle, data->client.user_virt_sb_base = 0; data->client.ihandle = NULL; - /* Allocate sglistinfo buffer for kernel client */ - data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL); - if (!(data->sglistinfo_ptr)) { - kfree(data); - kfree(*handle); - *handle = NULL; - return -ENOMEM; - } init_waitqueue_head(&data->abort_wq); atomic_set(&data->ioctl_count, 0); @@ -2923,7 +2975,6 @@ int qseecom_start_app(struct qseecom_handle **handle, ION_HEAP(ION_QSECOM_HEAP_ID), 0); if (IS_ERR_OR_NULL(data->client.ihandle)) { pr_err("Ion client could not retrieve the handle\n"); - kfree(data->sglistinfo_ptr); kfree(data); kfree(*handle); *handle = NULL; @@ -3033,7 +3084,6 @@ int qseecom_start_app(struct qseecom_handle **handle, return 0; err: - kfree(data->sglistinfo_ptr); kfree(data); kfree(*handle); *handle = NULL; @@ -3095,7 +3145,6 @@ int qseecom_shutdown_app(struct qseecom_handle **handle) atomic_dec(&data->ioctl_count); mutex_unlock(&app_access_lock); if (ret == 0) { - kzfree(data->sglistinfo_ptr); kzfree(data); kzfree(*handle); kzfree(kclient); @@ -3158,8 +3207,11 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf, } perf_enabled = true; } + if (!strcmp(data->client.app_name, "securemm")) + data->use_legacy_cmd = true; ret = __qseecom_send_cmd(data, &req); + data->use_legacy_cmd = false; if (qseecom.support_bus_scaling) __qseecom_add_bw_scale_down_timer( QSEECOM_SEND_CMD_CRYPTO_TIMEOUT); @@ -5392,6 +5444,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) wake_up_all(&data->abort_wq); if (ret) pr_err("failed qseecom_send_mod_resp: %d\n", ret); + __qseecom_clean_data_sglistinfo(data); break; } case QSEECOM_QTEEC_IOCTL_OPEN_SESSION_REQ: { @@ -5517,12 +5570,6 @@ static int qseecom_open(struct inode *inode, struct file *file) data->mode = INACTIVE; init_waitqueue_head(&data->abort_wq); atomic_set(&data->ioctl_count, 0); - - data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL); - if (!(data->sglistinfo_ptr)) { - kzfree(data); - return -ENOMEM; - } return ret; } @@ -5575,7 +5622,6 @@ static int qseecom_release(struct inode *inode, struct file *file) if (data->perf_enabled == true) qsee_disable_clock_vote(data, CLK_DFAB); } - kfree(data->sglistinfo_ptr); kfree(data); return ret; @@ -5730,73 +5776,14 @@ static void __qseecom_deinit_clk(enum qseecom_ce_hw_instance ce) } /* - * Check if whitelist feature is supported by making a test scm_call - * to send a whitelist command to an invalid app ID 0 + * Check whitelist feature, and if TZ feature version is < 1.0.0, + * then whitelist feature is not supported. */ static int qseecom_check_whitelist_feature(void) { - struct qseecom_client_send_data_ireq send_data_req = {0}; - struct qseecom_client_send_data_64bit_ireq send_data_req_64bit = {0}; - struct qseecom_command_scm_resp resp; - uint32_t buf_size = 128; - void *buf = NULL; - void *cmd_buf = NULL; - size_t cmd_len; - int ret = 0; - phys_addr_t pa; + int version = scm_get_feat_version(FEATURE_ID_WHITELIST); - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - pa = virt_to_phys(buf); - if (qseecom.qsee_version < QSEE_VERSION_40) { - send_data_req.qsee_cmd_id = - QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; - send_data_req.app_id = 0; - send_data_req.req_ptr = (uint32_t)pa; - send_data_req.req_len = buf_size; - send_data_req.rsp_ptr = (uint32_t)pa; - send_data_req.rsp_len = buf_size; - send_data_req.sglistinfo_ptr = (uint32_t)pa; - send_data_req.sglistinfo_len = buf_size; - cmd_buf = (void *)&send_data_req; - cmd_len = sizeof(struct qseecom_client_send_data_ireq); - } else { - send_data_req_64bit.qsee_cmd_id = - QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST; - send_data_req_64bit.app_id = 0; - send_data_req_64bit.req_ptr = (uint64_t)pa; - send_data_req_64bit.req_len = buf_size; - send_data_req_64bit.rsp_ptr = (uint64_t)pa; - send_data_req_64bit.rsp_len = buf_size; - send_data_req_64bit.sglistinfo_ptr = (uint64_t)pa; - send_data_req_64bit.sglistinfo_len = buf_size; - cmd_buf = (void *)&send_data_req_64bit; - cmd_len = sizeof(struct qseecom_client_send_data_64bit_ireq); - } - ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, - cmd_buf, cmd_len, - &resp, sizeof(resp)); -/* - * If this cmd exists and whitelist is supported, scm_call return -2 (scm - * driver remap it to -EINVAL) and resp.result 0xFFFFFFED(-19); Otherwise, - * scm_call return -1 (remap to -EIO). - */ - if (ret == -EIO) { - qseecom.whitelist_support = false; - ret = 0; - } else if (ret == -EINVAL && - resp.result == QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD) { - qseecom.whitelist_support = true; - ret = 0; - } else { - pr_info("Check whitelist with ret = %d, result = 0x%x\n", - ret, resp.result); - qseecom.whitelist_support = false; - ret = 0; - } - kfree(buf); - return ret; + return version >= MAKE_WHITELIST_VERSION(1, 0, 0); } static int qseecom_probe(struct platform_device *pdev) @@ -6107,11 +6094,7 @@ static int qseecom_probe(struct platform_device *pdev) qseecom.qsee_perf_client = msm_bus_scale_register_client( qseecom_platform_support); - rc = qseecom_check_whitelist_feature(); - if (rc) { - rc = -EINVAL; - goto exit_destroy_ion_client; - } + qseecom.whitelist_support = qseecom_check_whitelist_feature(); pr_warn("qseecom.whitelist_support = %d\n", qseecom.whitelist_support); diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h index e0eb33c2504..9357e02529d 100644 --- a/include/soc/qcom/qseecomi.h +++ b/include/soc/qcom/qseecomi.h @@ -65,6 +65,7 @@ enum qseecom_qceos_cmd_id { QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST = 0x1C, QSEOS_TEE_OPEN_SESSION_WHITELIST = 0x1D, QSEOS_TEE_INVOKE_COMMAND_WHITELIST = 0x1E, + QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST = 0x1F, QSEOS_FSM_LTE_INIT_DB = 0x100, QSEOS_FSM_LTE_STORE_KENB = 0x101, QSEOS_FSM_LTE_GEN_KEYS = 0x102, @@ -164,6 +165,8 @@ __packed struct qseecom_client_listener_data_irsp { uint32_t qsee_cmd_id; uint32_t listener_id; uint32_t status; + uint32_t sglistinfo_ptr; + uint32_t sglistinfo_len; }; /* @@ -597,4 +600,12 @@ __packed struct qseecom_client_send_fsm_key_req { TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ TZ_SYSCALL_PARAM_TYPE_VAL) +#define TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID \ + TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x05) + +#define TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_PARAM_ID \ + TZ_SYSCALL_CREATE_PARAM_ID_4( \ + TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_VAL, \ + TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL) + #endif /* __QSEECOMI_H_ */ From f8304044adf2ece275993edfac0d8a65e14c605d Mon Sep 17 00:00:00 2001 From: Conner Huff Date: Wed, 25 Jan 2017 18:00:08 -0800 Subject: [PATCH 18/44] net: rmnet_data: Fix incorrect netlink handling rmnet_data netlink handler currently does not check for the incoming process pid and instead just loops back the pid. A malicious root user could potentially send a message with source pid 0 and this could cause rmnet_data to loop the message back till an out of memory situation occurs. rmnet_data also does not check for the message length of the incoming netlink messages and instead casts the netlink message without checking for the boundary. Fix these two scenarios by adding the pid and message length checks respectively. Bug: 31252965 CRs-Fixed: 1098801 Change-Id: I172c1a7112e67e82959b397af7ddfd963d819bdc Signed-off-by: Conner Huff --- net/rmnet_data/rmnet_data_config.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c index 6173d0bcc08..fdb609b414a 100644 --- a/net/rmnet_data/rmnet_data_config.c +++ b/net/rmnet_data/rmnet_data_config.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-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 @@ -530,6 +530,11 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb) nlmsg_header = (struct nlmsghdr *) skb->data; rmnet_header = (struct rmnet_nl_msg_s *) nlmsg_data(nlmsg_header); + if (!nlmsg_header->nlmsg_pid || + (nlmsg_header->nlmsg_len < sizeof(struct nlmsghdr) + + sizeof(struct rmnet_nl_msg_s))) + return; + LOGL("Netlink message pid=%d, seq=%d, length=%d, rmnet_type=%d", nlmsg_header->nlmsg_pid, nlmsg_header->nlmsg_seq, From 8e4d93400b69c4cd31b692668e9d5e21d666ff12 Mon Sep 17 00:00:00 2001 From: Karthik Reddy Katta Date: Tue, 3 Jan 2017 11:09:05 +0530 Subject: [PATCH 19/44] drivers: soc: qcom: Add overflow check in ADM driver Add overflow check for param length to prevent heap overflow while allocating memory for ADM parameters. CRs-Fixed: 1103334 Change-Id: I1163aeaabbc84dba4cd0739a35ecbdee18b42717 Signed-off-by: Karthik Reddy Katta --- sound/soc/msm/qdsp6v2/q6adm.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index 0b159eec706..4b50471847c 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 2016, 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 @@ -991,9 +991,10 @@ int adm_get_params(int port_id, int copp_idx, uint32_t module_id, uint32_t param_id, uint32_t params_length, char *params) { struct adm_cmd_get_pp_params_v5 *adm_params = NULL; - int sz, rc = 0, i = 0; + int rc = 0, i = 0; int port_idx, idx; int *params_data = (int *)params; + uint64_t sz = 0; port_id = afe_convert_virtual_to_portid(port_id); port_idx = adm_validate_and_get_port_index(port_id); @@ -1002,7 +1003,16 @@ int adm_get_params(int port_id, int copp_idx, uint32_t module_id, return -EINVAL; } - sz = sizeof(struct adm_cmd_get_pp_params_v5) + params_length; + sz = (uint64_t)sizeof(struct adm_cmd_get_pp_params_v5) + + (uint64_t)params_length; + /* + * Check if the value of "sz" (which is ultimately assigned to + * "hdr.pkt_size") crosses U16_MAX. + */ + if (sz > U16_MAX) { + pr_err("%s: Invalid params_length\n", __func__); + return -EINVAL; + } adm_params = kzalloc(sz, GFP_KERNEL); if (!adm_params) { pr_err("%s: adm params memory alloc failed", __func__); From 85b42e10c47ac354f892f15dfdea254b9ece76da Mon Sep 17 00:00:00 2001 From: Sharad Sangle Date: Mon, 19 Dec 2016 17:00:25 +0530 Subject: [PATCH 20/44] ASoC: msm: qdsp6v2: DAP: Add check to validate param length To avoid buffer overflow, validate input length used to set Dolby params. Change-Id: I3f9d6040f118f63b60c20c83b0d8cae638f4a530 CRs-Fixed: 1095947 Signed-off-by: Sharad Sangle --- sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c index e894093c26a..54788bd0cb9 100644 --- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c +++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c @@ -681,7 +681,7 @@ int msm_dolby_dap_param_to_set_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int rc = 0, port_id, copp_idx; - uint32_t idx, j; + uint32_t idx, j, current_offset; uint32_t device = ucontrol->value.integer.value[0]; uint32_t param_id = ucontrol->value.integer.value[1]; uint32_t offset = ucontrol->value.integer.value[2]; @@ -758,6 +758,19 @@ int msm_dolby_dap_param_to_set_control_put(struct snd_kcontrol *kcontrol, default: { /* cache the parameters */ dolby_dap_params_modified[idx] += 1; + current_offset = dolby_dap_params_offset[idx] + offset; + if (current_offset >= TOTAL_LENGTH_DOLBY_PARAM) { + pr_err("%s: invalid offset %d at idx %d\n", + __func__, offset, idx); + return -EINVAL; + } + if ((0 == length) || (current_offset + length - 1 + < current_offset) || (current_offset + length + > TOTAL_LENGTH_DOLBY_PARAM)) { + pr_err("%s: invalid length %d at idx %d\n", + __func__, length, idx); + return -EINVAL; + } dolby_dap_params_length[idx] = length; pr_debug("%s: param recvd deviceId=0x%x paramId=0x%x offset=%d length=%d\n", __func__, device, param_id, offset, length); From b6a39325ca288a91733ef0677c026dd469e3f27d Mon Sep 17 00:00:00 2001 From: Andrey Markovytch Date: Mon, 23 Jan 2017 10:22:58 +0200 Subject: [PATCH 21/44] security: switched to stackable model for PFT/PFK module Moved hooks from SELINUX framework to general SECURITY framework Change-Id: I37e701b4925c4993f724c32b258c5088f4dcbe4d Signed-off-by: Andrey Markovytch Signed-off-by: Yang Guang --- drivers/platform/msm/pft.c | 8 +++-- security/selinux/hooks.c | 68 ++------------------------------------ 2 files changed, 8 insertions(+), 68 deletions(-) diff --git a/drivers/platform/msm/pft.c b/drivers/platform/msm/pft.c index 47f1ace0eba..15b8c3eaa78 100644 --- a/drivers/platform/msm/pft.c +++ b/drivers/platform/msm/pft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -217,12 +217,14 @@ static int __init pft_lsm_init(struct pft_device *dev) ret = register_security(&pft_security_ops); if (ret) { pr_err("pft lsm registeration failed, ret=%d.\n", ret); - return 0; + return ret; } + dev->is_chosen_lsm = true; pr_debug("pft is the chosen lsm, registered sucessfully !\n"); } else { - pr_debug("pft is not the chosen lsm.\n"); + pr_err("pft is not the chosen lsm.\n"); + return -ENODEV; } return 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 66c4482a1d0..7ff15d5bfe8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -84,7 +84,6 @@ #include #include #include -#include #include "avc.h" @@ -1677,14 +1676,8 @@ static int may_create(struct inode *dir, return rc; return avc_has_perm(newsid, sbsec->sid, - SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, &ad); - if (rc) - return rc; - - rc = pft_inode_mknod(dir, dentry, 0, 0); - - return rc; + SECCLASS_FILESYSTEM, + FILESYSTEM__ASSOCIATE, &ad); } /* Check whether a task can create a key. */ @@ -1740,14 +1733,7 @@ static int may_link(struct inode *dir, return 0; } - rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); - if (rc) - return rc; - - if (kind == MAY_UNLINK) - rc = pft_inode_unlink(dir, dentry); - - return rc; + return avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); } static inline int may_rename(struct inode *old_dir, @@ -2646,24 +2632,9 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode) { - int ret; - - ret = pft_inode_create(dir, dentry, mode); - if (ret < 0) - return ret; return may_create(dir, dentry, SECCLASS_FILE); } -static int selinux_inode_post_create(struct inode *dir, struct dentry *dentry, - umode_t mode) -{ - int ret; - - ret = pft_inode_post_create(dir, dentry, mode); - - return ret; -} - static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { return may_link(dir, old_dentry, MAY_LINK); @@ -2697,12 +2668,6 @@ static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { - int rc; - - rc = pft_inode_rename(old_inode, old_dentry, new_inode, new_dentry); - if (rc) - return rc; - return may_rename(old_inode, old_dentry, new_inode, new_dentry); } @@ -2821,9 +2786,6 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) { const struct cred *cred = current_cred(); - if (pft_inode_set_xattr(dentry, name, NULL, 0, 0) < 0) - return -EACCES; - if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof XATTR_SECURITY_PREFIX - 1)) { if (!strcmp(name, XATTR_NAME_CAPS)) { @@ -3067,16 +3029,11 @@ static int selinux_file_permission(struct file *file, int mask) struct file_security_struct *fsec = file->f_security; struct inode_security_struct *isec = inode->i_security; u32 sid = current_sid(); - int ret; if (!mask) /* No permission to check. Existence test. */ return 0; - ret = pft_file_permission(file, mask); - if (ret < 0) - return ret; - if (sid == fsec->sid && fsec->isid == isec->sid && fsec->pseqno == avc_policy_seqno()) /* No change since file_open check. */ @@ -3370,11 +3327,6 @@ static int selinux_file_open(struct file *file, const struct cred *cred) { struct file_security_struct *fsec; struct inode_security_struct *isec; - int ret; - - ret = pft_file_open(file, cred); - if (ret < 0) - return ret; fsec = file->f_security; isec = file_inode(file)->i_security; @@ -3398,17 +3350,6 @@ static int selinux_file_open(struct file *file, const struct cred *cred) return path_has_perm(cred, &file->f_path, open_file_to_av(file)); } -static int selinux_file_close(struct file *file) -{ - return pft_file_close(file); -} - - -static bool selinux_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return pft_allow_merge_bio(bio1, bio2); -} - /* task security operations */ static int selinux_task_create(unsigned long clone_flags) @@ -5811,7 +5752,6 @@ static struct security_operations selinux_ops = { .inode_free_security = selinux_inode_free_security, .inode_init_security = selinux_inode_init_security, .inode_create = selinux_inode_create, - .inode_post_create = selinux_inode_post_create, .inode_link = selinux_inode_link, .inode_unlink = selinux_inode_unlink, .inode_symlink = selinux_inode_symlink, @@ -5848,8 +5788,6 @@ static struct security_operations selinux_ops = { .file_receive = selinux_file_receive, .file_open = selinux_file_open, - .file_close = selinux_file_close, - .allow_merge_bio = selinux_allow_merge_bio, .task_create = selinux_task_create, .cred_alloc_blank = selinux_cred_alloc_blank, From d6acb5e9826577a1c190e996778f317ab9bf3bc3 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 19 Dec 2016 18:38:53 -0800 Subject: [PATCH 22/44] ASoC: msm: qdsp6v2: Add range checking in msm_dai_q6_set_channel_map Range checking is added to prevent buffer overflow that due to inputs can be set by user space. CRs-Fixed: 1098363 Change-Id: I057261291806240ee6d7b8106a5e83a7665e013d Signed-off-by: Xiaoyu Ye --- sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 1314b0cdb10..22b9e4b48ea 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 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 @@ -1277,6 +1277,11 @@ static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai, pr_err("%s: rx slot not found\n", __func__); return -EINVAL; } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + return -EINVAL; + } + for (i = 0; i < rx_num; i++) { dai_data->port_config.slim_sch.shared_ch_mapping[i] = rx_slot[i]; @@ -1307,6 +1312,11 @@ static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai, pr_err("%s: tx slot not found\n", __func__); return -EINVAL; } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + return -EINVAL; + } + for (i = 0; i < tx_num; i++) { dai_data->port_config.slim_sch.shared_ch_mapping[i] = tx_slot[i]; From ae4384b942403bc0efdd6b59054943b13776d503 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 20 Dec 2016 10:56:59 -0800 Subject: [PATCH 23/44] mfd: wcd9xxx: Add range checking in function wcd9xxx_init_slimslave Range checking is added to prevent buffer overflow. CRs-Fixed: 1098363 Change-Id: I5871a3a11ec5f5106a386bf149d7ec22605f3db8 Signed-off-by: Xiaoyu Ye --- drivers/mfd/wcd9xxx-slimslave.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c index 1480d8b7df1..5c2a124a2d0 100644 --- a/drivers/mfd/wcd9xxx-slimslave.c +++ b/drivers/mfd/wcd9xxx-slimslave.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 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 @@ -87,6 +87,10 @@ int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la, goto err; } + if (!rx_num || rx_num > wcd9xxx->num_rx_port) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + return -EINVAL; + } if (wcd9xxx->rx_chs) { wcd9xxx->num_rx_port = rx_num; for (i = 0; i < rx_num; i++) { @@ -109,6 +113,10 @@ int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la, wcd9xxx->num_rx_port); } + if (!tx_num || tx_num > wcd9xxx->num_tx_port) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + return -EINVAL; + } if (wcd9xxx->tx_chs) { wcd9xxx->num_tx_port = tx_num; for (i = 0; i < tx_num; i++) { From 03d7b3bdf995702ccf504def00817ca3b877cba1 Mon Sep 17 00:00:00 2001 From: Sathish Ambley Date: Tue, 13 Dec 2016 15:27:30 -0800 Subject: [PATCH 24/44] msm: ADSPRPC: Buffer length to be copied is truncated The buffer length that is being used to allocate gets truncated due to it being assigned to wrong type causing a much smaller buffer to be allocated than what is required for copying. Change-Id: I30818acd42bd282837c7c7aa16d56d3b95d4dfe7 Signed-off-by: Sathish Ambley --- drivers/char/adsprpc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 3a08b6cb2f7..b6ccd4105b2 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -835,9 +835,9 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx, void *args; remote_arg_t *pra = ctx->pra; remote_arg_t *rpra = ctx->rpra; - ssize_t rlen, used, size; + ssize_t rlen, used, size, copylen = 0; uint32_t sc = ctx->sc, start; - int i, inh, bufs = 0, err = 0, oix, copylen = 0; + int i, inh, bufs = 0, err = 0, oix; int inbufs = REMOTE_SCALARS_INBUFS(sc); int outbufs = REMOTE_SCALARS_OUTBUFS(sc); int cid = ctx->fdata->cid; @@ -886,13 +886,23 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx, /* calculate len requreed for copying */ for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; + uintptr_t mstart, mend; + if (!pra[i].buf.len) continue; if (list[i].num) continue; if (ctx->overps[oix]->offset == 0) copylen = ALIGN(copylen, BALIGN); - copylen += ctx->overps[oix]->mend - ctx->overps[oix]->mstart; + mstart = ctx->overps[oix]->mstart; + mend = ctx->overps[oix]->mend; + VERIFY(err, (mend - mstart) <= LONG_MAX); + if (err) + goto bail; + copylen += mend - mstart; + VERIFY(err, copylen >= 0); + if (err) + goto bail; } /* alocate new buffer */ @@ -918,7 +928,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx, /* copy non ion buffers */ for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; - int mlen = ctx->overps[oix]->mend - ctx->overps[oix]->mstart; + ssize_t mlen = ctx->overps[oix]->mend - ctx->overps[oix]->mstart; if (!pra[i].buf.len) continue; if (list[i].num) From 2d04b64335e5d48b6e96bf3d672aa02a92f45ef9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 26 Oct 2016 15:01:54 +0100 Subject: [PATCH 25/44] KEYS: Fix short sprintf buffer in /proc/keys show function This fixes CVE-2016-7042. Fix a short sprintf buffer in proc_keys_show(). If the gcc stack protector is turned on, this can cause a panic due to stack corruption. The problem is that xbuf[] is not big enough to hold a 64-bit timeout rendered as weeks: (gdb) p 0xffffffffffffffffULL/(60*60*24*7) $2 = 30500568904943 That's 14 chars plus NUL, not 11 chars plus NUL. Expand the buffer to 16 chars. I think the unpatched code apparently works if the stack-protector is not enabled because on a 32-bit machine the buffer won't be overflowed and on a 64-bit machine there's a 64-bit aligned pointer at one side and an int that isn't checked again on the other side. The panic incurred looks something like: Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffffff81352ebe CPU: 0 PID: 1692 Comm: reproducer Not tainted 4.7.2-201.fc24.x86_64 #1 Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 0000000000000086 00000000fbbd2679 ffff8800a044bc00 ffffffff813d941f ffffffff81a28d58 ffff8800a044bc98 ffff8800a044bc88 ffffffff811b2cb6 ffff880000000010 ffff8800a044bc98 ffff8800a044bc30 00000000fbbd2679 Call Trace: [] dump_stack+0x63/0x84 [] panic+0xde/0x22a [] ? proc_keys_show+0x3ce/0x3d0 [] __stack_chk_fail+0x19/0x30 [] proc_keys_show+0x3ce/0x3d0 [] ? key_validate+0x50/0x50 [] ? key_default_cmp+0x20/0x20 [] seq_read+0x2cc/0x390 [] proc_reg_read+0x42/0x70 [] __vfs_read+0x37/0x150 [] ? security_file_permission+0xa0/0xc0 [] vfs_read+0x96/0x130 [] SyS_read+0x55/0xc0 [] entry_SYSCALL_64_fastpath+0x1a/0xa4 Change-Id: I38877fcf9eb3c1204588cdab1e47141c30f373b4 Reported-by: Ondrej Kozina Signed-off-by: David Howells Tested-by: Ondrej Kozina cc: stable@vger.kernel.org Signed-off-by: James Morris Git-repo: http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git Git-commit:03dab869b7b239c4e013ec82aea22e181e441cfc Signed-off-by: Srinivasa Rao Kuppala --- security/keys/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/proc.c b/security/keys/proc.c index 217b6855e81..374c3301b80 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -188,7 +188,7 @@ static int proc_keys_show(struct seq_file *m, void *v) struct timespec now; unsigned long timo; key_ref_t key_ref, skey_ref; - char xbuf[12]; + char xbuf[16]; int rc; key_ref = make_key_ref(key, 0); From 758b70a64456f8c8c070972f09b2830fbdde85bc Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Thu, 19 Jan 2017 14:59:44 -0800 Subject: [PATCH 26/44] crypto: msm: check integer overflow on total data len in qcedev.c qcedev_vbuf_ablk_cipher will calculate total data length. It starts with the value of "areq->cipher_op_req.byteoffset", which is controlled by the user. Make change to check if this total data length has integer overflow issue in qcedev_check_cipher_params. Change-Id: Ice42dca6d47eb8febfe8a34e566c69e4799fab57 Signed-off-by: Zhen Kong --- drivers/crypto/msm/qcedev.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index d8b6c2e1c85..623530ca5e7 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -1456,6 +1456,15 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req, pr_err("%s: Invalid byte offset\n", __func__); goto error; } + total = req->byteoffset; + for (i = 0; i < req->entries; i++) { + if (total > U32_MAX - req->vbuf.src[i].len) { + pr_err("%s:Integer overflow on total src len\n", + __func__); + goto error; + } + total += req->vbuf.src[i].len; + } } if (req->data_len < req->byteoffset) { @@ -1491,7 +1500,7 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req, } } /* Check for sum of all dst length is equal to data_len */ - for (i = 0; i < req->entries; i++) { + for (i = 0, total = 0; i < req->entries; i++) { if (req->vbuf.dst[i].len >= U32_MAX - total) { pr_err("%s: Integer overflow on total req dst vbuf length\n", __func__); From b0608dc5512013eb094e5aeabc26e39683617444 Mon Sep 17 00:00:00 2001 From: Siena Richard Date: Mon, 5 Dec 2016 12:26:52 -0800 Subject: [PATCH 27/44] ASoC: msm: qdsp6v2: return error when copy from userspace fails A copy_from_user is not always expected to succeed. Therefore, check for an error before operating on the buffer post copy. Change-Id: Ibba9a47c84e735d30e32eeac5b80d51044b7a9e8 CRs-Fixed: 1094852 Signed-off-by: Siena Richard --- sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c index 08c4f2ac640..53f57cd39a9 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c @@ -827,6 +827,11 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, (sizeof(buf_node->frame.frm_hdr) + sizeof(buf_node->frame.pktlen)); } + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); list_add_tail(&buf_node->list, &prtd->in_queue); spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); From aa0b2abb92a2c10df746849db74061c768197ad6 Mon Sep 17 00:00:00 2001 From: Puja Gupta Date: Mon, 16 Jan 2017 15:56:27 -0800 Subject: [PATCH 28/44] soc: qcom: pil: Fix kernel pointer leak Replace WARN with pr_warn so that stack trace is not printed with the message. CRs-Fixed: 1111653 Change-Id: Id1c7bbe8a528199261455ba7901e9df81913aef8 Signed-off-by: Puja Gupta --- drivers/soc/qcom/subsystem_restart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index 1506373b2ae..6470c040683 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.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 @@ -959,8 +959,9 @@ int subsystem_restart_dev(struct subsys_device *dev) pr_info("Restart sequence requested for %s, restart_level = %s.\n", name, restart_levels[dev->restart_level]); - if (WARN(disable_restart_work == DISABLE_SSR, - "subsys-restart: Ignoring restart request for %s.\n", name)) { + if (disable_restart_work == DISABLE_SSR) { + pr_warn("subsys-restart: Ignoring restart request for %s.\n", + name); return 0; } From 259595b77932bda40e5fd183249867b3fe29c541 Mon Sep 17 00:00:00 2001 From: Aravind Kumar Date: Mon, 11 May 2015 18:19:11 +0530 Subject: [PATCH 29/44] ASoC: msm8x16-wcd: prevent out of bounds access Hardcoding the third argument in strnstr function is resulting in out of bounds access. Set the third argument to sizeof the character string passed as the first argument to prevent out of bounds access. CRs-Fixed: 832915 Change-Id: I61be88701340e271fd866e0e1801722dbe7d63ac Signed-off-by: Aravind Kumar --- sound/soc/codecs/msm8x16-wcd.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index 1448f80a3bf..bb7584e28d0 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -2215,15 +2215,19 @@ static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w, micbias2 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80); switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (strnstr(w->name, internal1_text, 30)) { + if (strnstr(w->name, internal1_text, strlen(w->name))) { + if (get_codec_version(msm8x16_wcd) == CAJON) + snd_soc_update_bits(codec, + MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80); - } else if (strnstr(w->name, internal2_text, 30)) { + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10); snd_soc_update_bits(codec, w->reg, 0x60, 0x00); - } else if (strnstr(w->name, internal3_text, 30)) { + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2); } - if (!strnstr(w->name, external_text, 30)) + if (!strnstr(w->name, external_text, strlen(w->name))) snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_MICB_1_EN, 0x05, 0x04); if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN) @@ -2232,28 +2236,28 @@ static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: usleep_range(20000, 20100); - if (strnstr(w->name, internal1_text, 30)) { + if (strnstr(w->name, internal1_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40); - } else if (strnstr(w->name, internal2_text, 30)) { + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08); msm8x16_notifier_call(codec, WCD_EVENT_PRE_MICBIAS_2_ON); - } else if (strnstr(w->name, internal3_text, 30)) { + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01); - } else if (strnstr(w->name, external2_text, 30)) { + } else if (strnstr(w->name, external2_text, strlen(w->name))) { msm8x16_notifier_call(codec, WCD_EVENT_PRE_MICBIAS_2_ON); } break; case SND_SOC_DAPM_POST_PMD: - if (strnstr(w->name, internal1_text, 30)) { + if (strnstr(w->name, internal1_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40); - } else if (strnstr(w->name, internal2_text, 30)) { + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { msm8x16_notifier_call(codec, WCD_EVENT_PRE_MICBIAS_2_OFF); - } else if (strnstr(w->name, internal3_text, 30)) { + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); - } else if (strnstr(w->name, external2_text, 30)) { + } else if (strnstr(w->name, external2_text, strlen(w->name))) { /* * send micbias turn off event to mbhc driver and then * break, as no need to set MICB_1_EN register. From eabc72256659f7e9e0c5ad5f62e84e7ecb7bbc41 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Wed, 16 Nov 2016 15:10:20 +0800 Subject: [PATCH 30/44] ASoC: qdsp6v2: prevent null pointer dereference for _vol_cmds In case of memory allocation failure, _vol_cmd_cnt is not reset. In _volume_cmds_free, null pointer dereference would happen for _vol_cmds[i]. To prevent it, reset _vol_cmd_cnt when memory allocation fails. CRs-Fixed: 1089598 Change-Id: Icb998549cdb999c6db2fd52aef505f200e630da5 Signed-off-by: Xiaojun Sang --- sound/soc/msm/qdsp6v2/msm-dts-eagle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c index 2ca9a3e0995..d6917122576 100644 --- a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c +++ b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c @@ -234,7 +234,8 @@ static s32 _volume_cmds_alloc1(s32 size) if (_vol_cmds) { _vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d), GFP_KERNEL); - } + } else + _vol_cmd_cnt = 0; if (_vol_cmds_d) return 0; _volume_cmds_free(); From 2feb1670bb83cc349537401a7d135e1d2695d67b Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Wed, 11 Jan 2017 12:12:31 -0800 Subject: [PATCH 31/44] qseecom: improve input validatation for qseecom_send_service_cmd Make change to improve input validation on request and response buffers' address and length for qseecom_send_service_cmd. Change-Id: I047e3264333d767541e43b7dadd1727232fd48ef Signed-off-by: Zhen Kong --- drivers/misc/qseecom.c | 152 ++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 8d134664157..93ee53d07d4 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -1,6 +1,6 @@ /*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver * - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -1798,11 +1798,6 @@ int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr, return -EINVAL; } - if ((!req_ptr->cmd_req_buf) || (!req_ptr->resp_buf)) { - pr_err("Invalid req/resp buffer, exiting\n"); - return -EINVAL; - } - /* Clients need to ensure req_buf is at base offset of shared buffer */ if ((uintptr_t)req_ptr->cmd_req_buf != data_ptr->client.user_virt_sb_base) { @@ -1810,15 +1805,11 @@ int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr, return -EINVAL; } - if (((uintptr_t)req_ptr->resp_buf < - data_ptr->client.user_virt_sb_base) || - ((uintptr_t)req_ptr->resp_buf >= - (data_ptr->client.user_virt_sb_base + - data_ptr->client.sb_length))){ - pr_err("response buffer address not within shared bufffer\n"); + if (data_ptr->client.sb_length < + sizeof(struct qseecom_rpmb_provision_key)) { + pr_err("shared buffer is too small to hold key type\n"); return -EINVAL; } - req_buf = data_ptr->client.sb_virt; send_svc_ireq_ptr->qsee_cmd_id = req_ptr->cmd_id; @@ -1845,36 +1836,6 @@ int __qseecom_process_fsm_key_svc_cmd(struct qseecom_dev_handle *data_ptr, return -EINVAL; } - if (((uintptr_t)req_ptr->cmd_req_buf < - data_ptr->client.user_virt_sb_base) || - ((uintptr_t)req_ptr->cmd_req_buf >= - (data_ptr->client.user_virt_sb_base + - data_ptr->client.sb_length))) { - pr_err("cmd buffer address not within shared bufffer\n"); - return -EINVAL; - } - - if (((uintptr_t)req_ptr->resp_buf < - data_ptr->client.user_virt_sb_base) || - ((uintptr_t)req_ptr->resp_buf >= - (data_ptr->client.user_virt_sb_base + - data_ptr->client.sb_length))){ - pr_err("response buffer address not within shared bufffer\n"); - return -EINVAL; - } - - if ((req_ptr->cmd_req_len == 0) || (req_ptr->resp_len == 0) || - req_ptr->cmd_req_len > data_ptr->client.sb_length || - req_ptr->resp_len > data_ptr->client.sb_length) { - pr_err("cmd buffer length or response buffer length not valid\n"); - return -EINVAL; - } - - if (req_ptr->cmd_req_len > UINT_MAX - req_ptr->resp_len) { - pr_err("Integer overflow detected in req_len & rsp_len, exiting now\n"); - return -EINVAL; - } - reqd_len_sb_in = req_ptr->cmd_req_len + req_ptr->resp_len; if (reqd_len_sb_in > data_ptr->client.sb_length) { pr_err("Not enough memory to fit cmd_buf and resp_buf. "); @@ -1896,28 +1857,11 @@ int __qseecom_process_fsm_key_svc_cmd(struct qseecom_dev_handle *data_ptr, return ret; } -static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, - void __user *argp) +static int __validate_send_service_cmd_inputs(struct qseecom_dev_handle *data, + struct qseecom_send_svc_cmd_req *req) { - int ret = 0; - struct qseecom_client_send_service_ireq send_svc_ireq; - struct qseecom_client_send_fsm_key_req send_fsm_key_svc_ireq; - struct qseecom_command_scm_resp resp; - struct qseecom_send_svc_cmd_req req; - void *send_req_ptr; - size_t req_buf_size; - - /*struct qseecom_command_scm_resp resp;*/ - - if (copy_from_user(&req, - (void __user *)argp, - sizeof(req))) { - pr_err("copy_from_user failed\n"); - return -EFAULT; - } - - if ((req.resp_buf == NULL) || (req.cmd_req_buf == NULL)) { - pr_err("cmd buffer or response buffer is null\n"); + if (!req || !req->resp_buf || !req->cmd_req_buf) { + pr_err("req or cmd buffer or response buffer is null\n"); return -EINVAL; } @@ -1941,6 +1885,86 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, return -EINVAL; } + if (((uintptr_t)req->cmd_req_buf < + data->client.user_virt_sb_base) || + ((uintptr_t)req->cmd_req_buf >= + (data->client.user_virt_sb_base + data->client.sb_length))) { + pr_err("cmd buffer address not within shared bufffer\n"); + return -EINVAL; + } + if (((uintptr_t)req->resp_buf < + data->client.user_virt_sb_base) || + ((uintptr_t)req->resp_buf >= + (data->client.user_virt_sb_base + data->client.sb_length))) { + pr_err("response buffer address not within shared bufffer\n"); + return -EINVAL; + } + if ((req->cmd_req_len == 0) || (req->resp_len == 0) || + (req->cmd_req_len > data->client.sb_length) || + (req->resp_len > data->client.sb_length)) { + pr_err("cmd buf length or response buf length not valid\n"); + return -EINVAL; + } + if (req->cmd_req_len > UINT_MAX - req->resp_len) { + pr_err("Integer overflow detected in req_len & rsp_len\n"); + return -EINVAL; + } + + if ((req->cmd_req_len + req->resp_len) > data->client.sb_length) { + pr_debug("Not enough memory to fit cmd_buf.\n"); + pr_debug("resp_buf. Required: %u, Available: %zu\n", + (req->cmd_req_len + req->resp_len), + data->client.sb_length); + return -ENOMEM; + } + if ((uintptr_t)req->cmd_req_buf > (ULONG_MAX - req->cmd_req_len)) { + pr_err("Integer overflow in req_len & cmd_req_buf\n"); + return -EINVAL; + } + if ((uintptr_t)req->resp_buf > (ULONG_MAX - req->resp_len)) { + pr_err("Integer overflow in resp_len & resp_buf\n"); + return -EINVAL; + } + if (data->client.user_virt_sb_base > + (ULONG_MAX - data->client.sb_length)) { + pr_err("Integer overflow in user_virt_sb_base & sb_length\n"); + return -EINVAL; + } + if ((((uintptr_t)req->cmd_req_buf + req->cmd_req_len) > + ((uintptr_t)data->client.user_virt_sb_base + + data->client.sb_length)) || + (((uintptr_t)req->resp_buf + req->resp_len) > + ((uintptr_t)data->client.user_virt_sb_base + + data->client.sb_length))) { + pr_err("cmd buf or resp buf is out of shared buffer region\n"); + return -EINVAL; + } + return 0; +} + +static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, + void __user *argp) +{ + int ret = 0; + struct qseecom_client_send_service_ireq send_svc_ireq; + struct qseecom_client_send_fsm_key_req send_fsm_key_svc_ireq; + struct qseecom_command_scm_resp resp; + struct qseecom_send_svc_cmd_req req; + void *send_req_ptr; + size_t req_buf_size; + + /*struct qseecom_command_scm_resp resp;*/ + + if (copy_from_user(&req, + (void __user *)argp, + sizeof(req))) { + pr_err("copy_from_user failed\n"); + return -EFAULT; + } + + if (__validate_send_service_cmd_inputs(data, &req)) + return -EINVAL; + data->type = QSEECOM_SECURE_SERVICE; switch (req.cmd_id) { From 83c3f0466c4d8695f06662da6178829cb002cb09 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 20 Jan 2017 16:38:16 +0800 Subject: [PATCH 32/44] spmi: Add flag to make SPMI debugfs write optional Currently, the SPMI debugfs entries support peek/poke functionality to access PMIC register directly. This is a possible way to access the PMIC and change some unexpected setting which is very dangerous. Add a flag CONFIG_MSM_SPMI_DEBUGFS_RO to make the write operation through debugfs optional, it can be defined if SPMI debugfs write is not desired. CRs-Fixed: 1103178 Change-Id: Iedbbf57a4a24a18f755a8b304591cc151b10bd4c Signed-off-by: Fenglin Wu --- drivers/spmi/Kconfig | 7 +++++++ drivers/spmi/spmi-dbgfs.c | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index c32978e8cc6..15499c7d0d1 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -27,4 +27,11 @@ config MSM_QPNP_INT help Say 'y' here to include support for the Qualcomm QPNP interrupt support. QPNP is a SPMI based PMIC implementation. + +config MSM_SPMI_DEBUGFS_RO + depends on DEBUG_FS + depends on SPMI + bool "Disable SPMI debugfs write" + help + Say 'y' here to disable the SPMI debugfs register write operation. endif diff --git a/drivers/spmi/spmi-dbgfs.c b/drivers/spmi/spmi-dbgfs.c index b0a354b59a5..3ebcbc387ba 100644 --- a/drivers/spmi/spmi-dbgfs.c +++ b/drivers/spmi/spmi-dbgfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -46,6 +46,12 @@ static const char *DFS_ROOT_NAME = "spmi"; static const mode_t DFS_MODE = S_IRUSR | S_IWUSR; +#ifndef CONFIG_MSM_SPMI_DEBUGFS_RO +static const mode_t DFS_DATA_MODE = S_IRUSR | S_IWUSR; +#else +static const mode_t DFS_DATA_MODE = S_IRUSR; +#endif + /* Log buffer */ struct spmi_log_buffer { size_t rpos; /* Current 'read' position in buffer */ @@ -241,6 +247,7 @@ done: return ret; } +#ifndef CONFIG_MSM_SPMI_DEBUGFS_RO /** * spmi_write_data: writes data across the SPMI bus * @ctrl: The SPMI controller @@ -277,6 +284,7 @@ spmi_write_data(struct spmi_controller *ctrl, uint8_t *buf, int offset, int cnt) done: return ret; } +#endif /** * print_to_log: format a string and place into the log buffer @@ -456,6 +464,7 @@ static int get_log_data(struct spmi_trans *trans) return total_items_read; } +#ifndef CONFIG_MSM_SPMI_DEBUGFS_RO /** * spmi_dfs_reg_write: write user's byte array (coded as string) over SPMI. * @file: file pointer @@ -519,6 +528,9 @@ free_buf: kfree(kbuf); return ret; } +#else +#define spmi_dfs_reg_write NULL +#endif /** * spmi_dfs_reg_read: reads value(s) over SPMI and fill user's buffer a @@ -671,14 +683,14 @@ int spmi_dfs_add_controller(struct spmi_controller *ctrl) goto err_remove_fs; } - file = debugfs_create_file("data", DFS_MODE, dir, ctrl_data, + file = debugfs_create_file("data", DFS_DATA_MODE, dir, ctrl_data, &spmi_dfs_reg_fops); if (!file) { pr_err("error creating 'data' entry\n"); goto err_remove_fs; } - file = debugfs_create_file("data_raw", DFS_MODE, dir, ctrl_data, + file = debugfs_create_file("data_raw", DFS_DATA_MODE, dir, ctrl_data, &spmi_dfs_raw_data_fops); if (!file) { pr_err("error creating 'data' entry\n"); From b3ada229b0e7b9e33600955e17de88c4533a82f1 Mon Sep 17 00:00:00 2001 From: Gidon Studinski Date: Mon, 23 Jan 2017 11:55:53 +0200 Subject: [PATCH 33/44] ecm_ipa: remove redundant code Remove ECM_IPA debugging capabilities which are no longer needed, nor used. CRs-Fixed: 1112912 Change-Id: I20765d84ca7351c436d39d0f10b7f7a819bcc40b Signed-off-by: Gidon Studinski --- drivers/net/ethernet/msm/ecm_ipa.c | 269 +---------------------------- 1 file changed, 4 insertions(+), 265 deletions(-) diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c index ce9cf6383bd..8de016da517 100644 --- a/drivers/net/ethernet/msm/ecm_ipa.c +++ b/drivers/net/ethernet/msm/ecm_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -9,7 +9,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - #include #include #include @@ -106,10 +105,6 @@ enum ecm_ipa_operation { * struct ecm_ipa_dev - main driver context parameters * @net: network interface struct implemented by this driver * @directory: debugfs directory for various debuging switches - * @tx_enable: flag that enable/disable Tx path to continue to IPA - * @rx_enable: flag that enable/disable Rx path to continue to IPA - * @rm_enable: flag that enable/disable Resource manager request prior to Tx - * @dma_enable: flag that allow on-the-fly DMA mode for IPA * @eth_ipv4_hdr_hdl: saved handle for ipv4 header-insertion table * @eth_ipv6_hdr_hdl: saved handle for ipv6 header-insertion table * @usb_to_ipa_hdl: save handle for IPA pipe operations @@ -129,10 +124,6 @@ enum ecm_ipa_operation { */ struct ecm_ipa_dev { struct net_device *net; - u32 tx_enable; - u32 rx_enable; - u32 rm_enable; - bool dma_enable; struct dentry *directory; uint32_t eth_ipv4_hdr_hdl; uint32_t eth_ipv6_hdr_hdl; @@ -167,32 +158,16 @@ static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event, static struct net_device_stats *ecm_ipa_get_stats(struct net_device *net); static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx); static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx); -static bool rx_filter(struct sk_buff *skb); -static bool tx_filter(struct sk_buff *skb); -static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx); static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx); static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx); static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb, struct net_device *net); -static int ecm_ipa_debugfs_stall_open(struct inode *inode, - struct file *file); -static ssize_t ecm_ipa_debugfs_stall_write(struct file *file, - const char __user *buf, size_t count, loff_t *ppos); static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file); -static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file, - const char __user *buf, size_t count, loff_t *ppos); -static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file); -static ssize_t ecm_ipa_debugfs_enable_write(struct file *file, - const char __user *buf, size_t count, loff_t *ppos); -static ssize_t ecm_ipa_debugfs_enable_read(struct file *file, - char __user *ubuf, size_t count, loff_t *ppos); static ssize_t ecm_ipa_debugfs_atomic_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos); static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx); static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx); static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl); -static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl, - enum ipa_client_type prod_client); static int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr, u8 device_ethaddr[]); static enum ecm_ipa_state ecm_ipa_next_state(enum ecm_ipa_state current_state, @@ -210,22 +185,11 @@ static const struct net_device_ops ecm_ipa_netdev_ops = { .ndo_get_stats = ecm_ipa_get_stats, }; -const struct file_operations ecm_ipa_debugfs_dma_ops = { - .open = ecm_ipa_debugfs_dma_open, - .read = ecm_ipa_debugfs_enable_read, - .write = ecm_ipa_debugfs_enable_write_dma, -}; - const struct file_operations ecm_ipa_debugfs_atomic_ops = { .open = ecm_ipa_debugfs_atomic_open, .read = ecm_ipa_debugfs_atomic_read, }; -const struct file_operations ecm_ipa_debugfs_stall_ops = { - .open = ecm_ipa_debugfs_stall_open, - .write = ecm_ipa_debugfs_stall_write, -}; - static void ecm_ipa_msg_free_cb(void *buff, u32 len, u32 type) { kfree(buff); @@ -286,9 +250,6 @@ int ecm_ipa_init(struct ecm_ipa_params *params) ECM_IPA_DEBUG("ecm_ipa_ctx (private) = %p\n", ecm_ipa_ctx); ecm_ipa_ctx->net = net; - ecm_ipa_ctx->tx_enable = true; - ecm_ipa_ctx->rx_enable = true; - ecm_ipa_ctx->rm_enable = true; ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH; ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW; atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0); @@ -604,12 +565,6 @@ static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb, return NETDEV_TX_BUSY; } - if (unlikely(tx_filter(skb))) { - dev_kfree_skb_any(skb); - ECM_IPA_DEBUG("packet got filtered out on Tx path\n"); - status = NETDEV_TX_OK; - goto out; - } ret = resource_request(ecm_ipa_ctx); if (ret) { ECM_IPA_DEBUG("Waiting to resource\n"); @@ -681,11 +636,6 @@ static void ecm_ipa_packet_receive_notify(void *priv, skb->dev = ecm_ipa_ctx->net; skb->protocol = eth_type_trans(skb, ecm_ipa_ctx->net); - if (rx_filter(skb)) { - ECM_IPA_DEBUG("packet got filtered out on Rx path\n"); - dev_kfree_skb_any(skb); - return; - } result = netif_rx(skb); if (result) @@ -1148,42 +1098,15 @@ static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx) ECM_IPA_LOG_EXIT(); } -static bool rx_filter(struct sk_buff *skb) -{ - struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev); - return !ecm_ipa_ctx->rx_enable; -} - -static bool tx_filter(struct sk_buff *skb) -{ - struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev); - return !ecm_ipa_ctx->tx_enable; -} - -static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx) -{ - return ecm_ipa_ctx->rm_enable; -} - static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx) { - int result = 0; - - if (!rm_enabled(ecm_ipa_ctx)) - goto out; - result = ipa_rm_inactivity_timer_request_resource( - IPA_RM_RESOURCE_STD_ECM_PROD); -out: - return result; + return ipa_rm_inactivity_timer_request_resource( + IPA_RM_RESOURCE_STD_ECM_PROD); } static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx) { - if (!rm_enabled(ecm_ipa_ctx)) - goto out; ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD); -out: - return; } /** @@ -1255,45 +1178,6 @@ static void ecm_ipa_tx_timeout(struct net_device *net) net->stats.tx_errors++; } -static int ecm_ipa_debugfs_stall_open(struct inode *inode, - struct file *file) -{ - ECM_IPA_LOG_ENTRY(); - - ECM_IPA_LOG_EXIT(); - - return 0; -} - -static ssize_t ecm_ipa_debugfs_stall_write(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - u32 cmdq_cfg_mmio_phy = 0xFD4E3038; - void *cmdq_cfg_mmio_virt; - int result; - bool val = 0; - - ECM_IPA_LOG_ENTRY(); - - file->private_data = &val; - result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos); - - cmdq_cfg_mmio_virt = ioremap(cmdq_cfg_mmio_phy, sizeof(u32)); - if (!cmdq_cfg_mmio_virt) { - ECM_IPA_ERROR("fail on mmio for cmdq_cfg_mmio_phy=0x%x", - cmdq_cfg_mmio_phy); - return result; - } - - iowrite32(val, cmdq_cfg_mmio_virt); - ECM_IPA_DEBUG("Value %d was written to cfgq", val); - - ECM_IPA_LOG_EXIT(); - - return result; - -} - static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file) { struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private; @@ -1303,78 +1187,6 @@ static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file) return 0; } -static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - struct ecm_ipa_dev *ecm_ipa_ctx = file->private_data; - int result; - ECM_IPA_LOG_ENTRY(); - file->private_data = &ecm_ipa_ctx->dma_enable; - result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos); - if (ecm_ipa_ctx->dma_enable) - ecm_ipa_ep_registers_dma_cfg(ecm_ipa_ctx->usb_to_ipa_hdl, - ecm_ipa_ctx->ipa_to_usb_client); - else - ecm_ipa_ep_registers_cfg(ecm_ipa_ctx->usb_to_ipa_hdl, - ecm_ipa_ctx->usb_to_ipa_hdl); - ECM_IPA_LOG_EXIT(); - return result; -} - -static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file) -{ - struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private; - ECM_IPA_LOG_ENTRY(); - file->private_data = ecm_ipa_ctx; - ECM_IPA_LOG_EXIT(); - return 0; -} - -static ssize_t ecm_ipa_debugfs_enable_write(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - unsigned long missing; - char input; - bool *enable = file->private_data; - if (count != sizeof(input) + 1) { - ECM_IPA_ERROR("wrong input length(%zd)\n", count); - return -EINVAL; - } - if (!buf) { - ECM_IPA_ERROR("Bad argument\n"); - return -EINVAL; - } - missing = copy_from_user(&input, buf, 1); - if (missing) - return -EFAULT; - ECM_IPA_DEBUG("input received %c\n", input); - *enable = input - '0'; - ECM_IPA_DEBUG("value was set to %d\n", *enable); - return count; -} - -static ssize_t ecm_ipa_debugfs_enable_read(struct file *file, - char __user *ubuf, size_t count, loff_t *ppos) -{ - int nbytes; - int size = 0; - int ret; - loff_t pos; - u8 enable_str[sizeof(char)*3] = {0}; - bool *enable = file->private_data; - pos = *ppos; - nbytes = scnprintf(enable_str, sizeof(enable_str), "%d\n", *enable); - ret = simple_read_from_buffer(ubuf, count, ppos, enable_str, nbytes); - if (ret < 0) { - ECM_IPA_ERROR("simple_read_from_buffer problem\n"); - return ret; - } - size += ret; - count -= nbytes; - *ppos = pos + size; - return size; -} - static ssize_t ecm_ipa_debugfs_atomic_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { @@ -1391,7 +1203,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx) { const mode_t flags_read_write = S_IRUGO | S_IWUGO; const mode_t flags_read_only = S_IRUGO; - const mode_t flags_write_only = S_IWUGO; struct dentry *file; ECM_IPA_LOG_ENTRY(); @@ -1404,24 +1215,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx) ECM_IPA_ERROR("could not create debugfs directory entry\n"); goto fail_directory; } - file = debugfs_create_bool("tx_enable", flags_read_write, - ecm_ipa_ctx->directory, &ecm_ipa_ctx->tx_enable); - if (!file) { - ECM_IPA_ERROR("could not create debugfs tx file\n"); - goto fail_file; - } - file = debugfs_create_bool("rx_enable", flags_read_write, - ecm_ipa_ctx->directory, &ecm_ipa_ctx->rx_enable); - if (!file) { - ECM_IPA_ERROR("could not create debugfs rx file\n"); - goto fail_file; - } - file = debugfs_create_bool("rm_enable", flags_read_write, - ecm_ipa_ctx->directory, &ecm_ipa_ctx->rm_enable); - if (!file) { - ECM_IPA_ERROR("could not create debugfs rm file\n"); - goto fail_file; - } file = debugfs_create_u8("outstanding_high", flags_read_write, ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_high); if (!file) { @@ -1434,13 +1227,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx) ECM_IPA_ERROR("could not create outstanding_low file\n"); goto fail_file; } - file = debugfs_create_file("dma_enable", flags_read_write, - ecm_ipa_ctx->directory, - ecm_ipa_ctx, &ecm_ipa_debugfs_dma_ops); - if (!file) { - ECM_IPA_ERROR("could not create debugfs dma file\n"); - goto fail_file; - } file = debugfs_create_file("outstanding", flags_read_only, ecm_ipa_ctx->directory, ecm_ipa_ctx, &ecm_ipa_debugfs_atomic_ops); @@ -1449,14 +1235,7 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx) goto fail_file; } - file = debugfs_create_file("stall_ipa_rx_proc", flags_write_only, - ecm_ipa_ctx->directory, - ecm_ipa_ctx, &ecm_ipa_debugfs_stall_ops); - if (!file) { - ECM_IPA_ERROR("could not create stall_ipa_rx_proc file\n"); - goto fail_file; - } - + ECM_IPA_DEBUG("debugfs entries were created\n"); ECM_IPA_LOG_EXIT(); return 0; @@ -1519,46 +1298,6 @@ out: return result; } -/** - * ecm_ipa_ep_registers_dma_cfg() - configure the USB endpoints for ECM - * DMA - * @usb_to_ipa_hdl: handle received from ipa_connect - * - * This function will override the previous configuration - * which is needed for cores that does not support blocks logic - * Note that client handles are the actual pipe index - */ -static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl, - enum ipa_client_type prod_client) -{ - int result = 0; - struct ipa_ep_cfg_mode cfg_mode; - u32 apps_to_ipa_hdl = 2; - - ECM_IPA_LOG_ENTRY(); - - memset(&cfg_mode, 0 , sizeof(cfg_mode)); - cfg_mode.mode = IPA_DMA; - cfg_mode.dst = prod_client; - result = ipa_cfg_ep_mode(apps_to_ipa_hdl, &cfg_mode); - if (result) { - ECM_IPA_ERROR("failed to configure Apps to IPA\n"); - goto out; - } - memset(&cfg_mode, 0 , sizeof(cfg_mode)); - cfg_mode.mode = IPA_DMA; - cfg_mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS; - result = ipa_cfg_ep_mode(usb_to_ipa_hdl, &cfg_mode); - if (result) { - ECM_IPA_ERROR("failed to configure USB to IPA\n"); - goto out; - } - ECM_IPA_DEBUG("end-point registers successfully configured\n"); -out: - ECM_IPA_LOG_EXIT(); - return result; -} - /** * ecm_ipa_set_device_ethernet_addr() - set device etherenet address * @dev_ethaddr: device etherenet address From 7dbc954149030d80aab65f18de2468d8602748ec Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 22 Nov 2016 13:51:51 -0800 Subject: [PATCH 34/44] ASoC: msm: qdsp6v2: Fix an incorrect string comparison logic A conditional branch is skipped due to incorrect string comparison. It is fixed by using "strcmp" to perform string comparison. CRs-fixed: 1085889 Change-Id: I12be44ac272fc5079230feb6ae24c68ccde99cf2 Signed-off-by: Xiaoyu Ye Signed-off-by: Bhalchandra Gajare --- sound/soc/msm/qdsp6v2/q6afe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 39284040187..60e34767756 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -3172,7 +3172,7 @@ static ssize_t afe_debug_write(struct file *filp, lbuf[cnt] = '\0'; - if (!strncmp(lb_str, "afe_loopback", 12)) { + if (!strcmp(lb_str, "afe_loopback")) { rc = afe_get_parameters(lbuf, param, 3); if (!rc) { pr_info("%s: %lu %lu %lu\n", lb_str, param[0], param[1], @@ -3201,7 +3201,7 @@ static ssize_t afe_debug_write(struct file *filp, rc = -EINVAL; } - } else if (!strncmp(lb_str, "afe_loopback_gain", 17)) { + } else if (!strcmp(lb_str, "afe_loopback_gain")) { rc = afe_get_parameters(lbuf, param, 2); if (!rc) { pr_info("%s: %s %lu %lu\n", From 8f522ae02dc18fdd0be81bf4f6083d26e1fee819 Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Mon, 7 Nov 2016 15:39:43 -0800 Subject: [PATCH 35/44] qseecom: fix incomplete error handling for several functions Perform a complete or adequate check of return codes for several functions, including __qseecom_enable_clk, ion_do_cache_op and ion_sg_table(), used by qseecom. Change-Id: Ib1682bdc6d3034a22586af62a3d8986c54d369d5 Signed-off-by: Zhen Kong Signed-off-by: Mallikarjuna Reddy Amireddy --- drivers/misc/qseecom.c | 155 +++++++++++++++++++++++++++++++---------- 1 file changed, 118 insertions(+), 37 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 8d134664157..1c178a34443 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -999,9 +999,11 @@ static int __qseecom_set_msm_bus_request(uint32_t mode) pr_err("Bandwidth req failed(%d) MODE (%d)\n", ret, mode); if (qclk->ce_core_src_clk != NULL) { - if (mode == INACTIVE) - __qseecom_enable_clk(CLK_QSEE); - else + if (mode == INACTIVE) { + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + pr_err("CLK enable failed\n"); + } else __qseecom_disable_clk(CLK_QSEE); } } @@ -1379,13 +1381,20 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data, SGLISTINFO_TABLE_SIZE; dmac_flush_range((void *)table, (void *)table + SGLISTINFO_TABLE_SIZE); - if (ptr_svc) + if (ptr_svc) { msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle, ptr_svc->sb_virt, ptr_svc->sb_length, - ION_IOC_CLEAN_INV_CACHES); - - if (lstnr == RPMB_SERVICE) - __qseecom_enable_clk(CLK_QSEE); + ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } + } + if (lstnr == RPMB_SERVICE) { + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + return ret; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&send_data_rsp, @@ -1536,6 +1545,10 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp) load_req.phy_addr = (uint32_t)pa; msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto loadapp_err; + } /* SCM_CALL to load the app and get the app_id back */ ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req, @@ -1991,15 +2004,16 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, } } - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, data->client.sb_length, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto exit; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)send_req_ptr, req_buf_size, &resp, sizeof(resp)); - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, - data->client.sb_virt, data->client.sb_length, - ION_IOC_INV_CACHES); if (ret) { pr_err("qseecom_scm_call failed with err: %d\n", ret); if (!qseecom.support_bus_scaling) { @@ -2011,7 +2025,13 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, } goto exit; } - + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + data->client.sb_virt, data->client.sb_length, + ION_IOC_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto exit; + } switch (resp.result) { case QSEOS_RESULT_SUCCESS: break; @@ -2167,10 +2187,14 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, dmac_flush_range((void *)table, (void *)table + SGLISTINFO_TABLE_SIZE); - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, reqd_len_sb_in, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&send_data_req, @@ -2194,9 +2218,11 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, ret = -EINVAL; } } - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, data->client.sb_length, ION_IOC_INV_CACHES); + if (ret) + pr_err("cache operation failed %d\n", ret); return ret; } @@ -2309,7 +2335,7 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, } /* Populate the cmd data structure with the phys_addr */ sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle); - if (sg_ptr == NULL) { + if (IS_ERR_OR_NULL(sg_ptr)) { pr_err("IOn client could not retrieve sg table\n"); goto err; } @@ -2380,13 +2406,21 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, } } if (cleanup) { - msm_ion_do_cache_op(qseecom.ion_clnt, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto err; + } } else { - msm_ion_do_cache_op(qseecom.ion_clnt, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto err; + } if (data->type == QSEECOM_CLIENT_APP) { offset = req->ifd_data[i].cmd_buf_offset; data->sglistinfo_ptr[i].indexAndFlags = @@ -2741,9 +2775,13 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname) goto exit_unregister_bus_bw_need; } - msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, img_data, fw_size, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto exit_disable_clk_vote; + } /* SCM_CALL to load the image */ ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req, @@ -2833,9 +2871,13 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data) goto exit_unregister_bus_bw_need; } - msm_ion_do_cache_op(qseecom.ion_clnt, qseecom.cmnlib_ion_handle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, qseecom.cmnlib_ion_handle, img_data, fw_size, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto exit_disable_clk_vote; + } /* SCM_CALL to load the image */ ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req, @@ -3403,9 +3445,10 @@ static int __qseecom_enable_clk(enum qseecom_ce_hw_instance ce) } mutex_lock(&clk_access_lock); - if (qclk->clk_access_cnt == ULONG_MAX) + if (qclk->clk_access_cnt == ULONG_MAX) { + pr_err("clk_access_cnt beyond limitation\n"); goto err; - + } if (qclk->clk_access_cnt > 0) { qclk->clk_access_cnt++; mutex_unlock(&clk_access_lock); @@ -3719,8 +3762,12 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data, ret = -EIO; goto exit_register_bus_bandwidth_needs; } - msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto exit_disable_clock; + } /* SCM_CALL to load the external elf */ ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req, sizeof(struct qseecom_load_app_ireq), @@ -3979,7 +4026,9 @@ static int __qseecom_generate_and_save_key(struct qseecom_dev_handle *data, pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } - __qseecom_enable_clk(CLK_QSEE); + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + return ret; ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, ireq, sizeof(struct qseecom_key_generate_ireq), @@ -4037,7 +4086,9 @@ static int __qseecom_delete_saved_key(struct qseecom_dev_handle *data, pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } - __qseecom_enable_clk(CLK_QSEE); + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + return ret; ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, ireq, sizeof(struct qseecom_key_delete_ireq), @@ -4096,10 +4147,15 @@ static int __qseecom_set_clear_ce_key(struct qseecom_dev_handle *data, pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + return ret; - __qseecom_enable_clk(CLK_QSEE); - if (qseecom.qsee.instance != qseecom.ce_drv.instance) - __qseecom_enable_clk(CLK_CE_DRV); + if (qseecom.qsee.instance != qseecom.ce_drv.instance) { + ret = __qseecom_enable_clk(CLK_CE_DRV); + if (ret) + return ret; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, ireq, sizeof(struct qseecom_key_select_ireq), @@ -4161,8 +4217,9 @@ static int __qseecom_update_current_key_user_info( pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } - - __qseecom_enable_clk(CLK_QSEE); + ret = __qseecom_enable_clk(CLK_QSEE); + if (ret) + return ret; ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, ireq, sizeof(struct qseecom_key_userinfo_update_ireq), @@ -4640,7 +4697,7 @@ static int __qseecom_update_qteec_req_buf(struct qseecom_qteec_modfd_req *req, } /* Populate the cmd data structure with the phys_addr */ sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle); - if (sg_ptr == NULL) { + if (IS_ERR_OR_NULL(sg_ptr)) { pr_err("IOn client could not retrieve sg table\n"); goto err; } @@ -4656,13 +4713,21 @@ static int __qseecom_update_qteec_req_buf(struct qseecom_qteec_modfd_req *req, *update = (uint32_t)sg_dma_address(sg_ptr->sgl); if (cleanup) { - msm_ion_do_cache_op(qseecom.ion_clnt, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, sg->length, ION_IOC_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto err; + } } else { - msm_ion_do_cache_op(qseecom.ion_clnt, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, sg->length, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + goto err; + } data->sglistinfo_ptr[i].indexAndFlags = SGLISTINFO_SET_INDEX_FLAG( (sg_ptr->nents == 1), 0, @@ -4722,10 +4787,14 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, (void *)table + SGLISTINFO_TABLE_SIZE); reqd_len_sb_in = req->req_len + req->resp_len; - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, reqd_len_sb_in, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &ireq, sizeof(ireq), @@ -4749,9 +4818,13 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, ret = -EINVAL; } } - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, data->client.sb_length, ION_IOC_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } if ((cmd_id == QSEOS_TEE_OPEN_SESSION) || (cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) { @@ -4854,10 +4927,14 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data, (void *)table + SGLISTINFO_TABLE_SIZE); reqd_len_sb_in = req.req_len + req.resp_len; - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, reqd_len_sb_in, ION_IOC_CLEAN_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &ireq, sizeof(ireq), @@ -4885,9 +4962,13 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data, if (ret) return ret; - msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, + ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle, data->client.sb_virt, data->client.sb_length, ION_IOC_INV_CACHES); + if (ret) { + pr_err("cache operation failed %d\n", ret); + return ret; + } return 0; } From e82c22f6298b2b5964b91d80b42821f310dea3f9 Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Mon, 16 Jan 2017 00:17:04 +0200 Subject: [PATCH 36/44] msm: ipa: Prevent multiple header deletion from user space An IPA header or processing context can be added once and later deleted once from user space. Multiple deletion may cause invalid state of the headers software cache. Change-Id: Ic0b8472b7fd8a76233a007d90c832af726184574 CRs-fixed: 1097714 Signed-off-by: Ghanim Fodi --- drivers/platform/msm/ipa/ipa.c | 13 ++--- drivers/platform/msm/ipa/ipa_hdr.c | 77 ++++++++++++++++++++++++------ drivers/platform/msm/ipa/ipa_i.h | 11 ++++- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c index ec050e95d09..a6334d4b2bd 100644 --- a/drivers/platform/msm/ipa/ipa.c +++ b/drivers/platform/msm/ipa/ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -431,7 +431,8 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } - if (ipa_del_hdr((struct ipa_ioc_del_hdr *)param)) { + if (ipa_del_hdr_by_user((struct ipa_ioc_del_hdr *)param, + true)) { retval = -EFAULT; break; } @@ -1112,8 +1113,8 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } - if (ipa_del_hdr_proc_ctx( - (struct ipa_ioc_del_hdr_proc_ctx *)param)) { + if (ipa_del_hdr_proc_ctx_by_user( + (struct ipa_ioc_del_hdr_proc_ctx *)param, true)) { retval = -EFAULT; break; } @@ -2266,7 +2267,7 @@ fail_schedule_delayed_work: if (ipa_ctx->dflt_v4_rt_rule_hdl) __ipa_del_rt_rule(ipa_ctx->dflt_v4_rt_rule_hdl); if (ipa_ctx->excp_hdr_hdl) - __ipa_del_hdr(ipa_ctx->excp_hdr_hdl); + __ipa_del_hdr(ipa_ctx->excp_hdr_hdl, false); ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd); fail_cmd: return result; @@ -2278,7 +2279,7 @@ static void ipa_teardown_apps_pipes(void) ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_in); __ipa_del_rt_rule(ipa_ctx->dflt_v6_rt_rule_hdl); __ipa_del_rt_rule(ipa_ctx->dflt_v4_rt_rule_hdl); - __ipa_del_hdr(ipa_ctx->excp_hdr_hdl); + __ipa_del_hdr(ipa_ctx->excp_hdr_hdl, false); ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd); } diff --git a/drivers/platform/msm/ipa/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_hdr.c index c1e920539b0..1426e4bc66e 100644 --- a/drivers/platform/msm/ipa/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_hdr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -719,7 +719,8 @@ error: return -EPERM; } -static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) +static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, + bool release_hdr, bool by_user) { struct ipa_hdr_proc_ctx_entry *entry; struct ipa_hdr_proc_ctx_tbl *htbl = &ipa_ctx->hdr_proc_ctx_tbl; @@ -733,6 +734,14 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) IPADBG("del ctx proc cnt=%d ofst=%d\n", htbl->proc_ctx_cnt, entry->offset_entry->offset); + if (by_user && entry->user_deleted) { + IPAERR("proc_ctx already deleted by user\n"); + return -EINVAL; + } + + if (by_user) + entry->user_deleted = true; + if (--entry->ref_cnt) { IPADBG("proc_ctx_hdl %x ref_cnt %d\n", proc_ctx_hdl, entry->ref_cnt); @@ -740,7 +749,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } if (release_hdr) - __ipa_release_hdr(entry->hdr->id); + __ipa_del_hdr(entry->hdr->id, false); /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -757,7 +766,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } -int __ipa_del_hdr(u32 hdr_hdl) +int __ipa_del_hdr(u32 hdr_hdl, bool by_user) { struct ipa_hdr_entry *entry; struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl; @@ -776,6 +785,14 @@ int __ipa_del_hdr(u32 hdr_hdl) IPADBG("del hdr of sz=%d hdr_cnt=%d ofst=%d\n", entry->hdr_len, htbl->hdr_cnt, entry->offset_entry->offset); + if (by_user && entry->user_deleted) { + IPAERR("hdr already deleted by user\n"); + return -EINVAL; + } + + if (by_user) + entry->user_deleted = true; + if (--entry->ref_cnt) { IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt); return 0; @@ -786,7 +803,7 @@ int __ipa_del_hdr(u32 hdr_hdl) entry->phys_base, entry->hdr_len, DMA_TO_DEVICE); - __ipa_del_hdr_proc_ctx(entry->proc_ctx->id, false); + __ipa_del_hdr_proc_ctx(entry->proc_ctx->id, false, false); } else { /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -849,15 +866,16 @@ bail: EXPORT_SYMBOL(ipa_add_hdr); /** - * ipa_del_hdr() - Remove the specified headers from SW and optionally commit them - * to IPA HW + * ipa_del_hdr_by_user() - Remove the specified headers + * from SW and optionally commit them to IPA HW * @hdls: [inout] set of headers to delete + * @by_user: Operation requested by user? * * Returns: 0 on success, negative on failure * * Note: Should not be called from atomic context */ -int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls) +int ipa_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user) { int i; int result = -EFAULT; @@ -869,7 +887,7 @@ int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls) mutex_lock(&ipa_ctx->lock); for (i = 0; i < hdls->num_hdls; i++) { - if (__ipa_del_hdr(hdls->hdl[i].hdl)) { + if (__ipa_del_hdr(hdls->hdl[i].hdl, by_user)) { IPAERR("failed to del hdr %i\n", i); hdls->hdl[i].status = -1; } else { @@ -888,6 +906,20 @@ bail: mutex_unlock(&ipa_ctx->lock); return result; } + +/** + * ipa_del_hdr() - Remove the specified headers from SW and optionally commit them + * to IPA HW + * @hdls: [inout] set of headers to delete + * + * Returns: 0 on success, negative on failure + * + * Note: Should not be called from atomic context + */ +int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls) +{ + return ipa_del_hdr_by_user(hdls, false); +} EXPORT_SYMBOL(ipa_del_hdr); /** @@ -936,16 +968,18 @@ bail: EXPORT_SYMBOL(ipa_add_hdr_proc_ctx); /** - * ipa_del_hdr_proc_ctx() - + * ipa_del_hdr_proc_ctx_by_user() - * Remove the specified processing context headers from SW and * optionally commit them to IPA HW. * @hdls: [inout] set of processing context headers to delete + * @by_user: Operation requested by user? * * Returns: 0 on success, negative on failure * * Note: Should not be called from atomic context */ -int ipa_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls) +int ipa_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls, + bool by_user) { int i; int result; @@ -957,7 +991,7 @@ int ipa_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls) mutex_lock(&ipa_ctx->lock); for (i = 0; i < hdls->num_hdls; i++) { - if (__ipa_del_hdr_proc_ctx(hdls->hdl[i].hdl, true)) { + if (__ipa_del_hdr_proc_ctx(hdls->hdl[i].hdl, true, by_user)) { IPAERR("failed to del hdr %i\n", i); hdls->hdl[i].status = -1; } else { @@ -976,6 +1010,21 @@ bail: mutex_unlock(&ipa_ctx->lock); return result; } + +/** + * ipa_del_hdr_proc_ctx() - + * Remove the specified processing context headers from SW and + * optionally commit them to IPA HW. + * @hdls: [inout] set of processing context headers to delete + * + * Returns: 0 on success, negative on failure + * + * Note: Should not be called from atomic context + */ +int ipa_del_hdr_proc_ctx(struct ipa_ioc_del_hdr_proc_ctx *hdls) +{ + return ipa_del_hdr_proc_ctx_by_user(hdls, false); +} EXPORT_SYMBOL(ipa_del_hdr_proc_ctx); /** @@ -1197,7 +1246,7 @@ int __ipa_release_hdr(u32 hdr_hdl) { int result = 0; - if (__ipa_del_hdr(hdr_hdl)) { + if (__ipa_del_hdr(hdr_hdl, false)) { IPADBG("fail to del hdr %x\n", hdr_hdl); result = -EFAULT; goto bail; @@ -1225,7 +1274,7 @@ int __ipa_release_hdr_proc_ctx(u32 proc_ctx_hdl) { int result = 0; - if (__ipa_del_hdr_proc_ctx(proc_ctx_hdl, true)) { + if (__ipa_del_hdr_proc_ctx(proc_ctx_hdl, true, false)) { IPADBG("fail to del hdr %x\n", proc_ctx_hdl); result = -EFAULT; goto bail; diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h index 9ff220bac6c..0342ad1ff05 100644 --- a/drivers/platform/msm/ipa/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -226,6 +226,7 @@ struct ipa_rt_tbl { * @id: header entry id * @is_eth2_ofst_valid: is eth2_ofst field valid? * @eth2_ofst: offset to start of Ethernet-II/802.3 header + * @user_deleted: is the header deleted by the user? */ struct ipa_hdr_entry { struct list_head link; @@ -243,6 +244,7 @@ struct ipa_hdr_entry { int id; u8 is_eth2_ofst_valid; u16 eth2_ofst; + bool user_deleted; }; /** @@ -318,6 +320,7 @@ struct ipa_hdr_proc_ctx_add_hdr_cmd_seq { * @cookie: cookie used for validity check * @ref_cnt: reference counter of routing table * @id: processing context header entry id + * @user_deleted: is the hdr processing context deleted by the user? */ struct ipa_hdr_proc_ctx_entry { struct list_head link; @@ -327,6 +330,7 @@ struct ipa_hdr_proc_ctx_entry { u32 cookie; u32 ref_cnt; int id; + bool user_deleted; }; /** @@ -1410,8 +1414,11 @@ void ipa_inc_client_enable_clks(void); int ipa_inc_client_enable_clks_no_block(void); void ipa_dec_client_disable_clks(void); int ipa_interrupts_init(u32 ipa_irq, u32 ee, struct device *ipa_dev); +int ipa_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user); +int ipa_del_hdr_proc_ctx_by_user(struct ipa_ioc_del_hdr_proc_ctx *hdls, + bool by_user); int __ipa_del_rt_rule(u32 rule_hdl); -int __ipa_del_hdr(u32 hdr_hdl); +int __ipa_del_hdr(u32 hdr_hdl, bool by_user); int __ipa_release_hdr(u32 hdr_hdl); int __ipa_release_hdr_proc_ctx(u32 proc_ctx_hdl); int _ipa_read_gen_reg_v1_1(char *buff, int max_len); From 33839cd52ce2359c290fd392fb7989a7194a50e0 Mon Sep 17 00:00:00 2001 From: Walter Yang Date: Wed, 21 Dec 2016 14:43:46 +0800 Subject: [PATCH 37/44] ASoC: msm-cpe-lsm: cleanup ioctl functions Some of the ioctl command handling is not properly using the copy_from_user interface. Fix these issues and cleanup the ioctl functions to make sure there is no illegal memory access. CRs-Fixed: 1090482 Change-Id: Ib18e4b132d3487a3103335768aad5df2ebe13f2d Signed-off-by: Walter Yang --- sound/soc/msm/msm-cpe-lsm.c | 46 +++++++++++-------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c index a05b7fd16db..31f9eb78182 100644 --- a/sound/soc/msm/msm-cpe-lsm.c +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2017 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 @@ -793,13 +793,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_REG_SND_MODEL_V2"); - if (!arg) { - dev_err(rtd->dev, - "%s: Invalid argument to ioctl %s\n", - __func__, - "SNDRV_LSM_REG_SND_MODEL_V2"); - return -EINVAL; - } memcpy(&snd_model, arg, sizeof(struct snd_lsm_sound_model_v2)); @@ -926,13 +919,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_EVENT_STATUS"); - if (!arg) { - dev_err(rtd->dev, - "%s: Invalid argument to ioctl %s\n", - __func__, - "SNDRV_LSM_EVENT_STATUS"); - return -EINVAL; - } user = arg; @@ -1035,12 +1021,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, break; case SNDRV_LSM_SET_PARAMS: - if (!arg) { - dev_err(rtd->dev, - "%s: %s Invalid argument\n", - __func__, "SNDRV_LSM_SET_PARAMS"); - return -EINVAL; - } memcpy(&det_params, arg, sizeof(det_params)); if (det_params.num_confidence_levels <= 0) { @@ -1305,12 +1285,6 @@ done: } #ifdef CONFIG_COMPAT -struct snd_lsm_event_status32 { - u16 status; - u16 payload_size; - u8 payload[0]; -}; - struct snd_lsm_sound_model_v2_32 { compat_uptr_t data; compat_uptr_t confidence_level; @@ -1328,8 +1302,6 @@ struct snd_lsm_detection_params_32 { }; enum { - SNDRV_LSM_EVENT_STATUS32 = - _IOW('U', 0x02, struct snd_lsm_event_status32), SNDRV_LSM_REG_SND_MODEL_V2_32 = _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), SNDRV_LSM_SET_PARAMS32 = @@ -1416,7 +1388,7 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, err); } break; - case SNDRV_LSM_EVENT_STATUS32: { + case SNDRV_LSM_EVENT_STATUS: { struct snd_lsm_event_status *event_status = NULL; struct snd_lsm_event_status u_event_status32; struct snd_lsm_event_status *udata_32 = NULL; @@ -1458,7 +1430,6 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, } else { event_status->payload_size = u_event_status32.payload_size; - cmd = SNDRV_LSM_EVENT_STATUS; err = msm_cpe_lsm_ioctl_shared(substream, cmd, event_status); if (err) @@ -1533,7 +1504,18 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, break; } - + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + break; default: err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg); break; From 34caea9a6f7fb2c2155d3a02dbeca42b4f496a6d Mon Sep 17 00:00:00 2001 From: ansharma Date: Thu, 9 Feb 2017 18:01:42 +0530 Subject: [PATCH 38/44] power: qpnp-fg: Fix possible race condition in FG debugfs There is a possible race condition when FG debugfs files are concurrently accessed by multiple threads. Fix this. CRs-Fixed: 1105481 Change-Id: I154e7f3cdd8d51cf67ef1dfd9d78f423f183cb64 Signed-off-by: ansharma --- drivers/power/qpnp-fg.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/power/qpnp-fg.c b/drivers/power/qpnp-fg.c index 9ac2b2e1586..033d32be3cf 100644 --- a/drivers/power/qpnp-fg.c +++ b/drivers/power/qpnp-fg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, 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 @@ -484,6 +484,7 @@ struct fg_trans { struct fg_chip *chip; struct fg_log_buffer *log; /* log buffer */ u8 *data; /* fg data that is read */ + struct mutex memif_dfs_lock; /* Prevent thread concurrency */ }; struct fg_dbgfs { @@ -4504,6 +4505,7 @@ static int fg_memif_data_open(struct inode *inode, struct file *file) trans->addr = dbgfs_data.addr; trans->chip = dbgfs_data.chip; trans->offset = trans->addr; + mutex_init(&trans->memif_dfs_lock); file->private_data = trans; return 0; @@ -4515,6 +4517,7 @@ static int fg_memif_dfs_close(struct inode *inode, struct file *file) if (trans && trans->log && trans->data) { file->private_data = NULL; + mutex_destroy(&trans->memif_dfs_lock); kfree(trans->log); kfree(trans->data); kfree(trans); @@ -4672,10 +4675,13 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, size_t ret; size_t len; + mutex_lock(&trans->memif_dfs_lock); /* Is the the log buffer empty */ if (log->rpos >= log->wpos) { - if (get_log_data(trans) <= 0) - return 0; + if (get_log_data(trans) <= 0) { + len = 0; + goto unlock_mutex; + } } len = min(count, log->wpos - log->rpos); @@ -4683,7 +4689,8 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, ret = copy_to_user(buf, &log->data[log->rpos], len); if (ret == len) { pr_err("error copy sram register values to user\n"); - return -EFAULT; + len = -EFAULT; + goto unlock_mutex; } /* 'ret' is the number of bytes not copied */ @@ -4691,6 +4698,9 @@ static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, *ppos += len; log->rpos += len; + +unlock_mutex: + mutex_unlock(&trans->memif_dfs_lock); return len; } @@ -4711,14 +4721,20 @@ static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, int cnt = 0; u8 *values; size_t ret = 0; + char *kbuf; + u32 offset; struct fg_trans *trans = file->private_data; - u32 offset = trans->offset; + + mutex_lock(&trans->memif_dfs_lock); + offset = trans->offset; /* Make a copy of the user data */ - char *kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto unlock_mutex; + } ret = copy_from_user(kbuf, buf, count); if (ret == count) { @@ -4757,6 +4773,8 @@ static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, free_buf: kfree(kbuf); +unlock_mutex: + mutex_unlock(&trans->memif_dfs_lock); return ret; } From 7cf080130c9276ed62dd10c269e4b60e61a65e5c Mon Sep 17 00:00:00 2001 From: ansharma Date: Thu, 9 Feb 2017 20:43:08 +0530 Subject: [PATCH 39/44] platform: msm: spmi: Fix possible race condition in debugfs There is a possible race condition when debugfs files are concurrently accessed by multiple threads. Fix this. CRs-Fixed: 1106842 Change-Id: Ifd092143f428db3cf73c45ec4f0aaa96318ae165 Signed-off-by: ansharma --- drivers/spmi/spmi-dbgfs.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/spmi/spmi-dbgfs.c b/drivers/spmi/spmi-dbgfs.c index 3ebcbc387ba..363a0697164 100644 --- a/drivers/spmi/spmi-dbgfs.c +++ b/drivers/spmi/spmi-dbgfs.c @@ -75,6 +75,7 @@ struct spmi_trans { u32 addr; /* 20-bit address: SID + PID + Register offset */ u32 offset; /* Offset of last read data */ bool raw_data; /* Set to true for raw data dump */ + struct mutex spmi_dfs_lock; /* Prevent thread concurrency */ struct spmi_controller *ctrl; struct spmi_log_buffer *log; /* log buffer */ }; @@ -174,6 +175,7 @@ static int spmi_dfs_open(struct spmi_ctrl_data *ctrl_data, struct file *file) trans->addr = ctrl_data->addr; trans->ctrl = ctrl_data->ctrl; trans->offset = trans->addr; + mutex_init(&trans->spmi_dfs_lock); file->private_data = trans; return 0; @@ -203,6 +205,7 @@ static int spmi_dfs_close(struct inode *inode, struct file *file) if (trans && trans->log) { file->private_data = NULL; + mutex_destroy(&trans->spmi_dfs_lock); kfree(trans->log); kfree(trans); } @@ -482,14 +485,20 @@ static ssize_t spmi_dfs_reg_write(struct file *file, const char __user *buf, int cnt = 0; u8 *values; size_t ret = 0; - + u32 offset; + char *kbuf; struct spmi_trans *trans = file->private_data; - u32 offset = trans->offset; + + mutex_lock(&trans->spmi_dfs_lock); + + offset = trans->offset; /* Make a copy of the user data */ - char *kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto unlock_mutex; + } ret = copy_from_user(kbuf, buf, count); if (ret == count) { @@ -526,6 +535,8 @@ static ssize_t spmi_dfs_reg_write(struct file *file, const char __user *buf, free_buf: kfree(kbuf); +unlock_mutex: + mutex_unlock(&trans->spmi_dfs_lock); return ret; } #else @@ -549,10 +560,13 @@ static ssize_t spmi_dfs_reg_read(struct file *file, char __user *buf, size_t ret; size_t len; + mutex_lock(&trans->spmi_dfs_lock); /* Is the the log buffer empty */ if (log->rpos >= log->wpos) { - if (get_log_data(trans) <= 0) - return 0; + if (get_log_data(trans) <= 0) { + len = 0; + goto unlock_mutex; + } } len = min(count, log->wpos - log->rpos); @@ -560,7 +574,8 @@ static ssize_t spmi_dfs_reg_read(struct file *file, char __user *buf, ret = copy_to_user(buf, &log->data[log->rpos], len); if (ret == len) { pr_err("error copy SPMI register values to user\n"); - return -EFAULT; + len = -EFAULT; + goto unlock_mutex; } /* 'ret' is the number of bytes not copied */ @@ -568,6 +583,9 @@ static ssize_t spmi_dfs_reg_read(struct file *file, char __user *buf, *ppos += len; log->rpos += len; + +unlock_mutex: + mutex_unlock(&trans->spmi_dfs_lock); return len; } From 2b78e465c05f675dac0c57a8e66964b6c7acb93a Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Thu, 3 Nov 2016 15:24:35 +0530 Subject: [PATCH 40/44] mm: cma: check the max limit for cma allocation CMA allocation request size is represented by size_t that gets truncated when same is passed as int to bitmap_find_next_zero_area_off. We observe that during fuzz testing when cma allocation request is too high, bitmap_find_next_zero_area_off still returns success due to the truncation. This leads to kernel crash, as subsequent code assumes that requested memory is available. Fail cma allocation in case the request breaches the corresponding cma region size. Change-Id: Ieb5fd8429726efd7686387bccb55952fb053280a Signed-off-by: Shiraz Hashim --- drivers/base/dma-contiguous.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 10fb61e5dfe..6a8c1e722dc 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -610,6 +610,9 @@ unsigned long dma_alloc_from_contiguous(struct device *dev, size_t count, if (!count) return 0; + if (count > cma->count) + return 0; + mask = (1 << align) - 1; From ac285660279ce4880f269bb972b6a3ab60240e0e Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Thu, 1 Dec 2016 18:07:42 -0800 Subject: [PATCH 41/44] fs/proc/array.c: make safe access to group_leader As mentioned in commit 52ee2dfdd4f51cf422ea6a96a0846dc94244aa37 ("pids: refactor vnr/nr_ns helpers to make them safe"). *_nr_ns helpers used to be buggy. The commit addresses most of the helpers but is missing task_tgid_xxx() Without this protection there is a possible use after free reported by kasan instrumented kernel: ================================================================== BUG: KASAN: use-after-free in task_tgid_nr_ns+0x2c/0x44 at addr *** Read of size 8 by task cat/2472 CPU: 1 PID: 2472 Comm: cat Tainted: **** Hardware name: Google Tegra210 Smaug Rev 1,3+ (DT) Call trace: [] dump_backtrace+0x0/0x17c [] show_stack+0x18/0x24 [] dump_stack+0x94/0x100 [] kasan_report+0x308/0x554 [] __asan_load8+0x20/0x7c [] task_tgid_nr_ns+0x28/0x44 [] proc_pid_status+0x444/0x1080 [] proc_single_show+0x8c/0xdc [] seq_read+0x2e8/0x6f0 [] vfs_read+0xd8/0x1e0 [] SyS_read+0x68/0xd4 Accessing group_leader while holding rcu_lock and using the now safe helpers introduced in the commit mentioned, this race condition is addressed. Signed-off-by: Adrian Salido Change-Id: I4315217922dda375a30a3581c0c1740dda7b531b Bug: 31495866 Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-3.10.git Git-commit: 1d6d364ee174676a225a77dc7ca8dac887199718 Signed-off-by: Srinivasa Rao Kuppala --- fs/proc/array.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 09f0d9c374a..6ed95802239 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -168,16 +168,16 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, int g; struct fdtable *fdt = NULL; const struct cred *cred; - pid_t ppid, tpid; + pid_t ppid = 0, tpid = 0; + struct task_struct *leader = NULL; rcu_read_lock(); - ppid = pid_alive(p) ? - task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; - tpid = 0; if (pid_alive(p)) { struct task_struct *tracer = ptrace_parent(p); if (tracer) tpid = task_pid_nr_ns(tracer, ns); + ppid = task_tgid_nr_ns(rcu_dereference(p->real_parent), ns); + leader = p->group_leader; } cred = get_task_cred(p); seq_printf(m, @@ -189,7 +189,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, "Uid:\t%d\t%d\t%d\t%d\n" "Gid:\t%d\t%d\t%d\t%d\n", get_task_state(p), - task_tgid_nr_ns(p, ns), + leader ? task_pid_nr_ns(leader, ns) : 0, pid_nr_ns(pid, ns), ppid, tpid, from_kuid_munged(user_ns, cred->uid), From 5609b4b02301392bcf8cdbcb7ec48b59b5ed9f7e Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Fri, 18 Nov 2016 10:44:16 -0800 Subject: [PATCH 42/44] BACKPORT: aio: mark AIO pseudo-fs noexec This ensures that do_mmap() won't implicitly make AIO memory mappings executable if the READ_IMPLIES_EXEC personality flag is set. Such behavior is problematic because the security_mmap_file LSM hook doesn't catch this case, potentially permitting an attacker to bypass a W^X policy enforced by SELinux. I have tested the patch on my machine. To test the behavior, compile and run this: #define _GNU_SOURCE #include #include #include #include #include #include #include int main(void) { personality(READ_IMPLIES_EXEC); aio_context_t ctx = 0; if (syscall(__NR_io_setup, 1, &ctx)) err(1, "io_setup"); char cmd[1000]; sprintf(cmd, "cat /proc/%d/maps | grep -F '/[aio]'", (int)getpid()); system(cmd); return 0; } In the output, "rw-s" is good, "rwxs" is bad. Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds (cherry picked from commit 22f6b4d34fcf039c63a94e7670e0da24f8575a5a) Bug: 31711619 Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-3.10 Git-commit: I9f2872703bef240d6b82320c744529459bb076dc Change-Id: I9f2872703bef240d6b82320c744529459bb076dc Signed-off-by: Srinivasa Rao Kuppala --- fs/aio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/aio.c b/fs/aio.c index 9798d4edfd8..0f2c38f29c4 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -153,6 +154,9 @@ static int aio_setup_ring(struct kioctx *ctx) unsigned long size, populate; int nr_pages; + if (current->personality & READ_IMPLIES_EXEC) + return -EPERM; + /* Compensate for the ring buffer's head/tail overlap entry */ nr_events += 2; /* 1 is required, 2 for good luck */ From 9738861f3105eb82afdd4ebbf3b0d52ad4617780 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Fri, 3 Feb 2017 13:24:19 +0530 Subject: [PATCH 43/44] uio: fix potential use after free issue when accessing debug_buffer The variable debug_buffer is a global variable which is allocated and free'd when open/close is called on debugfs file - "/sys/kernel/debug/rmt_storage/info". The current code doesn't have locks to handle concurrent accesses to the above file. This results into use after free issue when debug_buffer is accessed by two threads at the same time. Fix this by adding a mutex lock to protect this global variable. Change-Id: I6bc3f0ae2d7fca3ca9fe8561612f5863b6c3268a Signed-off-by: Sahitya Tummala --- drivers/uio/msm_sharedmem/sharedmem_qmi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/uio/msm_sharedmem/sharedmem_qmi.c b/drivers/uio/msm_sharedmem/sharedmem_qmi.c index 3b04c582cb3..cc8f7cd8342 100644 --- a/drivers/uio/msm_sharedmem/sharedmem_qmi.c +++ b/drivers/uio/msm_sharedmem/sharedmem_qmi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, 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 @@ -221,6 +221,7 @@ static int sharedmem_qmi_req_cb(struct qmi_handle *handle, void *conn_h, #define DEBUG_BUF_SIZE (2048) static char *debug_buffer; static u32 debug_data_size; +static struct mutex dbg_buf_lock; /* mutex for debug_buffer */ static ssize_t debug_read(struct file *file, char __user *buf, size_t count, loff_t *file_pos) @@ -276,21 +277,30 @@ static u32 fill_debug_info(char *buffer, u32 buffer_size) static int debug_open(struct inode *inode, struct file *file) { u32 buffer_size; - if (debug_buffer != NULL) + + mutex_lock(&dbg_buf_lock); + if (debug_buffer != NULL) { + mutex_unlock(&dbg_buf_lock); return -EBUSY; + } buffer_size = DEBUG_BUF_SIZE; debug_buffer = kzalloc(buffer_size, GFP_KERNEL); - if (debug_buffer == NULL) + if (debug_buffer == NULL) { + mutex_unlock(&dbg_buf_lock); return -ENOMEM; + } debug_data_size = fill_debug_info(debug_buffer, buffer_size); + mutex_unlock(&dbg_buf_lock); return 0; } static int debug_close(struct inode *inode, struct file *file) { + mutex_lock(&dbg_buf_lock); kfree(debug_buffer); debug_buffer = NULL; debug_data_size = 0; + mutex_unlock(&dbg_buf_lock); return 0; } @@ -321,6 +331,7 @@ static void debugfs_init(void) { struct dentry *f_ent; + mutex_init(&dbg_buf_lock); dir_ent = debugfs_create_dir("rmt_storage", NULL); if (IS_ERR(dir_ent)) { pr_err("Failed to create debug_fs directory\n"); @@ -349,6 +360,7 @@ static void debugfs_init(void) static void debugfs_exit(void) { debugfs_remove_recursive(dir_ent); + mutex_destroy(&dbg_buf_lock); } static void sharedmem_qmi_svc_recv_msg(struct work_struct *work) From 5fd6a281d60aad8aa3c87e16fde57401de6f2256 Mon Sep 17 00:00:00 2001 From: Siena Richard Date: Mon, 23 Jan 2017 13:15:58 -0800 Subject: [PATCH 44/44] ASoC: msm: qdsp6v2: completely deallocate on cal block creation failure Completely deallocate the cal block if creation fails to ensure no memory leaks are present. CRs-Fixed: 1112751 Change-Id: I76916c8b3f7e8e9b864dc39dab96f7d330774473 Signed-off-by: Siena Richard --- sound/soc/msm/qdsp6v2/audio_cal_utils.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c index 3fe659e80ae..3ac81b2bc68 100644 --- a/sound/soc/msm/qdsp6v2/audio_cal_utils.c +++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c @@ -536,7 +536,6 @@ static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, memset(cal_block, 0, sizeof(*cal_block)); INIT_LIST_HEAD(&cal_block->list); - list_add_tail(&cal_block->list, &cal_type->cal_blocks); cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle; if (basic_cal->cal_data.mem_handle > 0) { @@ -568,7 +567,8 @@ static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, goto err; } cal_block->buffer_number = basic_cal->cal_hdr.buffer_number; - pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pa!\n", + list_add_tail(&cal_block->list, &cal_type->cal_blocks); + pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n", __func__, cal_type->info.reg.cal_type, cal_block->buffer_number, cal_block->map_data.ion_map_handle, @@ -577,6 +577,8 @@ static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, done: return cal_block; err: + kfree(cal_block->cal_info); + kfree(cal_block->client_info); kfree(cal_block); cal_block = NULL; return cal_block;