lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <bc129d87b5e6b42a330f83b2b02e43f98cf0e0f7.camel@collabora.com>
Date: Tue, 27 May 2025 08:57:19 -0400
From: Nicolas Dufresne <nicolas.dufresne@...labora.com>
To: "jackson.lee" <jackson.lee@...psnmedia.com>, "mchehab@...nel.org"	
 <mchehab@...nel.org>, "hverkuil-cisco@...all.nl"
 <hverkuil-cisco@...all.nl>,  "sebastian.fricke@...labora.com"	
 <sebastian.fricke@...labora.com>, "bob.beckett@...labora.com"	
 <bob.beckett@...labora.com>, "dafna.hirschfeld@...labora.com"	
 <dafna.hirschfeld@...labora.com>
Cc: "linux-media@...r.kernel.org" <linux-media@...r.kernel.org>, 
 "linux-kernel@...r.kernel.org"	 <linux-kernel@...r.kernel.org>,
 "lafley.kim" <lafley.kim@...psnmedia.com>,  "b-brnich@...com"	
 <b-brnich@...com>, "hverkuil@...all.nl" <hverkuil@...all.nl>, Nas Chung	
 <nas.chung@...psnmedia.com>
Subject: Re: [PATCH v2 1/7] media: chips-media: wave5: Fix Null reference
 while testing fluster

Hi,

Le mardi 27 mai 2025 à 04:05 +0000, jackson.lee a écrit :
> Hi Nicolas
> 
> 
> > -----Original Message-----
> > From: Nicolas Dufresne <nicolas.dufresne@...labora.com>
> > Sent: Saturday, May 24, 2025 2:20 AM
> > To: jackson.lee <jackson.lee@...psnmedia.com>; mchehab@...nel.org;
> > hverkuil-cisco@...all.nl; sebastian.fricke@...labora.com;
> > bob.beckett@...labora.com; dafna.hirschfeld@...labora.com
> > Cc: linux-media@...r.kernel.org; linux-kernel@...r.kernel.org; lafley.kim
> > <lafley.kim@...psnmedia.com>; b-brnich@...com; hverkuil@...all.nl; Nas
> > Chung <nas.chung@...psnmedia.com>
> > Subject: Re: [PATCH v2 1/7] media: chips-media: wave5: Fix Null reference
> > while testing fluster
> > 
> > Hi,
> > 
> > Le jeudi 22 mai 2025 à 16:26 +0900, Jackson.lee a écrit :
> > > From: Jackson Lee <jackson.lee@...psnmedia.com>
> > > 
> > > When multi instances are created/destroyed, many interrupts happens or
> > > structures for decoder are removed.
> > > "struct vpu_instance" this structure is shared for all flow in
> > > decoder, so if the structure is not protected by lock, Null reference
> > > exception could happens sometimes.
> > > IRQ Handler was spilt to two phases and Lock was added as well.
> > > 
> > > Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer")
> > > Signed-off-by: Jackson Lee <jackson.lee@...psnmedia.com>
> > > Signed-off-by: Nas Chung <nas.chung@...psnmedia.com>
> > > ---
> > >  .../platform/chips-media/wave5/wave5-helper.c | 10 ++-
> > >  .../chips-media/wave5/wave5-vpu-dec.c         |  5 ++
> > >  .../chips-media/wave5/wave5-vpu-enc.c         |  5 ++
> > >  .../platform/chips-media/wave5/wave5-vpu.c    | 69
> > > ++++++++++++++++---
> > >  .../platform/chips-media/wave5/wave5-vpuapi.h |  6 ++
> > >  5 files changed, 86 insertions(+), 9 deletions(-)
> > > 
> > > diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c
> > > b/drivers/media/platform/chips-media/wave5/wave5-
> > > helper.c
> > > index 2c9d8cbca6e4..5d9969bb7ada 100644
> > > --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c
> > > +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
> > > @@ -49,7 +49,7 @@ void wave5_cleanup_instance(struct vpu_instance *inst)
> > >  		v4l2_fh_del(&inst->v4l2_fh);
> > >  		v4l2_fh_exit(&inst->v4l2_fh);
> > >  	}
> > > -	list_del_init(&inst->list);
> > > +	kfifo_free(&inst->irq_status);
> > >  	ida_free(&inst->dev->inst_ida, inst->id);
> > >  	kfree(inst->codec_info);
> > >  	kfree(inst);
> > > @@ -61,8 +61,16 @@ int wave5_vpu_release_device(struct file *filp,
> > >  {
> > >  	struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
> > >  	int ret = 0;
> > > +	unsigned long flags;
> > > 
> > >  	v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
> > > +	ret = mutex_lock_interruptible(&inst->dev->irq_lock);
> > > +	if (ret)
> > > +		return ret;
> > 
> > This is quite some heavy locking, why do you need both the mutex and the
> > spinlock ?
> 
> 
> To prevent Null reference exception, the existing irq handler were separated to two modules
> One is to queue interrupt reason in the irq hander, the other is to call  the wave5_vpu_dec_finish_decode to get
> decoded frame.
> The list of instances should be protected between all flow of the decoding process, but to protect the list in the
> irq_handler, spin lock should be used, and 
> mutex should be used in the irq_thread because spin lock is not able to be used because mutex is being used in the
> wave5_vpu_dec_finish_decode.
> So to protect the list in the release function, spin lock and mutex were used.

I don't have a better solution without massively refactoring this driver,
so I'd say its fine. But all this explanation should be found in the code
as comment as its not obvious and quite easy to break.

regards,
Nicolas

> 
> 
> 
> > 
> > > +	spin_lock_irqsave(&inst->dev->irq_spinlock, flags);
> > > +	list_del_init(&inst->list);
> > > +	spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags);
> > > +	mutex_unlock(&inst->dev->irq_lock);
> > >  	if (inst->state != VPU_INST_STATE_NONE) {
> > >  		u32 fail_res;
> > > 
> > > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> > > b/drivers/media/platform/chips- media/wave5/wave5-vpu-dec.c index
> > > fd71f0c43ac3..32de43de1870 100644
> > > --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> > > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> > > @@ -1811,6 +1811,11 @@ static int wave5_vpu_open_dec(struct file *filp)
> > >  	inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> > > 
> > >  	init_completion(&inst->irq_done);
> > > +	ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> > > +	if (ret) {
> > > +		dev_err(inst->dev->dev, "failed to allocate fifo\n");
> > > +		goto cleanup_inst;
> > > +	}
> > > 
> > >  	inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> > >  	if (inst->id < 0) {
> > > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> > > b/drivers/media/platform/chips- media/wave5/wave5-vpu-enc.c index
> > > 1e5fc5f8b856..52a1a00fd9bb 100644
> > > --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> > > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> > > @@ -1760,6 +1760,11 @@ static int wave5_vpu_open_enc(struct file *filp)
> > >  	inst->frame_rate = 30;
> > > 
> > >  	init_completion(&inst->irq_done);
> > > +	ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> > > +	if (ret) {
> > > +		dev_err(inst->dev->dev, "failed to allocate fifo\n");
> > > +		goto cleanup_inst;
> > > +	}
> > > 
> > >  	inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> > >  	if (inst->id < 0) {
> > > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> > > b/drivers/media/platform/chips-media/wave5/wave5-
> > > vpu.c
> > > index e1715d3f43b0..a2c09523d76b 100644
> > > --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> > > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> > > @@ -51,8 +51,11 @@ static void wave5_vpu_handle_irq(void *dev_id)
> > >  	u32 seq_done;
> > >  	u32 cmd_done;
> > >  	u32 irq_reason;
> > > -	struct vpu_instance *inst;
> > > +	u32 irq_subreason;
> > > +	struct vpu_instance *inst, *tmp;
> > >  	struct vpu_device *dev = dev_id;
> > > +	int val;
> > > +	unsigned long flags;
> > > 
> > >  	irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
> > >  	seq_done = wave5_vdi_read_register(dev,
> > > W5_RET_SEQ_DONE_INSTANCE_INFO); @@ -60,7 +63,8 @@ static void
> > wave5_vpu_handle_irq(void *dev_id)
> > >  	wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
> > >  	wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
> > > 
> > > -	list_for_each_entry(inst, &dev->instances, list) {
> > > +	spin_lock_irqsave(&dev->irq_spinlock, flags);
> > > +	list_for_each_entry_safe(inst, tmp, &dev->instances, list) {
> > > 
> > >  		if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
> > >  		    irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { @@ -82,14
> > +86,22
> > > @@ static void wave5_vpu_handle_irq(void *dev_id)
> > >  		    irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
> > >  			if (cmd_done & BIT(inst->id)) {
> > >  				cmd_done &= ~BIT(inst->id);
> > > -				wave5_vdi_write_register(dev,
> > W5_RET_QUEUE_CMD_DONE_INST,
> > > -							 cmd_done);
> > > -				inst->ops->finish_process(inst);
> > > +				if (dev->irq >= 0) {
> > > +					irq_subreason =
> > > +						wave5_vdi_read_register(dev,
> > W5_VPU_VINT_REASON);
> > > +					if (!(irq_subreason &
> > BIT(INT_WAVE5_DEC_PIC)))
> > > +						wave5_vdi_write_register(dev,
> > > +
> > W5_RET_QUEUE_CMD_DONE_INST,
> > > +									 cmd_done);
> > > +				}
> > > +				val = BIT(INT_WAVE5_DEC_PIC);
> > > +				kfifo_in(&inst->irq_status, &val, sizeof(int));
> > >  			}
> > >  		}
> > > -
> > > -		wave5_vpu_clear_interrupt(inst, irq_reason);
> > >  	}
> > > +	spin_unlock_irqrestore(&dev->irq_spinlock, flags);
> > > +
> > > +	up(&dev->irq_sem);
> > >  }
> > > 
> > >  static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) @@
> > > -121,6 +133,35 @@ static enum hrtimer_restart
> > wave5_vpu_timer_callback(struct hrtimer *timer)
> > >  	return HRTIMER_RESTART;
> > >  }
> > > 
> > > +static int irq_thread(void *data)
> > > +{
> > > +	struct vpu_device *dev = (struct vpu_device *)data;
> > > +	struct vpu_instance *inst, *tmp;
> > > +	int irq_status, ret;
> > > +
> > > +	while (!kthread_should_stop()) {
> > > +		if (down_interruptible(&dev->irq_sem))
> > > +			continue;
> > > +
> > > +		if (kthread_should_stop())
> > > +			break;
> > > +
> > > +		mutex_lock(&dev->irq_lock);
> > > +		list_for_each_entry_safe(inst, tmp, &dev->instances, list) {
> > > +			while (kfifo_len(&inst->irq_status)) {
> > > +				ret = kfifo_out(&inst->irq_status, &irq_status,
> > sizeof(int));
> > > +				if (!ret)
> > > +					break;
> > > +
> > > +				inst->ops->finish_process(inst);
> > > +			}
> > > +		}
> > > +		mutex_unlock(&dev->irq_lock);
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >  static int wave5_vpu_load_firmware(struct device *dev, const char
> > *fw_name,
> > >  				   u32 *revision)
> > >  {
> > > @@ -224,6 +265,8 @@ static int wave5_vpu_probe(struct platform_device
> > > *pdev)
> > > 
> > >  	mutex_init(&dev->dev_lock);
> > >  	mutex_init(&dev->hw_lock);
> > > +	mutex_init(&dev->irq_lock);
> > > +	spin_lock_init(&dev->irq_spinlock);
> > >  	dev_set_drvdata(&pdev->dev, dev);
> > >  	dev->dev = &pdev->dev;
> > > 
> > > @@ -266,6 +309,10 @@ static int wave5_vpu_probe(struct platform_device
> > *pdev)
> > >  	}
> > >  	dev->product = wave5_vpu_get_product_id(dev);
> > > 
> > > +	sema_init(&dev->irq_sem, 1);
> > > +	INIT_LIST_HEAD(&dev->instances);
> > > +	dev->irq_thread = kthread_run(irq_thread, dev, "irq thread");
> > > +
> > >  	dev->irq = platform_get_irq(pdev, 0);
> > >  	if (dev->irq < 0) {
> > >  		dev_err(&pdev->dev, "failed to get irq resource, falling
> > back to
> > > polling\n"); @@ -288,7 +335,6 @@ static int wave5_vpu_probe(struct
> > platform_device *pdev)
> > >  		}
> > >  	}
> > > 
> > > -	INIT_LIST_HEAD(&dev->instances);
> > >  	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> > >  	if (ret) {
> > >  		dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret);
> > @@
> > > -351,6 +397,12 @@ static void wave5_vpu_remove(struct platform_device
> > > *pdev)
> > >  {
> > >  	struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
> > > 
> > > +	if (dev->irq_thread) {
> > > +		kthread_stop(dev->irq_thread);
> > > +		up(&dev->irq_sem);
> > > +		dev->irq_thread = NULL;
> > > +	}
> > > +
> > >  	if (dev->irq < 0) {
> > >  		kthread_destroy_worker(dev->worker);
> > >  		hrtimer_cancel(&dev->hrtimer);
> > > @@ -361,6 +413,7 @@ static void wave5_vpu_remove(struct
> > > platform_device *pdev)
> > > 
> > >  	mutex_destroy(&dev->dev_lock);
> > >  	mutex_destroy(&dev->hw_lock);
> > > +	mutex_destroy(&dev->irq_lock);
> > >  	reset_control_assert(dev->resets);
> > >  	clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
> > >  	wave5_vpu_enc_unregister_device(dev);
> > > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
> > > b/drivers/media/platform/chips-media/wave5/wave5-
> > > vpuapi.h
> > > index 45615c15beca..f3c1ad6fb3be 100644
> > > --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
> > > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
> > > @@ -8,6 +8,7 @@
> > >  #ifndef VPUAPI_H_INCLUDED
> > >  #define VPUAPI_H_INCLUDED
> > > 
> > > +#include <linux/kfifo.h>
> > >  #include <linux/idr.h>
> > >  #include <linux/genalloc.h>
> > >  #include <media/v4l2-device.h>
> > > @@ -747,6 +748,7 @@ struct vpu_device {
> > >  	struct video_device *video_dev_enc;
> > >  	struct mutex dev_lock; /* lock for the src, dst v4l2 queues */
> > >  	struct mutex hw_lock; /* lock hw configurations */
> > > +	struct mutex irq_lock;
> > >  	int irq;
> > >  	enum product_id product;
> > >  	struct vpu_attr attr;
> > > @@ -764,7 +766,10 @@ struct vpu_device {
> > >  	struct kthread_worker *worker;
> > >  	int vpu_poll_interval;
> > >  	int num_clks;
> > > +	struct task_struct *irq_thread;
> > > +	struct semaphore irq_sem;
> > 
> > Can you comment on what they actually protect ?
> 
> Okay
> 
> > 
> > >  	struct reset_control *resets;
> > > +	spinlock_t irq_spinlock; /* protect instances list */
> > >  };
> > > 
> > >  struct vpu_instance;
> > > @@ -788,6 +793,7 @@ struct vpu_instance {
> > >  	enum v4l2_ycbcr_encoding ycbcr_enc;
> > >  	enum v4l2_quantization quantization;
> > > 
> > > +	struct kfifo irq_status;
> > >  	enum vpu_instance_state state;
> > >  	enum vpu_instance_type type;
> > >  	const struct vpu_instance_ops *ops;

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ