dsp-kernel: Fix to avoid untrusted pointer dereference
Currently, the compat ioctl call distinguishes itself using a global flag. If a user sends a compat ioctl call followed by a normal ioctl call, it may result in using a user passed address as a kernel address in the fastrpcdriver. To address this issue, consider localizing the compat flag for the ioctl call. Change-Id: Ie8fc724424534102736b8c0bc594720547ab6ff6 Signed-off-by: rnallago <quic_rnallago@quicinc.com>
This commit is contained in:

committed by
rnallago

parent
a4afa6832a
commit
c60ac212aa
@@ -1838,11 +1838,11 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
|
|||||||
struct fastrpc_ioctl_invoke *invoke = &invokefd->inv;
|
struct fastrpc_ioctl_invoke *invoke = &invokefd->inv;
|
||||||
struct fastrpc_channel_ctx *chan = NULL;
|
struct fastrpc_channel_ctx *chan = NULL;
|
||||||
unsigned long irq_flags = 0;
|
unsigned long irq_flags = 0;
|
||||||
uint32_t is_kernel_memory = 0;
|
uint32_t kernel_msg = ((kernel == COMPAT_MSG) ? USER_MSG : kernel);
|
||||||
|
|
||||||
spin_lock(&fl->hlock);
|
spin_lock(&fl->hlock);
|
||||||
if (fl->clst.num_active_ctxs > MAX_PENDING_CTX_PER_SESSION &&
|
if (fl->clst.num_active_ctxs > MAX_PENDING_CTX_PER_SESSION &&
|
||||||
!(kernel || invoke->handle < FASTRPC_STATIC_HANDLE_MAX)) {
|
!(kernel_msg || invoke->handle < FASTRPC_STATIC_HANDLE_MAX)) {
|
||||||
err = -EDQUOT;
|
err = -EDQUOT;
|
||||||
spin_unlock(&fl->hlock);
|
spin_unlock(&fl->hlock);
|
||||||
goto bail;
|
goto bail;
|
||||||
@@ -1873,12 +1873,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
|
|||||||
ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
|
ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
|
||||||
ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
|
ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
|
||||||
|
|
||||||
/* If user message, do not use copy_from_user to copy buffers for
|
K_COPY_FROM_USER(err, kernel, (void *)ctx->lpra, invoke->pra,
|
||||||
* compat driver,as memory is already copied to kernel memory
|
|
||||||
* for compat driver
|
|
||||||
*/
|
|
||||||
is_kernel_memory = ((kernel == USER_MSG) ? (fl->is_compat) : kernel);
|
|
||||||
K_COPY_FROM_USER(err, is_kernel_memory, (void *)ctx->lpra, invoke->pra,
|
|
||||||
bufs * sizeof(*ctx->lpra));
|
bufs * sizeof(*ctx->lpra));
|
||||||
if (err) {
|
if (err) {
|
||||||
ADSPRPC_ERR(
|
ADSPRPC_ERR(
|
||||||
@@ -1961,7 +1956,15 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
|
|||||||
|
|
||||||
spin_lock_irqsave(&chan->ctxlock, irq_flags);
|
spin_lock_irqsave(&chan->ctxlock, irq_flags);
|
||||||
me->jobid[cid]++;
|
me->jobid[cid]++;
|
||||||
for (ii = ((kernel || ctx->handle < FASTRPC_STATIC_HANDLE_MAX)
|
|
||||||
|
/*
|
||||||
|
* To prevent user invocations from exhausting all entries in context
|
||||||
|
* table, it is necessary to reserve a few context table entries for
|
||||||
|
* critical kernel and static RPC calls. The index will begin at 0 for
|
||||||
|
* static handles, while user handles start from
|
||||||
|
* NUM_KERNEL_AND_STATIC_ONLY_CONTEXTS.
|
||||||
|
*/
|
||||||
|
for (ii = ((kernel_msg || ctx->handle < FASTRPC_STATIC_HANDLE_MAX)
|
||||||
? 0 : NUM_KERNEL_AND_STATIC_ONLY_CONTEXTS);
|
? 0 : NUM_KERNEL_AND_STATIC_ONLY_CONTEXTS);
|
||||||
ii < FASTRPC_CTX_MAX; ii++) {
|
ii < FASTRPC_CTX_MAX; ii++) {
|
||||||
if (!chan->ctxtable[ii]) {
|
if (!chan->ctxtable[ii]) {
|
||||||
@@ -3342,7 +3345,7 @@ static void fastrpc_update_invoke_count(uint32_t handle, uint64_t *perf_counter,
|
|||||||
static int fastrpc_check_pd_status(struct fastrpc_file *fl, char *sloc_name);
|
static int fastrpc_check_pd_status(struct fastrpc_file *fl, char *sloc_name);
|
||||||
|
|
||||||
int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
||||||
uint32_t kernel,
|
uint32_t msg_type,
|
||||||
struct fastrpc_ioctl_invoke_async *inv)
|
struct fastrpc_ioctl_invoke_async *inv)
|
||||||
{
|
{
|
||||||
struct smq_invoke_ctx *ctx = NULL;
|
struct smq_invoke_ctx *ctx = NULL;
|
||||||
@@ -3351,6 +3354,7 @@ int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
|||||||
struct timespec64 invoket = {0};
|
struct timespec64 invoket = {0};
|
||||||
uint64_t *perf_counter = NULL;
|
uint64_t *perf_counter = NULL;
|
||||||
bool isasyncinvoke = false, isworkdone = false;
|
bool isasyncinvoke = false, isworkdone = false;
|
||||||
|
uint32_t kernel = (msg_type == COMPAT_MSG) ? USER_MSG : msg_type;
|
||||||
|
|
||||||
cid = fl->cid;
|
cid = fl->cid;
|
||||||
VERIFY(err, VALID_FASTRPC_CID(cid) &&
|
VERIFY(err, VALID_FASTRPC_CID(cid) &&
|
||||||
@@ -3377,9 +3381,6 @@ int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
|||||||
cid, invoke->handle);
|
cid, invoke->handle);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!kernel) {
|
|
||||||
VERIFY(err, 0 == (err = context_restore_interrupted(fl,
|
VERIFY(err, 0 == (err = context_restore_interrupted(fl,
|
||||||
inv, &ctx)));
|
inv, &ctx)));
|
||||||
if (err)
|
if (err)
|
||||||
@@ -3397,7 +3398,7 @@ int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
trace_fastrpc_msg("context_alloc: begin");
|
trace_fastrpc_msg("context_alloc: begin");
|
||||||
VERIFY(err, 0 == (err = context_alloc(fl, kernel, inv, &ctx)));
|
VERIFY(err, 0 == (err = context_alloc(fl, msg_type, inv, &ctx)));
|
||||||
trace_fastrpc_msg("context_alloc: end");
|
trace_fastrpc_msg("context_alloc: end");
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
@@ -3819,7 +3820,7 @@ bail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
||||||
struct fastrpc_ioctl_invoke2 *inv2)
|
struct fastrpc_ioctl_invoke2 *inv2, bool is_compat)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
struct fastrpc_ioctl_invoke_async inv;
|
struct fastrpc_ioctl_invoke_async inv;
|
||||||
@@ -3831,7 +3832,7 @@ int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
|||||||
struct fastrpc_proc_sess_info sess_info;
|
struct fastrpc_proc_sess_info sess_info;
|
||||||
} p;
|
} p;
|
||||||
struct fastrpc_dsp_capabilities *dsp_cap_ptr = NULL;
|
struct fastrpc_dsp_capabilities *dsp_cap_ptr = NULL;
|
||||||
uint32_t size = 0;
|
uint32_t size = 0, kernel = 0;
|
||||||
int err = 0, domain = fl->cid;
|
int err = 0, domain = fl->cid;
|
||||||
|
|
||||||
if (inv2->req == FASTRPC_INVOKE2_ASYNC ||
|
if (inv2->req == FASTRPC_INVOKE2_ASYNC ||
|
||||||
@@ -3859,19 +3860,20 @@ int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
|||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
if (size > inv2->size) {
|
if (size > inv2->size) {
|
||||||
K_COPY_FROM_USER(err, fl->is_compat, &p.inv3, (void *)inv2->invparam,
|
K_COPY_FROM_USER(err, is_compat, &p.inv3, (void *)inv2->invparam,
|
||||||
sizeof(struct fastrpc_ioctl_invoke_async_no_perf));
|
sizeof(struct fastrpc_ioctl_invoke_async_no_perf));
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
memcpy(&p.inv, &p.inv3, sizeof(struct fastrpc_ioctl_invoke_crc));
|
memcpy(&p.inv, &p.inv3, sizeof(struct fastrpc_ioctl_invoke_crc));
|
||||||
memcpy(&p.inv.job, &p.inv3.job, sizeof(p.inv.job));
|
memcpy(&p.inv.job, &p.inv3.job, sizeof(p.inv.job));
|
||||||
} else {
|
} else {
|
||||||
K_COPY_FROM_USER(err, fl->is_compat, &p.inv, (void *)inv2->invparam, size);
|
K_COPY_FROM_USER(err, is_compat, &p.inv, (void *)inv2->invparam, size);
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
kernel = (is_compat) ? COMPAT_MSG : USER_MSG;
|
||||||
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, fl->mode,
|
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, fl->mode,
|
||||||
USER_MSG, &p.inv)));
|
kernel, &p.inv)));
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
break;
|
break;
|
||||||
@@ -3891,7 +3893,7 @@ int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
|||||||
err = -EBADE;
|
err = -EBADE;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
K_COPY_FROM_USER(err, 0, &p.user_concurrency,
|
K_COPY_FROM_USER(err, is_compat, &p.user_concurrency,
|
||||||
(void *)inv2->invparam, size);
|
(void *)inv2->invparam, size);
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
@@ -3915,7 +3917,7 @@ int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
|||||||
err = -EBADE;
|
err = -EBADE;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
K_COPY_FROM_USER(err, fl->is_compat, &p.buff_info,
|
K_COPY_FROM_USER(err, 0, &p.buff_info,
|
||||||
(void *)inv2->invparam, inv2->size);
|
(void *)inv2->invparam, inv2->size);
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
@@ -3930,7 +3932,7 @@ int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
|||||||
err = -EBADE;
|
err = -EBADE;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
K_COPY_FROM_USER(err, fl->is_compat, &p.sess_info,
|
K_COPY_FROM_USER(err, is_compat, &p.sess_info,
|
||||||
(void *)inv2->invparam, inv2->size);
|
(void *)inv2->invparam, inv2->size);
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
@@ -6499,7 +6501,6 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
|
|||||||
fl->dsp_proc_init = 0;
|
fl->dsp_proc_init = 0;
|
||||||
fl->dsp_process_state = PROCESS_CREATE_DEFAULT;
|
fl->dsp_process_state = PROCESS_CREATE_DEFAULT;
|
||||||
fl->is_unsigned_pd = false;
|
fl->is_unsigned_pd = false;
|
||||||
fl->is_compat = false;
|
|
||||||
fl->exit_notif = false;
|
fl->exit_notif = false;
|
||||||
fl->exit_async = false;
|
fl->exit_async = false;
|
||||||
fl->multi_session_support = false;
|
fl->multi_session_support = false;
|
||||||
@@ -7499,7 +7500,7 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num,
|
|||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
VERIFY(err, 0 == (err = fastrpc_internal_invoke2(fl, &p.inv2)));
|
VERIFY(err, 0 == (err = fastrpc_internal_invoke2(fl, &p.inv2, false)));
|
||||||
if (err)
|
if (err)
|
||||||
goto bail;
|
goto bail;
|
||||||
break;
|
break;
|
||||||
|
@@ -347,7 +347,7 @@ static int compat_fastrpc_ioctl_invoke(struct file *filp,
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
|
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
|
||||||
fl->mode, USER_MSG, inv)));
|
fl->mode, COMPAT_MSG, inv)));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ static int compat_fastrpc_ioctl_invoke2(struct file *filp,
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
VERIFY(err, 0 == (err = fastrpc_internal_invoke2(fl, inv)));
|
VERIFY(err, 0 == (err = fastrpc_internal_invoke2(fl, inv, true)));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,7 +975,6 @@ long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
|
|||||||
if (!filp->f_op || !filp->f_op->unlocked_ioctl)
|
if (!filp->f_op || !filp->f_op->unlocked_ioctl)
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
|
|
||||||
fl->is_compat = true;
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case COMPAT_FASTRPC_IOCTL_INVOKE:
|
case COMPAT_FASTRPC_IOCTL_INVOKE:
|
||||||
case COMPAT_FASTRPC_IOCTL_INVOKE_FD:
|
case COMPAT_FASTRPC_IOCTL_INVOKE_FD:
|
||||||
|
@@ -469,9 +469,14 @@ enum fastrpc_buf_type {
|
|||||||
|
|
||||||
/* Types of RPC calls to DSP */
|
/* Types of RPC calls to DSP */
|
||||||
enum fastrpc_msg_type {
|
enum fastrpc_msg_type {
|
||||||
|
/* 64 bit user application invoke message */
|
||||||
USER_MSG = 0,
|
USER_MSG = 0,
|
||||||
|
/* kernel invoke message with zero pid */
|
||||||
KERNEL_MSG_WITH_ZERO_PID,
|
KERNEL_MSG_WITH_ZERO_PID,
|
||||||
|
/* kernel invoke message with non zero pid to kill the PD in DSP */
|
||||||
KERNEL_MSG_WITH_NONZERO_PID,
|
KERNEL_MSG_WITH_NONZERO_PID,
|
||||||
|
/* 32 bit user application invoke message */
|
||||||
|
COMPAT_MSG,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Fastrpc remote pd type */
|
/* Fastrpc remote pd type */
|
||||||
@@ -911,8 +916,6 @@ struct fastrpc_file {
|
|||||||
/* Flag to indicate dynamic process creation status*/
|
/* Flag to indicate dynamic process creation status*/
|
||||||
enum fastrpc_process_create_state dsp_process_state;
|
enum fastrpc_process_create_state dsp_process_state;
|
||||||
bool is_unsigned_pd;
|
bool is_unsigned_pd;
|
||||||
/* Flag to indicate 32 bit driver*/
|
|
||||||
bool is_compat;
|
|
||||||
/* Completion objects and state for dspsignals */
|
/* Completion objects and state for dspsignals */
|
||||||
struct fastrpc_dspsignal *signal_groups[DSPSIGNAL_NUM_SIGNALS / DSPSIGNAL_GROUP_SIZE];
|
struct fastrpc_dspsignal *signal_groups[DSPSIGNAL_NUM_SIGNALS / DSPSIGNAL_GROUP_SIZE];
|
||||||
spinlock_t dspsignals_lock;
|
spinlock_t dspsignals_lock;
|
||||||
@@ -939,7 +942,7 @@ int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
|
|||||||
struct fastrpc_ioctl_invoke_async *inv);
|
struct fastrpc_ioctl_invoke_async *inv);
|
||||||
|
|
||||||
int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
int fastrpc_internal_invoke2(struct fastrpc_file *fl,
|
||||||
struct fastrpc_ioctl_invoke2 *inv2);
|
struct fastrpc_ioctl_invoke2 *inv2, bool is_compat);
|
||||||
|
|
||||||
int fastrpc_internal_munmap(struct fastrpc_file *fl,
|
int fastrpc_internal_munmap(struct fastrpc_file *fl,
|
||||||
struct fastrpc_ioctl_munmap *ud);
|
struct fastrpc_ioctl_munmap *ud);
|
||||||
|
Reference in New Issue
Block a user