From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
Five independent fixes for spufs. There is no dependency between them,
so they can be applied or dropped individually.
1/5 spufs_coredump_extra_notes_write() never drops the reference taken
by coredump_next_context(), so every SPE context written into a
core dump leaks a spu_context. The matching ..._size() path gets
this right.
2/5 do_spu_run() copies out an uninitialized 'status' on the paths
where spufs_run_spu() returns before assigning it, leaking four
bytes of kernel stack to userspace.
3/5 spufs_setattr() calls setattr_copy() without setattr_prepare().
notify_change() leaves that check to the filesystem, so mode and
ownership of a context's files can be changed without the usual
authorization.
4/5 spufs_create_gang() calls unuse_gang() with the parent directory's
i_rwsem held for write, and the resulting simple_recursive_removal()
takes it again as I_MUTEX_CHILD. The task deadlocks against itself
and leaves the spufs directory write-locked.
5/5 The mailbox read/write handlers hold ctx->state_mutex across
put_user()/get_user(), so a userfaultfd region or FUSE-backed user
buffer can stall the context lock for an arbitrary time.
Patches 1-4 are tagged for stable. 5/5 is deliberately not: it changes the
atomicity of multi-element mailbox transfers rather than just adding a
check, so it seems better to let it soak in mainline first.
Build-tested only. I have no Cell or PS3 hardware, so none of this has
been exercised at runtime.
---
Changes in v2:
- Add an Assisted-by: trailer to each patch identifying the tooling, as
requested by Arnd.
- Drop 3/6 from v1 (bound NPC against local store size).
- Link to v1: https://lore.kernel.org/r/20260802-fixes-v1-0-7368423440f4@outlook.com
---
Junrui Luo (5):
powerpc/spufs: fix spu_context leak in coredump
powerpc/spufs: don't leak kernel stack via spu_run
powerpc/spufs: check permissions in spufs_setattr()
powerpc/spufs: fix deadlock on gang creation failure
powerpc/spufs: don't hold state_mutex during user access
arch/powerpc/platforms/cell/spufs/coredump.c | 6 +++-
arch/powerpc/platforms/cell/spufs/file.c | 52 ++++++++++++++++------------
arch/powerpc/platforms/cell/spufs/inode.c | 21 +++++++----
arch/powerpc/platforms/cell/spufs/syscalls.c | 2 +-
4 files changed, 50 insertions(+), 31 deletions(-)
---
base-commit: 02dc699f83d04069fdabc996fc22d47cda47a4a9
change-id: 20260802-fixes-f33361c1b2ae
Best regards,
--
Junrui Luo [off-list ref]
From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
From: Junrui Luo <redacted>
coredump_next_context() returns a spu_context with a reference taken by
get_spu_context(), which the caller must drop.
spufs_coredump_extra_notes_size() does so on all of its exits, but
spufs_coredump_extra_notes_write() never calls put_spu_context(), so
every context dumped through elf_coredump_extra_notes_write() leaks a
reference, including on the success path.
Fix by dropping the reference on each of the three exits of the loop,
mirroring ..._size().
Fixes: 38b407be172d ("powerpc/spufs: Rework fcheck() usage")
Reported-by: Yuhao Jiang <redacted>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <redacted>
---
arch/powerpc/platforms/cell/spufs/coredump.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
@@ -162,13 +162,16 @@ int spufs_coredump_extra_notes_write(struct coredump_params *cprm)fd=0;while((ctx=coredump_next_context(&fd))!=NULL){rc=spu_acquire_saved(ctx);-if(rc)+if(rc){+put_spu_context(ctx);returnrc;+}for(j=0;spufs_coredump_read[j].name!=NULL;j++){rc=spufs_arch_write_note(ctx,j,cprm,fd);if(rc){spu_release_saved(ctx);+put_spu_context(ctx);returnrc;}}
@@ -177,6 +180,7 @@ int spufs_coredump_extra_notes_write(struct coredump_params *cprm)/* start searching the next fd next time */fd++;+put_spu_context(ctx);}return0;
From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
From: Junrui Luo <redacted>
do_spu_run() hands the address of an uninitialized local to
spufs_run_spu() and then copies it out unconditionally:
u32 npc, status;
...
ret = spufs_run_spu(i->i_ctx, &npc, &status);
...
if (ustatus && put_user(status, ustatus))
ret = -EFAULT;
spufs_run_spu() writes through that pointer at exactly one place, the
"out:" label, and two of its exits never reach it: the interruptible
acquisition of ctx->run_mutex returns -ERESTARTSYS directly, and a
failed spu_acquire() jumps to "out_unlock", which sits just after the
assignment.
Initialize status to 0, which is what userspace would have observed had
the assignment been reached anyway: spufs_run_spu() resets
ctx->event_return to 0 on entry, and 0 is the "no events pending" value
for this word.
Fixes: 67207b9664a8 ("[PATCH] spufs: The SPU file system, base")
Reported-by: Yuhao Jiang <redacted>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <redacted>
---
arch/powerpc/platforms/cell/spufs/syscalls.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
From: Junrui Luo <redacted>
spufs_setattr() applies the caller's attributes with setattr_copy() but
never calls setattr_prepare(). notify_change() leaves that to the
filesystem: it runs only may_setattr(), while inode_owner_or_capable()
and the CAP_CHOWN test live inside setattr_prepare(). setattr_copy()
performs no checking of its own.
The handler is installed for every regular spufs file, so mode and
ownership of another user's context files can be changed without the
usual authorization.
Call setattr_prepare() before setattr_copy(). The existing ATTR_SIZE
test stays ahead of it so that resizing a spufs file keeps returning
-EINVAL. &nop_mnt_idmap matches the adjacent setattr_copy() call.
Fixes: 67207b9664a8 ("[PATCH] spufs: The SPU file system, base")
Reported-by: Yuhao Jiang <redacted>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <redacted>
---
arch/powerpc/platforms/cell/spufs/inode.c | 4 ++++
1 file changed, 4 insertions(+)
From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
From: Junrui Luo <redacted>
do_spu_create() enters spufs with the parent directory's i_rwsem held
for write, taken as I_MUTEX_PARENT by start_creating_user_path() and
dropped only by end_creating_path(). When spufs_gang_open() fails inside
that window, spufs_create_gang() cleans up by calling unuse_gang().
The gang was just created, so gang->alive drops to 0 and unuse_gang()
proceeds to simple_recursive_removal(), which takes the parent inode's
i_rwsem as I_MUTEX_CHILD. This leads to the task blocking on an rwsem it
already holds, leaving the spufs directory write-locked. The other two
callers of unuse_gang() do not hold the parent lock: spufs_gang_close()
runs from ->release, and spufs_dir_close() drops the parent lock first.
Tell unuse_gang() which context it is called from, and use
locked_recursive_removal() when the parent is already held. This matches
spufs_rmdir(), which spufs_create_context() already uses for the same
cleanup under the same lock.
Fixes: c134deabf478 ("spufs: fix gang directory lifetimes")
Reported-by: Yuhao Jiang <redacted>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <redacted>
---
arch/powerpc/platforms/cell/spufs/inode.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
From: Junrui Luo <hidden> Date: 2026-08-04 08:50:54
From: Junrui Luo <redacted>
spufs_mbox_read(), spufs_ibox_read() and spufs_wbox_write() take the
context state_mutex with spu_acquire() and only drop it once their
transfer loop has finished, so every put_user()/get_user() in those
loops runs with the mutex held. The faulting address comes from
userspace, so the fault can be made to take arbitrarily long via
userfaultfd region or a FUSE-backed mapping.
Drop the mutex around the user accesses: acquire it per mailbox element,
just long enough for the ctx->ops mailbox operation, and release it
before touching the user buffer.
spufs_switch_log_read() has the same problem but its loop needs the lock
for more than just the copy.
Fixes: cdcc89bb1c6e ("[POWERPC] spufs: make mailbox functions handle multiple elements")
Reported-by: Yuhao Jiang <redacted>
Assisted-by: Claude:claude-opus-5
Signed-off-by: Junrui Luo <redacted>
---
arch/powerpc/platforms/cell/spufs/file.c | 52 ++++++++++++++++++--------------
1 file changed, 29 insertions(+), 23 deletions(-)
@@ -705,29 +708,34 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf,count=spu_acquire(ctx);if(count)-gotoout;+returncount;/* wait only for the first element */-count=0;if(file->f_flags&O_NONBLOCK){if(!spu_ibox_read(ctx,&ibox_data)){-count=-EAGAIN;-gotoout_unlock;+spu_release(ctx);+return-EAGAIN;}}else{count=spufs_wait(ctx->ibox_wq,spu_ibox_read(ctx,&ibox_data));if(count)-gotoout;+returncount;}+spu_release(ctx);/* if we can't write at all, return -EFAULT */count=put_user(ibox_data,udata);if(count)-gotoout_unlock;+returncount;for(count=4,udata++;(count+4)<=len;count+=4,udata++){intret;++ret=spu_acquire(ctx);+if(ret)+break;ret=ctx->ops->ibox_read(ctx,&ibox_data);+spu_release(ctx);if(ret==0)break;/*
@@ -839,40 +844,41 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,count=spu_acquire(ctx);if(count)-gotoout;+returncount;/**makesurewecanatleastwriteoneelement,bywaiting*incaseof!O_NONBLOCK*/-count=0;if(file->f_flags&O_NONBLOCK){if(!spu_wbox_write(ctx,wbox_data)){-count=-EAGAIN;-gotoout_unlock;+spu_release(ctx);+return-EAGAIN;}}else{count=spufs_wait(ctx->wbox_wq,spu_wbox_write(ctx,wbox_data));if(count)-gotoout;+returncount;}-+spu_release(ctx);/* write as much as possible */for(count=4,udata++;(count+4)<=len;count+=4,udata++){intret;+ret=get_user(wbox_data,udata);if(ret)break;+ret=spu_acquire(ctx);+if(ret)+break;ret=spu_wbox_write(ctx,wbox_data);+spu_release(ctx);if(ret==0)break;}-out_unlock:-spu_release(ctx);-out:returncount;}