android_kernel_xiaomi_sdm845/drivers/char/diag/diagmem.c
Hardik Arya 9cb4c10f40 diag: Add protection while freeing diag mempool buffer
There is possibility of double free because of protection
is missing while freeing diag hdlc mempool buffer. The patch
adds protection for the same by taking spinlock.

Change-Id: I7d2176e05408df80c839e198994807663ce172e8
Signed-off-by: Hardik Arya <harya@codeaurora.org>
2019-01-30 16:16:49 +05:30

296 lines
6.4 KiB
C

/* Copyright (c) 2008-2014, 2016, 2019 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mempool.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/kmemleak.h>
#include <linux/ratelimit.h>
#include <linux/atomic.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/kmemleak.h>
#include "diagchar.h"
#include "diagmem.h"
struct diag_mempool_t diag_mempools[NUM_MEMORY_POOLS] = {
{
.id = POOL_TYPE_COPY,
.name = "POOL_COPY",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_HDLC,
.name = "POOL_HDLC",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_USER,
.name = "POOL_USER",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MUX_APPS,
.name = "POOL_MUX_APPS",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_DCI,
.name = "POOL_DCI",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
{
.id = POOL_TYPE_MDM,
.name = "POOL_MDM",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM2,
.name = "POOL_MDM2",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM_DCI,
.name = "POOL_MDM_DCI",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM2_DCI,
.name = "POOL_MDM2_DCI",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM_MUX,
.name = "POOL_MDM_MUX",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM2_MUX,
.name = "POOL_MDM2_MUX",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM_DCI_WRITE,
.name = "POOL_MDM_DCI_WRITE",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_MDM2_DCI_WRITE,
.name = "POOL_MDM2_DCI_WRITE",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
},
{
.id = POOL_TYPE_QSC_MUX,
.name = "POOL_QSC_MUX",
.pool = NULL,
.itemsize = 0,
.poolsize = 0,
.count = 0
}
#endif
};
void diagmem_setsize(int pool_idx, int itemsize, int poolsize)
{
if (pool_idx < 0 || pool_idx >= NUM_MEMORY_POOLS) {
pr_err("diag: Invalid pool index %d in %s\n", pool_idx,
__func__);
return;
}
diag_mempools[pool_idx].itemsize = itemsize;
diag_mempools[pool_idx].poolsize = poolsize;
pr_debug("diag: Mempool %s sizes: itemsize %d poolsize %d\n",
diag_mempools[pool_idx].name, diag_mempools[pool_idx].itemsize,
diag_mempools[pool_idx].poolsize);
}
void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
{
void *buf = NULL;
int i = 0;
unsigned long flags;
struct diag_mempool_t *mempool = NULL;
if (!driver)
return NULL;
for (i = 0; i < NUM_MEMORY_POOLS; i++) {
mempool = &diag_mempools[i];
if (pool_type != mempool->id)
continue;
if (!mempool->pool) {
pr_err_ratelimited("diag: %s mempool is not initialized yet\n",
mempool->name);
break;
}
if (size == 0 || size > mempool->itemsize) {
pr_err_ratelimited("diag: cannot alloc from mempool %s, invalid size: %d\n",
mempool->name, size);
break;
}
spin_lock_irqsave(&mempool->lock, flags);
if (mempool->count < mempool->poolsize) {
atomic_add(1, (atomic_t *)&mempool->count);
buf = mempool_alloc(mempool->pool, GFP_ATOMIC);
kmemleak_not_leak(buf);
}
spin_unlock_irqrestore(&mempool->lock, flags);
if (!buf) {
pr_debug_ratelimited("diag: Unable to allocate buffer from memory pool %s, size: %d/%d count: %d/%d\n",
mempool->name,
size, mempool->itemsize,
mempool->count,
mempool->poolsize);
}
break;
}
return buf;
}
void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type)
{
int i = 0;
unsigned long flags;
struct diag_mempool_t *mempool = NULL;
if (!driver || !buf)
return;
for (i = 0; i < NUM_MEMORY_POOLS; i++) {
mempool = &diag_mempools[i];
if (pool_type != mempool->id)
continue;
if (!mempool->pool) {
pr_err_ratelimited("diag: %s mempool is not initialized yet\n",
mempool->name);
break;
}
spin_lock_irqsave(&mempool->lock, flags);
if (mempool->count > 0 && buf) {
mempool_free(buf, mempool->pool);
atomic_add(-1, (atomic_t *)&mempool->count);
} else {
pr_err_ratelimited("diag: Attempting to free items from %s mempool which is already empty\n",
mempool->name);
}
spin_unlock_irqrestore(&mempool->lock, flags);
break;
}
}
void diagmem_init(struct diagchar_dev *driver, int index)
{
struct diag_mempool_t *mempool = NULL;
if (!driver)
return;
if (index < 0 || index >= NUM_MEMORY_POOLS) {
pr_err("diag: In %s, Invalid index %d\n", __func__, index);
return;
}
mempool = &diag_mempools[index];
if (mempool->pool) {
pr_debug("diag: mempool %s is already initialized\n",
mempool->name);
return;
}
if (mempool->itemsize <= 0 || mempool->poolsize <= 0) {
pr_err("diag: Unable to initialize %s mempool, itemsize: %d poolsize: %d\n",
mempool->name, mempool->itemsize,
mempool->poolsize);
return;
}
mempool->pool = mempool_create_kmalloc_pool(mempool->poolsize,
mempool->itemsize);
if (!mempool->pool)
pr_err("diag: cannot allocate %s mempool\n", mempool->name);
else
kmemleak_not_leak(mempool->pool);
spin_lock_init(&mempool->lock);
}
void diagmem_exit(struct diagchar_dev *driver, int index)
{
unsigned long flags;
struct diag_mempool_t *mempool = NULL;
if (!driver)
return;
if (index < 0 || index >= NUM_MEMORY_POOLS) {
pr_err("diag: In %s, Invalid index %d\n", __func__, index);
return;
}
mempool = &diag_mempools[index];
spin_lock_irqsave(&mempool->lock, flags);
if (mempool->count == 0 && mempool->pool != NULL) {
mempool_destroy(mempool->pool);
mempool->pool = NULL;
} else {
pr_err("diag: Unable to destroy %s pool, count: %d\n",
mempool->name, mempool->count);
}
spin_unlock_irqrestore(&mempool->lock, flags);
}