msm: gsi: Correctly allocate memory for copy_from_user

Copying from user to a global variable inside the driver is
unsafe and insecure: allocate and free the memory used for
copying from userspace in the functions doing it.

This also has the good side effect of solving a build failure
when the kernel is built with GCC >=4.9

Change-Id: I96d0e74fa73939883079cf2b3308dbfa7de6a453
Signed-off-by: Richard Raya <rdxzv.dev@gmail.com>
This commit is contained in:
Angelo G. Del Regno 2019-06-06 09:09:45 +02:00 committed by Richard Raya
parent deef0312bc
commit a8d7b20544

View File

@ -12,6 +12,7 @@
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/uaccess.h>
#include <linux/msm_gsi.h>
@ -28,7 +29,6 @@
#ifdef CONFIG_DEBUG_FS
static struct dentry *dent;
#endif
static char dbg_buff[4096];
static void *gsi_ipc_logbuf_low;
static void gsi_wq_print_dp_stats(struct work_struct *work);
@ -46,35 +46,49 @@ static ssize_t gsi_dump_evt(struct file *file,
uint32_t val;
struct gsi_evt_ctx *ctx;
uint16_t i;
int ret = 0;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
missing = copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count));
if (missing)
return -EFAULT;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
dbg_buff[count] = '\0';
missing = copy_from_user(sptr, buf, count);
if (missing) {
ret = -EFAULT;
goto end;
}
sptr = dbg_buff;
sptr[count] = '\0';
token = strsep(&sptr, " ");
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &arg1))
return -EINVAL;
if (!token) {
ret = -EINVAL;
goto end;
}
if (kstrtou32(token, 0, &arg1)) {
ret = -EINVAL;
goto end;
}
token = strsep(&sptr, " ");
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &arg2))
return -EINVAL;
if (!token) {
ret = -EINVAL;
goto end;
}
if (kstrtou32(token, 0, &arg2)) {
ret = -EINVAL;
goto end;
}
TDBG("arg1=%u arg2=%u\n", arg1, arg2);
if (arg1 >= gsi_ctx->max_ev) {
TERR("invalid evt ring id %u\n", arg1);
return -EINVAL;
ret = -EINVAL;
goto end;
}
val = gsi_readl(gsi_ctx->base +
@ -146,7 +160,9 @@ static ssize_t gsi_dump_evt(struct file *file,
}
}
return count;
end:
kfree(sptr);
return ret < 0 ? ret : count;
}
static ssize_t gsi_dump_ch(struct file *file,
@ -159,35 +175,49 @@ static ssize_t gsi_dump_ch(struct file *file,
uint32_t val;
struct gsi_chan_ctx *ctx;
uint16_t i;
int ret = 0;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
missing = copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count));
if (missing)
return -EFAULT;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
dbg_buff[count] = '\0';
missing = copy_from_user(sptr, buf, count);
if (missing) {
ret = -EFAULT;
goto end;
}
sptr = dbg_buff;
sptr[count] = '\0';
token = strsep(&sptr, " ");
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &arg1))
return -EINVAL;
if (!token) {
ret = -EINVAL;
goto end;
}
if (kstrtou32(token, 0, &arg1)) {
ret = -EINVAL;
goto end;
}
token = strsep(&sptr, " ");
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &arg2))
return -EINVAL;
if (!token) {
ret = -EINVAL;
goto end;
}
if (kstrtou32(token, 0, &arg2)) {
ret = -EINVAL;
goto end;
}
TDBG("arg1=%u arg2=%u\n", arg1, arg2);
if (arg1 >= gsi_ctx->max_ch) {
TERR("invalid chan id %u\n", arg1);
return -EINVAL;
ret = -EINVAL;
goto end;
}
val = gsi_readl(gsi_ctx->base +
@ -262,8 +292,9 @@ static ssize_t gsi_dump_ch(struct file *file,
TERR("No VA supplied for chan id %u\n", arg1);
}
}
return count;
end:
kfree(sptr);
return ret < 0 ? ret : count;
}
static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx)
@ -300,16 +331,19 @@ static ssize_t gsi_dump_stats(struct file *file,
{
int ch_id;
int min, max;
char *sptr;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -EINVAL;
if(copy_from_user(sptr, buf, count))
goto error;
if (copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count)))
goto error;
dbg_buff[count] = '\0';
if (kstrtos32(dbg_buff, 0, &ch_id))
if (kstrtos32(sptr, 0, &ch_id))
goto error;
if (ch_id == -1) {
@ -326,8 +360,10 @@ static ssize_t gsi_dump_stats(struct file *file,
for (ch_id = min; ch_id < max; ch_id++)
gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]);
kfree(sptr);
return count;
error:
kfree(sptr);
TERR("Usage: echo ch_id > stats. Use -1 for all\n");
return -EINVAL;
}
@ -359,21 +395,26 @@ static ssize_t gsi_enable_dp_stats(struct file *file,
int ch_id;
bool enable;
int ret;
char *sptr;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
if (copy_from_user(sptr, buf, count))
goto error;
if (copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count)))
sptr[count] = '\0';
if (sptr[0] != '+' && sptr[0] != '-')
goto error;
dbg_buff[count] = '\0';
enable = (sptr[0] == '+');
if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
goto error;
enable = (dbg_buff[0] == '+');
if (kstrtos32(dbg_buff + 1, 0, &ch_id))
if (kstrtos32(sptr + 1, 0, &ch_id))
goto error;
if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
@ -383,7 +424,7 @@ static ssize_t gsi_enable_dp_stats(struct file *file,
if (gsi_ctx->chan[ch_id].enable_dp_stats == enable) {
TERR("ch_%d: already enabled/disabled\n", ch_id);
return -EINVAL;
goto error;
}
gsi_ctx->chan[ch_id].enable_dp_stats = enable;
@ -405,8 +446,10 @@ static ssize_t gsi_enable_dp_stats(struct file *file,
gsi_dbg_destroy_stats_wq();
}
kfree(sptr);
return count;
error:
kfree(sptr);
TERR("Usage: echo [+-]ch_id > enable_dp_stats\n");
return -EINVAL;
}
@ -419,17 +462,18 @@ static ssize_t gsi_set_max_elem_dp_stats(struct file *file,
unsigned long missing;
char *sptr, *token;
if (count < 2)
return -EINVAL;
if (sizeof(dbg_buff) < count + 1)
goto error;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
missing = copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count));
missing = copy_from_user(sptr, buf, count);
if (missing)
goto error;
dbg_buff[count] = '\0';
sptr = dbg_buff;
sptr[count] = '\0';
token = strsep(&sptr, " ");
if (!token) {
@ -445,13 +489,13 @@ static ssize_t gsi_set_max_elem_dp_stats(struct file *file,
token = strsep(&sptr, " ");
if (!token) {
/* get */
if (kstrtou32(dbg_buff, 0, &ch_id))
if (kstrtou32(sptr, 0, &ch_id))
goto error;
if (ch_id >= gsi_ctx->max_ch)
goto error;
PRT_STAT("ch %d: max_re_expected=%d\n", ch_id,
gsi_ctx->chan[ch_id].props.max_re_expected);
return count;
goto end;
}
if (kstrtou32(token, 0, &max_elem)) {
TERR("\n");
@ -466,10 +510,12 @@ static ssize_t gsi_set_max_elem_dp_stats(struct file *file,
}
gsi_ctx->chan[ch_id].props.max_re_expected = max_elem;
end:
kfree(sptr);
return count;
error:
kfree(sptr);
TERR("Usage: (set) echo <ch_id> <max_elem> > max_elem_dp_stats\n");
TERR("Usage: (get) echo <ch_id> > max_elem_dp_stats\n");
return -EINVAL;
@ -541,16 +587,19 @@ static ssize_t gsi_rst_stats(struct file *file,
{
int ch_id;
int min, max;
char *sptr;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
if (copy_from_user(sptr, buf, count))
goto error;
if (copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count)))
goto error;
dbg_buff[count] = '\0';
if (kstrtos32(dbg_buff, 0, &ch_id))
if (kstrtos32(sptr, 0, &ch_id))
goto error;
if (ch_id == -1) {
@ -567,9 +616,11 @@ static ssize_t gsi_rst_stats(struct file *file,
for (ch_id = min; ch_id < max; ch_id++)
memset(&gsi_ctx->chan[ch_id].stats, 0,
sizeof(gsi_ctx->chan[ch_id].stats));
kfree(sptr);
return count;
error:
kfree(sptr);
TERR("Usage: echo ch_id > rst_stats. Use -1 for all\n");
return -EINVAL;
}
@ -580,22 +631,27 @@ static ssize_t gsi_print_dp_stats(struct file *file,
int ch_id;
bool enable;
int ret;
char *sptr;
if (sizeof(dbg_buff) < count + 1)
if (count < 2)
return -EINVAL;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return -ENOMEM;
if (copy_from_user(sptr, buf, count))
goto error;
if (copy_from_user(dbg_buff, buf, min(sizeof(dbg_buff), count)))
if (sptr[0] != '+' && sptr[0] != '-')
goto error;
dbg_buff[count] = '\0';
enable = (sptr[0] == '+');
if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
goto error;
enable = (dbg_buff[0] == '+');
if (kstrtos32(dbg_buff + 1, 0, &ch_id))
if (kstrtos32(sptr + 1, 0, &ch_id)) {
goto error;
}
kfree(sptr);
if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
!gsi_ctx->chan[ch_id].allocated) {
@ -628,6 +684,7 @@ static ssize_t gsi_print_dp_stats(struct file *file,
return count;
error:
kfree(sptr);
TERR("Usage: echo [+-]ch_id > print_dp_stats\n");
return -EINVAL;
}
@ -637,17 +694,28 @@ static ssize_t gsi_enable_ipc_low(struct file *file,
{
unsigned long missing;
s8 option = 0;
char *sptr;
int ret = 0;
if (sizeof(dbg_buff) < count + 1)
return -EFAULT;
if (count < 2)
return ret;
missing = copy_from_user(dbg_buff, ubuf, min(sizeof(dbg_buff), count));
if (missing)
return -EFAULT;
sptr = kmalloc((count+1) * sizeof(char), GFP_KERNEL);
if (!sptr)
return ret;
dbg_buff[count] = '\0';
if (kstrtos8(dbg_buff, 0, &option))
return -EINVAL;
missing = copy_from_user(sptr, ubuf, count);
if (missing) {
ret = -EFAULT;
goto error;
}
sptr[count] = '\0';
if (kstrtos8(sptr, 0, &option)) {
ret = -EINVAL;
goto error;
}
kfree(sptr);
mutex_lock(&gsi_ctx->mlock);
if (option) {
@ -664,7 +732,9 @@ static ssize_t gsi_enable_ipc_low(struct file *file,
}
mutex_unlock(&gsi_ctx->mlock);
return count;
error:
kfree(sptr);
return ret < 0 ? ret : count;
}
const struct file_operations gsi_ev_dump_ops = {