* [PATCH v3] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
[not found] <53952f011f2c57ad28d6f864317054a2a34922e5.camel@codeconstruct.com.au>
@ 2026-05-27 17:59 ` Karthikeyan KS
2026-05-28 2:39 ` Andrew Jeffery
0 siblings, 1 reply; 7+ messages in thread
From: Karthikeyan KS @ 2026-05-27 17:59 UTC (permalink / raw)
To: andrew
Cc: joel, andrew, linux-arm-kernel, linux-aspeed, linux-kernel,
Karthikeyan KS, stable
put_fifo_with_discard() acts as both producer and consumer on the kfifo:
it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
the IRQ handler without synchronizing with snoop_file_read(), which also
consumes via kfifo_to_user(). On SMP systems this concurrent access can
leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
copy_to_user() past the kmalloc-2k backing store:
usercopy: Kernel memory exposure attempt detected from SLUB object
'kmalloc-2k' (offset 0, size 2049)!
kernel BUG at mm/usercopy.c:99!
Call trace:
usercopy_abort
__check_heap_object
__check_object_size
kfifo_copy_to_user
__kfifo_to_user
snoop_file_read
vfs_read
Reproduced on ast2600-evb (dual-core ARM Cortex-A7) when the host floods
POST codes while userspace reads /dev/aspeed-lpc-snoop0.
Serialize kfifo access with a per-channel spinlock: use spin_lock()/
spin_unlock() in put_fifo_with_discard() (hardirq only) and
spin_lock_irq()/spin_unlock_irq() around kfifo_to_user() in
snoop_file_read().
Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
Cc: stable@vger.kernel.org
Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
---
Andrew,
Thanks for the review.
> The AST2500 has a (single-core) ARM1176JZS
Corrected in v3.
> Don't double-account for the bug
Agreed — the spinlock eliminates the unsynchronized window that
produces the inconsistent pointer state. Clamp removed.
> _irqsave isn't wrong
Changed to spin_lock_irq — fops callbacks always enter with
interrupts enabled.
> Can you provide more details? The 2500 is single-core
The issue was observed on physical AST2600 (dual-core Cortex-A7)
in production under heavy POST code traffic during concurrent
userspace reads. Since the x86 host does not model ARM weak memory
ordering, the race cannot be reproduced naturally in QEMU. The test
module adjusts kfifo pointers to reproduce the post-race state for
deterministic validation.
> AST2600 has a dual-core Cortex-A7, so your bug makes more sense there
Yes, the issue is intermittently observed on production AST2600.
Changes since v2:
- Dropped count clamp
- spin_lock_irqsave -> spin_lock_irq in snoop_file_read
- Fixed platform: AST2600 (dual-core Cortex-A7)
- Trimmed backtrace
- Added Fixes tag
drivers/soc/aspeed/aspeed-lpc-snoop.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index eceeaf8df..ef6697a42 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -60,6 +60,7 @@ struct aspeed_lpc_snoop_model_data {
struct aspeed_lpc_snoop_channel {
struct kfifo fifo;
+ spinlock_t lock;
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -93,7 +94,11 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
+
+ spin_lock_irq(&chan->lock);
ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
+ spin_unlock_irq(&chan->lock);
+
if (ret)
return ret;
@@ -121,9 +126,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
if (!kfifo_initialized(&chan->fifo))
return;
+ spin_lock(&chan->lock);
if (kfifo_is_full(&chan->fifo))
kfifo_skip(&chan->fifo);
kfifo_put(&chan->fifo, val);
+ spin_unlock(&chan->lock);
wake_up_interruptible(&chan->wq);
}
@@ -192,6 +199,7 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
of_device_get_match_data(dev);
init_waitqueue_head(&lpc_snoop->chan[channel].wq);
+ spin_lock_init(&lpc_snoop->chan[channel].lock);
/* Create FIFO datastructure */
rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo,
SNOOP_FIFO_SIZE, GFP_KERNEL);
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
2026-05-27 17:59 ` [PATCH v3] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read Karthikeyan KS
@ 2026-05-28 2:39 ` Andrew Jeffery
2026-06-01 12:52 ` [PATCH v4] " Karthikeyan KS
0 siblings, 1 reply; 7+ messages in thread
From: Andrew Jeffery @ 2026-05-28 2:39 UTC (permalink / raw)
To: Karthikeyan KS
Cc: joel, andrew, linux-arm-kernel, linux-aspeed, linux-kernel,
stable
Hi Karthikeyan,
On Wed, 2026-05-27 at 17:59 +0000, Karthikeyan KS wrote:
> diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> index eceeaf8df..ef6697a42 100644
> --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
> +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> @@ -60,6 +60,7 @@ struct aspeed_lpc_snoop_model_data {
>
> struct aspeed_lpc_snoop_channel {
> struct kfifo fifo;
> + spinlock_t lock;
> wait_queue_head_t wq;
> struct miscdevice miscdev;
> };
> @@ -93,7 +94,11 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> if (ret == -ERESTARTSYS)
> return -EINTR;
> }
> +
> + spin_lock_irq(&chan->lock);
> ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
> + spin_unlock_irq(&chan->lock);
This seems inappropriate and I expect is flagged if you compile with
CONFIG_PROVE_LOCKING=y or CONFIG_DEBUG_ATOMIC_SLEEP=y. I suggest both
if you're not already.
Further, I hit conflicts when applying your change on v7.1-rc5. Can you
please ensure you develop, build and test on recent releases.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
2026-05-28 2:39 ` Andrew Jeffery
@ 2026-06-01 12:52 ` Karthikeyan KS
2026-06-10 2:26 ` Andrew Jeffery
0 siblings, 1 reply; 7+ messages in thread
From: Karthikeyan KS @ 2026-06-01 12:52 UTC (permalink / raw)
To: andrew
Cc: joel, andrew, linux-arm-kernel, linux-aspeed, linux-kernel,
Karthikeyan KS, stable
put_fifo_with_discard() acts as both producer and consumer on the kfifo:
it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
the IRQ handler without synchronizing with snoop_file_read(), which also
consumes via kfifo_to_user(). On SMP systems this concurrent access can
leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
copy_to_user() past the kmalloc-2k backing store:
usercopy: Kernel memory exposure attempt detected from SLUB object
'kmalloc-2k' (offset 0, size 2049)!
kernel BUG at mm/usercopy.c!
Call trace:
usercopy_abort
__check_heap_object
__check_object_size
kfifo_copy_to_user
__kfifo_to_user
snoop_file_read
vfs_read
Serialize kfifo access with a per-channel spinlock. copy_to_user()
runs after dropping the lock, since it may sleep on a page fault.
Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
Cc: stable@vger.kernel.org
Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
---
Andrew,
Thanks for the review.
> This seems inappropriate and I expect is flagged if you compile with
> CONFIG_PROVE_LOCKING=y or CONFIG_DEBUG_ATOMIC_SLEEP=y
v4 drains the kfifo into a kernel buffer via kfifo_out() under
the lock, then performs copy_to_user() after dropping it.
(cf. drivers/gpio/gpiolib-cdev.c, which drains under its event lock
and copies outside it.)
> ensure you develop, build and test on recent releases
Tested on both v7.1-rc5 and v7.1-rc6 with PROVE_LOCKING,
DEBUG_ATOMIC_SLEEP and HARDENED_USERCOPY enabled: read path
round-trips correctly, no lockdep splats, no atomic-sleep
warnings, no usercopy aborts.
Changes since v3:
- Replaced kfifo_to_user() with kfifo_out() + copy_to_user()
to avoid sleeping under spinlock
- Rebased onto v7.1-rc6
drivers/soc/aspeed/aspeed-lpc-snoop.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index b03310c0830d..0fe463020e25 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -74,6 +74,7 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
+ spinlock_t lock;
struct kfifo fifo;
wait_queue_head_t wq;
struct miscdevice miscdev;
@@ -115,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
{
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
unsigned int copied;
+ u8 *buf;
int ret = 0;
if (kfifo_is_empty(&chan->fifo)) {
@@ -125,11 +127,22 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
- ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
- if (ret)
- return ret;
- return copied;
+ buf = kmalloc(SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ spin_lock_irq(&chan->lock);
+ copied = kfifo_out(&chan->fifo, buf,
+ min_t(size_t, count, SNOOP_FIFO_SIZE));
+ spin_unlock_irq(&chan->lock);
+
+ ret = copied;
+ if (copied && copy_to_user(buffer, buf, copied))
+ ret = -EFAULT;
+
+ kfree(buf);
+ return ret;
}
static __poll_t snoop_file_poll(struct file *file,
@@ -153,9 +166,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
if (!kfifo_initialized(&chan->fifo))
return;
+ spin_lock(&chan->lock);
if (kfifo_is_full(&chan->fifo))
kfifo_skip(&chan->fifo);
kfifo_put(&chan->fifo, val);
+ spin_unlock(&chan->lock);
wake_up_interruptible(&chan->wq);
}
@@ -228,6 +243,7 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
return -EBUSY;
init_waitqueue_head(&channel->wq);
+ spin_lock_init(&channel->lock);
channel->cfg = cfg;
channel->miscdev.minor = MISC_DYNAMIC_MINOR;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
2026-06-01 12:52 ` [PATCH v4] " Karthikeyan KS
@ 2026-06-10 2:26 ` Andrew Jeffery
2026-06-10 17:23 ` [PATCH v5] " Karthikeyan KS
0 siblings, 1 reply; 7+ messages in thread
From: Andrew Jeffery @ 2026-06-10 2:26 UTC (permalink / raw)
To: Karthikeyan KS
Cc: joel, andrew, linux-arm-kernel, linux-aspeed, linux-kernel,
stable
Hi Karthikeyan,
On Mon, 2026-06-01 at 12:52 +0000, Karthikeyan KS wrote:
> put_fifo_with_discard() acts as both producer and consumer on the kfifo:
> it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
> the IRQ handler without synchronizing with snoop_file_read(), which also
> consumes via kfifo_to_user(). On SMP systems this concurrent access can
> leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
> to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
> copy_to_user() past the kmalloc-2k backing store:
>
> usercopy: Kernel memory exposure attempt detected from SLUB object
> 'kmalloc-2k' (offset 0, size 2049)!
> kernel BUG at mm/usercopy.c!
> Call trace:
> usercopy_abort
> __check_heap_object
> __check_object_size
> kfifo_copy_to_user
> __kfifo_to_user
> snoop_file_read
> vfs_read
>
>
> Serialize kfifo access with a per-channel spinlock. copy_to_user()
> runs after dropping the lock, since it may sleep on a page fault.
>
> Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
> Cc: stable@vger.kernel.org
> Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
> ---
> Andrew,
>
> Thanks for the review.
>
> > This seems inappropriate and I expect is flagged if you compile with
> > CONFIG_PROVE_LOCKING=y or CONFIG_DEBUG_ATOMIC_SLEEP=y
>
> v4 drains the kfifo into a kernel buffer via kfifo_out() under
> the lock, then performs copy_to_user() after dropping it.
> (cf. drivers/gpio/gpiolib-cdev.c, which drains under its event lock
> and copies outside it.)
>
> > ensure you develop, build and test on recent releases
>
> Tested on both v7.1-rc5 and v7.1-rc6 with PROVE_LOCKING,
> DEBUG_ATOMIC_SLEEP and HARDENED_USERCOPY enabled: read path
> round-trips correctly, no lockdep splats, no atomic-sleep
> warnings, no usercopy aborts.
>
> Changes since v3:
> - Replaced kfifo_to_user() with kfifo_out() + copy_to_user()
> to avoid sleeping under spinlock
> - Rebased onto v7.1-rc6
>
> drivers/soc/aspeed/aspeed-lpc-snoop.c | 24 ++++++++++++++++++++----
> 1 file changed, 20 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> index b03310c0830d..0fe463020e25 100644
> --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
> +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> @@ -74,6 +74,7 @@ struct aspeed_lpc_snoop_channel_cfg {
> struct aspeed_lpc_snoop_channel {
> const struct aspeed_lpc_snoop_channel_cfg *cfg;
> bool enabled;
> + spinlock_t lock;
> struct kfifo fifo;
> wait_queue_head_t wq;
> struct miscdevice miscdev;
> @@ -115,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> {
> struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
> unsigned int copied;
> + u8 *buf;
Can use the cleanup helpers again here:
u8 *buf __free(kfree) = NULL;
> int ret = 0;
>
> if (kfifo_is_empty(&chan->fifo)) {
> @@ -125,11 +127,22 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> if (ret == -ERESTARTSYS)
> return -EINTR;
> }
> - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
> - if (ret)
> - return ret;
>
> - return copied;
> + buf = kmalloc(SNOOP_FIFO_SIZE, GFP_KERNEL);
I expect using count clamped to SNOOP_FIFO_SIZE might be a better
option here? The clamp below can be moved here.
I'm not enamoured with the bounce buffer, but I guess it solves the
problem.
> + if (!buf)
> + return -ENOMEM;
> +
> + spin_lock_irq(&chan->lock);
> + copied = kfifo_out(&chan->fifo, buf,
> + min_t(size_t, count, SNOOP_FIFO_SIZE));
This is handled by kfifo_out() as discussed previously, but also see
the above. You may want to check that count doesn't exceed UINT_MAX
though, in the event that SIZE_MAX > UINT_MAX.
> + spin_unlock_irq(&chan->lock);
Recently the kernel gained cleanup helpers. scoped_guard() would be
handy here, however the kfifo API also provides kfifo_out_spinlocked().
I'd use that as it is at least idiomatic.
> +
> + ret = copied;
> + if (copied && copy_to_user(buffer, buf, copied))
> + ret = -EFAULT;
> +
> + kfree(buf);
> + return ret;
> }
>
> static __poll_t snoop_file_poll(struct file *file,
> @@ -153,9 +166,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
> {
> if (!kfifo_initialized(&chan->fifo))
> return;
> + spin_lock(&chan->lock);
> if (kfifo_is_full(&chan->fifo))
> kfifo_skip(&chan->fifo);
> kfifo_put(&chan->fifo, val);
> + spin_unlock(&chan->lock);
I prefer we use scoped_guard() here.
> wake_up_interruptible(&chan->wq);
> }
>
> @@ -228,6 +243,7 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
> return -EBUSY;
>
> init_waitqueue_head(&channel->wq);
> + spin_lock_init(&channel->lock);
>
> channel->cfg = cfg;
> channel->miscdev.minor = MISC_DYNAMIC_MINOR;
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v5] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
2026-06-10 2:26 ` Andrew Jeffery
@ 2026-06-10 17:23 ` Karthikeyan KS
2026-06-11 2:50 ` Andrew Jeffery
0 siblings, 1 reply; 7+ messages in thread
From: Karthikeyan KS @ 2026-06-10 17:23 UTC (permalink / raw)
To: andrew
Cc: joel, andrew, Kees Cook, linux-arm-kernel, linux-aspeed,
linux-kernel, linux-hardening, Karthikeyan KS, stable
put_fifo_with_discard() acts as both producer and consumer on the kfifo:
it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
the IRQ handler without synchronizing with snoop_file_read(), which also
consumes via kfifo_to_user(). On SMP systems this concurrent access can
leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
copy_to_user() past the kmalloc-2k backing store:
usercopy: Kernel memory exposure attempt detected from SLUB object
'kmalloc-2k' (offset 0, size 2049)!
kernel BUG at mm/usercopy.c!
Call trace:
usercopy_abort
__check_heap_object
__check_object_size
kfifo_copy_to_user
__kfifo_to_user
snoop_file_read
vfs_read
Serialize kfifo access with a per-channel spinlock. The reader drains
into a bounce buffer under the lock with kfifo_out_spinlocked() and then
copies to userspace after dropping it, since copy_to_user() may sleep on
a page fault.
Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
Cc: stable@vger.kernel.org
Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
---
Andrew,
Thanks for the review.
Changes since v4:
- Use __free(kfree) for automatic cleanup
- Allocate clamped count instead of full SNOOP_FIFO_SIZE
- Use kfifo_out_spinlocked() in snoop_file_read
- Use scoped_guard(spinlock) in put_fifo_with_discard
drivers/soc/aspeed/aspeed-lpc-snoop.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index b03310c0830d..c9c87a794228 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -11,6 +11,7 @@
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dev_printk.h>
#include <linux/interrupt.h>
@@ -74,6 +75,7 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
+ spinlock_t lock; /* serialises @fifo: irq producer vs reader */
struct kfifo fifo;
wait_queue_head_t wq;
struct miscdevice miscdev;
@@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
+ u8 *buf __free(kfree) = NULL;
unsigned int copied;
int ret = 0;
@@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
- ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
- if (ret)
- return ret;
+
+ count = min_t(size_t, count, SNOOP_FIFO_SIZE);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
+ if (copied && copy_to_user(buffer, buf, copied))
+ return -EFAULT;
return copied;
}
@@ -153,9 +163,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
if (!kfifo_initialized(&chan->fifo))
return;
- if (kfifo_is_full(&chan->fifo))
- kfifo_skip(&chan->fifo);
- kfifo_put(&chan->fifo, val);
+ scoped_guard(spinlock, &chan->lock) {
+ if (kfifo_is_full(&chan->fifo))
+ kfifo_skip(&chan->fifo);
+ kfifo_put(&chan->fifo, val);
+ }
wake_up_interruptible(&chan->wq);
}
@@ -228,6 +240,7 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
return -EBUSY;
init_waitqueue_head(&channel->wq);
+ spin_lock_init(&channel->lock);
channel->cfg = cfg;
channel->miscdev.minor = MISC_DYNAMIC_MINOR;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v5] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
2026-06-10 17:23 ` [PATCH v5] " Karthikeyan KS
@ 2026-06-11 2:50 ` Andrew Jeffery
[not found] ` <CAP_JKPu9MTpMUZmg9BY3sxGhmBzgR0E6HnvAT7sQjVUpQp0dSQ@mail.gmail.com>
0 siblings, 1 reply; 7+ messages in thread
From: Andrew Jeffery @ 2026-06-11 2:50 UTC (permalink / raw)
To: Karthikeyan KS
Cc: joel, andrew, Kees Cook, linux-arm-kernel, linux-aspeed,
linux-kernel, linux-hardening, stable
Hi Karthikeyan,
On Wed, 2026-06-10 at 17:23 +0000, Karthikeyan KS wrote:
> put_fifo_with_discard() acts as both producer and consumer on the kfifo:
> it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
> the IRQ handler without synchronizing with snoop_file_read(), which also
> consumes via kfifo_to_user(). On SMP systems this concurrent access can
> leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
> to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
> copy_to_user() past the kmalloc-2k backing store:
>
> usercopy: Kernel memory exposure attempt detected from SLUB object
> 'kmalloc-2k' (offset 0, size 2049)!
> kernel BUG at mm/usercopy.c!
> Call trace:
> usercopy_abort
> __check_heap_object
> __check_object_size
> kfifo_copy_to_user
> __kfifo_to_user
> snoop_file_read
> vfs_read
>
> Serialize kfifo access with a per-channel spinlock. The reader drains
> into a bounce buffer under the lock with kfifo_out_spinlocked() and then
> copies to userspace after dropping it, since copy_to_user() may sleep on
> a page fault.
>
> Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
> Cc: stable@vger.kernel.org
> Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
> ---
> Andrew,
>
> Thanks for the review.
>
> Changes since v4:
> - Use __free(kfree) for automatic cleanup
> - Allocate clamped count instead of full SNOOP_FIFO_SIZE
> - Use kfifo_out_spinlocked() in snoop_file_read
> - Use scoped_guard(spinlock) in put_fifo_with_discard
>
> drivers/soc/aspeed/aspeed-lpc-snoop.c | 25 +++++++++++++++++++------
> 1 file changed, 19 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> index b03310c0830d..c9c87a794228 100644
> --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
> +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> @@ -11,6 +11,7 @@
> */
>
> #include <linux/bitops.h>
> +#include <linux/cleanup.h>
> #include <linux/clk.h>
> #include <linux/dev_printk.h>
> #include <linux/interrupt.h>
> @@ -74,6 +75,7 @@ struct aspeed_lpc_snoop_channel_cfg {
> struct aspeed_lpc_snoop_channel {
> const struct aspeed_lpc_snoop_channel_cfg *cfg;
> bool enabled;
> + spinlock_t lock; /* serialises @fifo: irq producer vs reader */
I'd prefer we avoid trailing comments, which it seems you've added this
time around. Since you did that ...
> struct kfifo fifo;
... in this specific case we can improve on the comment, with:
struct kfifo fifo __guarded_by(&lock);
More details here:
https://docs.kernel.org/dev-tools/context-analysis.html
Adding a change along these lines currently produces:
../drivers/soc/aspeed/aspeed-lpc-snoop.c:164:32: warning: reading variable 'fifo' requires holding spinlock '&aspeed_lpc_snoop_channel::lock' [-Wthread-safety-analysis]
164 | if (!kfifo_initialized(&chan->fifo))
| ^
I ended up applying this on top of your patch:
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..9cba7be8c395 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+
+CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index 9165a543a250..7fa1a345acac 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -75,8 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
- spinlock_t lock; /* serialises @fifo: irq producer vs reader */
- struct kfifo fifo;
+ spinlock_t lock;
+ struct kfifo fifo __guarded_by(&lock);
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -161,9 +161,9 @@ static const struct file_operations snoop_fops = {
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
- if (!kfifo_initialized(&chan->fifo))
- return;
scoped_guard(spinlock, &chan->lock) {
+ if (!kfifo_initialized(&chan->fifo))
+ return;
if (kfifo_is_full(&chan->fifo))
kfifo_skip(&chan->fifo);
kfifo_put(&chan->fifo, val);
@@ -240,7 +240,6 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
return -EBUSY;
init_waitqueue_head(&channel->wq);
- spin_lock_init(&channel->lock);
channel->cfg = cfg;
channel->miscdev.minor = MISC_DYNAMIC_MINOR;
@@ -252,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
if (!channel->miscdev.name)
return -ENOMEM;
- rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
- if (rc)
- return rc;
+ scoped_guard(spinlock_init, &channel->lock) {
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+ }
rc = misc_register(&channel->miscdev);
if (rc)
I prefer that we add the annotation as the compiler analysis provides
some comfort in contrast to the comment.
Otherwise, the rest of the fix seems okay to me.
Andrew
> wait_queue_head_t wq;
> struct miscdevice miscdev;
> @@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> size_t count, loff_t *ppos)
> {
> struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
> + u8 *buf __free(kfree) = NULL;
> unsigned int copied;
> int ret = 0;
>
> @@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> if (ret == -ERESTARTSYS)
> return -EINTR;
> }
> - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
> - if (ret)
> - return ret;
> +
> + count = min_t(size_t, count, SNOOP_FIFO_SIZE);
> +
> + buf = kmalloc(count, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
> + if (copied && copy_to_user(buffer, buf, copied))
> + return -EFAULT;
>
> return copied;
> }
> @@ -153,9 +163,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
> {
> if (!kfifo_initialized(&chan->fifo))
> return;
> - if (kfifo_is_full(&chan->fifo))
> - kfifo_skip(&chan->fifo);
> - kfifo_put(&chan->fifo, val);
> + scoped_guard(spinlock, &chan->lock) {
> + if (kfifo_is_full(&chan->fifo))
> + kfifo_skip(&chan->fifo);
> + kfifo_put(&chan->fifo, val);
> + }
> wake_up_interruptible(&chan->wq);
> }
>
> @@ -228,6 +240,7 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
> return -EBUSY;
>
> init_waitqueue_head(&channel->wq);
> + spin_lock_init(&channel->lock);
>
> channel->cfg = cfg;
> channel->miscdev.minor = MISC_DYNAMIC_MINOR;
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
[not found] ` <CAP_JKPu9MTpMUZmg9BY3sxGhmBzgR0E6HnvAT7sQjVUpQp0dSQ@mail.gmail.com>
@ 2026-06-12 0:39 ` Andrew Jeffery
0 siblings, 0 replies; 7+ messages in thread
From: Andrew Jeffery @ 2026-06-12 0:39 UTC (permalink / raw)
To: karthikeyan K S
Cc: joel, andrew, Kees Cook, linux-arm-kernel, linux-aspeed,
linux-kernel, linux-hardening, stable
On Thu, 2026-06-11 at 23:01 +0530, karthikeyan K S wrote:
> Thanks Andrew. The __guarded_by annotation and context analysis integration
> look good, I wasn't aware of that infrastructure.
> Thanks for applying those changes on top.
Sorry, on reflection I chose my words poorly there. I applied that
patch I pasted on top as an experiment on my end. I haven't yet added
your patch to the fixes branch.
Do you mind integrating that rework, testing, and then sending the
result?
Cheers,
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-12 0:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <53952f011f2c57ad28d6f864317054a2a34922e5.camel@codeconstruct.com.au>
2026-05-27 17:59 ` [PATCH v3] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read Karthikeyan KS
2026-05-28 2:39 ` Andrew Jeffery
2026-06-01 12:52 ` [PATCH v4] " Karthikeyan KS
2026-06-10 2:26 ` Andrew Jeffery
2026-06-10 17:23 ` [PATCH v5] " Karthikeyan KS
2026-06-11 2:50 ` Andrew Jeffery
[not found] ` <CAP_JKPu9MTpMUZmg9BY3sxGhmBzgR0E6HnvAT7sQjVUpQp0dSQ@mail.gmail.com>
2026-06-12 0:39 ` Andrew Jeffery
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox