powerpc/spufs: fix copy_to_user while atomic
authorJeremy Kerr <jk@ozlabs.org>
Tue, 5 May 2020 10:12:50 +0000 (12:12 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 20 Jun 2020 08:24:16 +0000 (10:24 +0200)
[ Upstream commit 88413a6bfbbe2f648df399b62f85c934460b7a4d ]

Currently, we may perform a copy_to_user (through
simple_read_from_buffer()) while holding a context's register_lock,
while accessing the context save area.

This change uses a temporary buffer for the context save area data,
which we then pass to simple_read_from_buffer.

Includes changes from Christoph Hellwig <hch@lst.de>.

Fixes: bf1ab978be23 ("[POWERPC] coredump: Add SPU elf notes to coredump.")
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
[hch: renamed to function to avoid ___-prefixes]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
arch/powerpc/platforms/cell/spufs/file.c

index 0625446..f12b00a 100644 (file)
@@ -2044,8 +2044,9 @@ static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
 static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
-       int ret;
        struct spu_context *ctx = file->private_data;
+       u32 stat, data;
+       int ret;
 
        if (!access_ok(VERIFY_WRITE, buf, len))
                return -EFAULT;
@@ -2054,11 +2055,16 @@ static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_mbox_info_read(ctx, buf, len, pos);
+       stat = ctx->csa.prob.mb_stat_R;
+       data = ctx->csa.prob.pu_mb_R;
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       /* EOF if there's no entry in the mbox */
+       if (!(stat & 0x0000ff))
+               return 0;
+
+       return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
 }
 
 static const struct file_operations spufs_mbox_info_fops = {
@@ -2085,6 +2091,7 @@ static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       u32 stat, data;
        int ret;
 
        if (!access_ok(VERIFY_WRITE, buf, len))
@@ -2094,11 +2101,16 @@ static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_ibox_info_read(ctx, buf, len, pos);
+       stat = ctx->csa.prob.mb_stat_R;
+       data = ctx->csa.priv2.puint_mb_R;
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       /* EOF if there's no entry in the ibox */
+       if (!(stat & 0xff0000))
+               return 0;
+
+       return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
 }
 
 static const struct file_operations spufs_ibox_info_fops = {
@@ -2107,6 +2119,11 @@ static const struct file_operations spufs_ibox_info_fops = {
        .llseek  = generic_file_llseek,
 };
 
+static size_t spufs_wbox_info_cnt(struct spu_context *ctx)
+{
+       return (4 - ((ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8)) * sizeof(u32);
+}
+
 static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
                        char __user *buf, size_t len, loff_t *pos)
 {
@@ -2115,7 +2132,7 @@ static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
        u32 wbox_stat;
 
        wbox_stat = ctx->csa.prob.mb_stat_R;
-       cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
+       cnt = spufs_wbox_info_cnt(ctx);
        for (i = 0; i < cnt; i++) {
                data[i] = ctx->csa.spu_mailbox_data[i];
        }
@@ -2128,7 +2145,8 @@ static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       int ret;
+       u32 data[ARRAY_SIZE(ctx->csa.spu_mailbox_data)];
+       int ret, count;
 
        if (!access_ok(VERIFY_WRITE, buf, len))
                return -EFAULT;
@@ -2137,11 +2155,13 @@ static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_wbox_info_read(ctx, buf, len, pos);
+       count = spufs_wbox_info_cnt(ctx);
+       memcpy(&data, &ctx->csa.spu_mailbox_data, sizeof(data));
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &data,
+                               count * sizeof(u32));
 }
 
 static const struct file_operations spufs_wbox_info_fops = {
@@ -2150,27 +2170,33 @@ static const struct file_operations spufs_wbox_info_fops = {
        .llseek  = generic_file_llseek,
 };
 
-static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
-                       char __user *buf, size_t len, loff_t *pos)
+static void spufs_get_dma_info(struct spu_context *ctx,
+               struct spu_dma_info *info)
 {
-       struct spu_dma_info info;
-       struct mfc_cq_sr *qp, *spuqp;
        int i;
 
-       info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
-       info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
-       info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
-       info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
-       info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
+       info->dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
+       info->dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
+       info->dma_info_status = ctx->csa.spu_chnldata_RW[24];
+       info->dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
+       info->dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
        for (i = 0; i < 16; i++) {
-               qp = &info.dma_info_command_data[i];
-               spuqp = &ctx->csa.priv2.spuq[i];
+               struct mfc_cq_sr *qp = &info->dma_info_command_data[i];
+               struct mfc_cq_sr *spuqp = &ctx->csa.priv2.spuq[i];
 
                qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
                qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
                qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
                qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
        }
+}
+
+static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
+                       char __user *buf, size_t len, loff_t *pos)
+{
+       struct spu_dma_info info;
+
+       spufs_get_dma_info(ctx, &info);
 
        return simple_read_from_buffer(buf, len, pos, &info,
                                sizeof info);
@@ -2180,6 +2206,7 @@ static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
                              size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       struct spu_dma_info info;
        int ret;
 
        if (!access_ok(VERIFY_WRITE, buf, len))
@@ -2189,11 +2216,12 @@ static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_dma_info_read(ctx, buf, len, pos);
+       spufs_get_dma_info(ctx, &info);
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &info,
+                               sizeof(info));
 }
 
 static const struct file_operations spufs_dma_info_fops = {
@@ -2202,13 +2230,31 @@ static const struct file_operations spufs_dma_info_fops = {
        .llseek = no_llseek,
 };
 
+static void spufs_get_proxydma_info(struct spu_context *ctx,
+               struct spu_proxydma_info *info)
+{
+       int i;
+
+       info->proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
+       info->proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
+       info->proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
+
+       for (i = 0; i < 8; i++) {
+               struct mfc_cq_sr *qp = &info->proxydma_info_command_data[i];
+               struct mfc_cq_sr *puqp = &ctx->csa.priv2.puq[i];
+
+               qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
+               qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
+               qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
+               qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
+       }
+}
+
 static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
                        char __user *buf, size_t len, loff_t *pos)
 {
        struct spu_proxydma_info info;
-       struct mfc_cq_sr *qp, *puqp;
        int ret = sizeof info;
-       int i;
 
        if (len < ret)
                return -EINVAL;
@@ -2216,18 +2262,7 @@ static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
        if (!access_ok(VERIFY_WRITE, buf, len))
                return -EFAULT;
 
-       info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
-       info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
-       info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
-       for (i = 0; i < 8; i++) {
-               qp = &info.proxydma_info_command_data[i];
-               puqp = &ctx->csa.priv2.puq[i];
-
-               qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
-               qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
-               qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
-               qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
-       }
+       spufs_get_proxydma_info(ctx, &info);
 
        return simple_read_from_buffer(buf, len, pos, &info,
                                sizeof info);
@@ -2237,17 +2272,19 @@ static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       struct spu_proxydma_info info;
        int ret;
 
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
+       spufs_get_proxydma_info(ctx, &info);
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &info,
+                               sizeof(info));
 }
 
 static const struct file_operations spufs_proxydma_info_fops = {