All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
@ 2019-01-18 17:04 Tetsuo Handa
  2019-01-18 19:48 ` Daniel Jordan
  0 siblings, 1 reply; 7+ messages in thread
From: Tetsuo Handa @ 2019-01-18 17:04 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan; +Cc: linux-kernel, Tetsuo Handa

syzbot found a flush_work() caller who forgot to call INIT_WORK()
because that work_struct was allocated by kzalloc(). But the message

  INFO: trying to register non-static key.
  the code is fine but needs lockdep annotation.
  turning off the locking correctness validator.

by lock_map_acquire() is failing to tell that INIT_WORK() is missing.

Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
set ->func field to non-zero, let's warn if ->func field is zero.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 kernel/workqueue.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 392be4b..a503ad9 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -2908,6 +2908,9 @@ static bool __flush_work(struct work_struct *work, bool from_cancel)
 	if (WARN_ON(!wq_online))
 		return false;
 
+	if (WARN_ON(!work->func))
+		return false;
+
 	if (!from_cancel) {
 		lock_map_acquire(&work->lockdep_map);
 		lock_map_release(&work->lockdep_map);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-18 17:04 [PATCH] workqueue: Try to catch flush_work() without INIT_WORK() Tetsuo Handa
@ 2019-01-18 19:48 ` Daniel Jordan
  2019-01-19  2:41   ` Tetsuo Handa
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Jordan @ 2019-01-18 19:48 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: Tejun Heo, Lai Jiangshan, linux-kernel, daniel.m.jordan

On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
> syzbot found a flush_work() caller who forgot to call INIT_WORK()
> because that work_struct was allocated by kzalloc(). But the message
> 
>   INFO: trying to register non-static key.
>   the code is fine but needs lockdep annotation.
>   turning off the locking correctness validator.
> 
> by lock_map_acquire() is failing to tell that INIT_WORK() is missing.
> 
> Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
> set ->func field to non-zero, let's warn if ->func field is zero.

Agree that it's a good idea to catch this.  So the caller did flush_work
without queueing it beforehand?  Out of curiosity, what situation leads to
this?  Link to the report might be helpful.

> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index 392be4b..a503ad9 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -2908,6 +2908,9 @@ static bool __flush_work(struct work_struct *work, bool from_cancel)
>  	if (WARN_ON(!wq_online))
>  		return false;
>  
> +	if (WARN_ON(!work->func))
> +		return false;
> +

__queue_work has a sanity check already for work, but using list_empty.  Seems
slightly better to be consistent?

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-18 19:48 ` Daniel Jordan
@ 2019-01-19  2:41   ` Tetsuo Handa
  2019-01-22 15:18     ` Daniel Jordan
  0 siblings, 1 reply; 7+ messages in thread
From: Tetsuo Handa @ 2019-01-19  2:41 UTC (permalink / raw)
  To: Daniel Jordan; +Cc: Tejun Heo, Lai Jiangshan, linux-kernel

On 2019/01/19 4:48, Daniel Jordan wrote:
> On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
>> syzbot found a flush_work() caller who forgot to call INIT_WORK()
>> because that work_struct was allocated by kzalloc(). But the message
>>
>>   INFO: trying to register non-static key.
>>   the code is fine but needs lockdep annotation.
>>   turning off the locking correctness validator.
>>
>> by lock_map_acquire() is failing to tell that INIT_WORK() is missing.
>>
>> Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
>> set ->func field to non-zero, let's warn if ->func field is zero.
> 
> Agree that it's a good idea to catch this.  So the caller did flush_work
> without queueing it beforehand?  Out of curiosity, what situation leads to
> this?  Link to the report might be helpful.

I quoted the patch below.

> 
>> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
>> index 392be4b..a503ad9 100644
>> --- a/kernel/workqueue.c
>> +++ b/kernel/workqueue.c
>> @@ -2908,6 +2908,9 @@ static bool __flush_work(struct work_struct *work, bool from_cancel)
>>  	if (WARN_ON(!wq_online))
>>  		return false;
>>  
>> +	if (WARN_ON(!work->func))
>> +		return false;
>> +
> 
> __queue_work has a sanity check already for work, but using list_empty.  Seems
> slightly better to be consistent?
> 

list_empty() won't work, for "struct work_struct" is embedded into a struct
which is allocated by kzalloc().



From 1bbf8d9c7aaef78d7f483d2131261162485c6f72 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sat, 19 Jan 2019 01:38:53 +0900
Subject: [PATCH] drm/vkms: Fix flush_work() without INIT_WORK().

syzbot is hitting a lockdep warning [1] because flush_work() is called
without INIT_WORK() after kzalloc() at vkms_atomic_crtc_reset().

Commit 6c234fe37c57627a ("drm/vkms: Implement CRC debugfs API") added
INIT_WORK() to only vkms_atomic_crtc_duplicate_state() side. Assuming
that lifecycle of crc_work is appropriately managed, fix this problem
by adding INIT_WORK() to vkms_atomic_crtc_reset() side.

[1] https://syzkaller.appspot.com/bug?id=a5954455fcfa51c29ca2ab55b203076337e1c770

Reported-and-tested-by: syzbot <syzbot+12f1b031b6da017e34f8@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 drivers/gpu/drm/vkms/vkms_crtc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index 177bbcb..3c37d8c 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -104,6 +104,7 @@ static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
 	vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
 	if (!vkms_state)
 		return;
+	INIT_WORK(&vkms_state->crc_work, vkms_crc_work_handle);
 
 	crtc->state = &vkms_state->base;
 	crtc->state->crtc = crtc;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-19  2:41   ` Tetsuo Handa
@ 2019-01-22 15:18     ` Daniel Jordan
  2019-01-23  0:44       ` Tetsuo Handa
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Jordan @ 2019-01-22 15:18 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: Daniel Jordan, Tejun Heo, Lai Jiangshan, linux-kernel

On Sat, Jan 19, 2019 at 11:41:22AM +0900, Tetsuo Handa wrote:
> On 2019/01/19 4:48, Daniel Jordan wrote:
> > On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
> > __queue_work has a sanity check already for work, but using list_empty.  Seems
> > slightly better to be consistent?
> > 
> 
> list_empty() won't work, for "struct work_struct" is embedded into a struct
> which is allocated by kzalloc().

Please check list_empty's definition again, it compares the address of the node
to its next pointer, so it should work for a zeroed node.  I'll reiterate that
it seems slightly better to be consistent in "is work_struct initialized?"
checks, but it's not a big deal and I'm fine either way.

> From 1bbf8d9c7aaef78d7f483d2131261162485c6f72 Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Sat, 19 Jan 2019 01:38:53 +0900
> Subject: [PATCH] drm/vkms: Fix flush_work() without INIT_WORK().
> 
> syzbot is hitting a lockdep warning [1] because flush_work() is called
> without INIT_WORK() after kzalloc() at vkms_atomic_crtc_reset().
> 
> Commit 6c234fe37c57627a ("drm/vkms: Implement CRC debugfs API") added
> INIT_WORK() to only vkms_atomic_crtc_duplicate_state() side. Assuming
> that lifecycle of crc_work is appropriately managed, fix this problem
> by adding INIT_WORK() to vkms_atomic_crtc_reset() side.
> 
> [1] https://syzkaller.appspot.com/bug?id=a5954455fcfa51c29ca2ab55b203076337e1c770
> 
> Reported-and-tested-by: syzbot <syzbot+12f1b031b6da017e34f8@syzkaller.appspotmail.com>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  drivers/gpu/drm/vkms/vkms_crtc.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
> index 177bbcb..3c37d8c 100644
> --- a/drivers/gpu/drm/vkms/vkms_crtc.c
> +++ b/drivers/gpu/drm/vkms/vkms_crtc.c
> @@ -104,6 +104,7 @@ static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
>  	vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
>  	if (!vkms_state)
>  		return;
> +	INIT_WORK(&vkms_state->crc_work, vkms_crc_work_handle);
>  
>  	crtc->state = &vkms_state->base;
>  	crtc->state->crtc = crtc;

Great, thanks, I think mentioning this path would improve the changelog.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-22 15:18     ` Daniel Jordan
@ 2019-01-23  0:44       ` Tetsuo Handa
  2019-01-23  2:05         ` Daniel Jordan
  2019-01-25 15:31         ` Tejun Heo
  0 siblings, 2 replies; 7+ messages in thread
From: Tetsuo Handa @ 2019-01-23  0:44 UTC (permalink / raw)
  To: Daniel Jordan; +Cc: Daniel Jordan, Tejun Heo, Lai Jiangshan, linux-kernel

Daniel Jordan wrote:
> On Sat, Jan 19, 2019 at 11:41:22AM +0900, Tetsuo Handa wrote:
> > On 2019/01/19 4:48, Daniel Jordan wrote:
> > > On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
> > > __queue_work has a sanity check already for work, but using list_empty.  Seems
> > > slightly better to be consistent?
> > > 
> > 
> > list_empty() won't work, for "struct work_struct" is embedded into a struct
> > which is allocated by kzalloc().
> 
> Please check list_empty's definition again, it compares the address of the node
> to its next pointer, so it should work for a zeroed node.  I'll reiterate that
> it seems slightly better to be consistent in "is work_struct initialized?"
> checks, but it's not a big deal and I'm fine either way.

You are talking about

	if (WARN_ON(!list_empty(&work->entry))) {
		spin_unlock(&pwq->pool->lock);
		return;
	}

part in __queue_work(), aren't you? But since flush_work() is used for waiting for
a work to complete, that work can be either queued state (list_empty() == false) or
not queued state (list_empty() == true). Thus, I don't think that flush_work() can
use list_empty() for checking whether that work was initialized.



[PATCH v2] workqueue: Try to catch flush_work() without INIT_WORK().

syzbot found a flush_work() caller who forgot to call INIT_WORK()
because that work_struct was allocated by kzalloc() [1]. But the message

  INFO: trying to register non-static key.
  the code is fine but needs lockdep annotation.
  turning off the locking correctness validator.

by lock_map_acquire() is failing to tell that INIT_WORK() is missing.

Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
set ->func field to non-zero, let's warn if ->func field is zero.

[1] https://syzkaller.appspot.com/bug?id=a5954455fcfa51c29ca2ab55b203076337e1c770

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 kernel/workqueue.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 392be4b..a503ad9 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -2908,6 +2908,9 @@ static bool __flush_work(struct work_struct *work, bool from_cancel)
 	if (WARN_ON(!wq_online))
 		return false;
 
+	if (WARN_ON(!work->func))
+		return false;
+
 	if (!from_cancel) {
 		lock_map_acquire(&work->lockdep_map);
 		lock_map_release(&work->lockdep_map);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-23  0:44       ` Tetsuo Handa
@ 2019-01-23  2:05         ` Daniel Jordan
  2019-01-25 15:31         ` Tejun Heo
  1 sibling, 0 replies; 7+ messages in thread
From: Daniel Jordan @ 2019-01-23  2:05 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: Daniel Jordan, Tejun Heo, Lai Jiangshan, linux-kernel

On Wed, Jan 23, 2019 at 09:44:12AM +0900, Tetsuo Handa wrote:
> Daniel Jordan wrote:
> > On Sat, Jan 19, 2019 at 11:41:22AM +0900, Tetsuo Handa wrote:
> > > On 2019/01/19 4:48, Daniel Jordan wrote:
> > > > On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
> > > > __queue_work has a sanity check already for work, but using list_empty.  Seems
> > > > slightly better to be consistent?
> > > > 
> > > 
> > > list_empty() won't work, for "struct work_struct" is embedded into a struct
> > > which is allocated by kzalloc().
> > 
> > Please check list_empty's definition again, it compares the address of the node
> > to its next pointer, so it should work for a zeroed node.  I'll reiterate that
> > it seems slightly better to be consistent in "is work_struct initialized?"
> > checks, but it's not a big deal and I'm fine either way.
> 
> You are talking about
> 
> 	if (WARN_ON(!list_empty(&work->entry))) {
> 		spin_unlock(&pwq->pool->lock);
> 		return;
> 	}
> 
> part in __queue_work(), aren't you? But since flush_work() is used for waiting for
> a work to complete, that work can be either queued state (list_empty() == false) or
> not queued state (list_empty() == true). Thus, I don't think that flush_work() can
> use list_empty() for checking whether that work was initialized.

Oh, you're right, sorry for the noise!

> [PATCH v2] workqueue: Try to catch flush_work() without INIT_WORK().
> 
> syzbot found a flush_work() caller who forgot to call INIT_WORK()
> because that work_struct was allocated by kzalloc() [1]. But the message
> 
>   INFO: trying to register non-static key.
>   the code is fine but needs lockdep annotation.
>   turning off the locking correctness validator.
> 
> by lock_map_acquire() is failing to tell that INIT_WORK() is missing.
> 
> Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
> set ->func field to non-zero, let's warn if ->func field is zero.
> 
> [1] https://syzkaller.appspot.com/bug?id=a5954455fcfa51c29ca2ab55b203076337e1c770
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  kernel/workqueue.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index 392be4b..a503ad9 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -2908,6 +2908,9 @@ static bool __flush_work(struct work_struct *work, bool from_cancel)
>  	if (WARN_ON(!wq_online))
>  		return false;
>  
> +	if (WARN_ON(!work->func))
> +		return false;
> +
>  	if (!from_cancel) {
>  		lock_map_acquire(&work->lockdep_map);
>  		lock_map_release(&work->lockdep_map);

Thanks for updating the changelog.  FWIW, you can add

Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] workqueue: Try to catch flush_work() without INIT_WORK().
  2019-01-23  0:44       ` Tetsuo Handa
  2019-01-23  2:05         ` Daniel Jordan
@ 2019-01-25 15:31         ` Tejun Heo
  1 sibling, 0 replies; 7+ messages in thread
From: Tejun Heo @ 2019-01-25 15:31 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: Daniel Jordan, Lai Jiangshan, linux-kernel

On Wed, Jan 23, 2019 at 09:44:12AM +0900, Tetsuo Handa wrote:
> Daniel Jordan wrote:
> > On Sat, Jan 19, 2019 at 11:41:22AM +0900, Tetsuo Handa wrote:
> > > On 2019/01/19 4:48, Daniel Jordan wrote:
> > > > On Sat, Jan 19, 2019 at 02:04:58AM +0900, Tetsuo Handa wrote:
> > > > __queue_work has a sanity check already for work, but using list_empty.  Seems
> > > > slightly better to be consistent?
> > > > 
> > > 
> > > list_empty() won't work, for "struct work_struct" is embedded into a struct
> > > which is allocated by kzalloc().
> > 
> > Please check list_empty's definition again, it compares the address of the node
> > to its next pointer, so it should work for a zeroed node.  I'll reiterate that
> > it seems slightly better to be consistent in "is work_struct initialized?"
> > checks, but it's not a big deal and I'm fine either way.
> 
> You are talking about
> 
> 	if (WARN_ON(!list_empty(&work->entry))) {
> 		spin_unlock(&pwq->pool->lock);
> 		return;
> 	}
> 
> part in __queue_work(), aren't you? But since flush_work() is used for waiting for
> a work to complete, that work can be either queued state (list_empty() == false) or
> not queued state (list_empty() == true). Thus, I don't think that flush_work() can
> use list_empty() for checking whether that work was initialized.
> 
> 
> 
> [PATCH v2] workqueue: Try to catch flush_work() without INIT_WORK().
> 
> syzbot found a flush_work() caller who forgot to call INIT_WORK()
> because that work_struct was allocated by kzalloc() [1]. But the message
> 
>   INFO: trying to register non-static key.
>   the code is fine but needs lockdep annotation.
>   turning off the locking correctness validator.
> 
> by lock_map_acquire() is failing to tell that INIT_WORK() is missing.
> 
> Since flush_work() without INIT_WORK() is a bug, and INIT_WORK() should
> set ->func field to non-zero, let's warn if ->func field is zero.
> 
> [1] https://syzkaller.appspot.com/bug?id=a5954455fcfa51c29ca2ab55b203076337e1c770
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>

Applied to wq/for-5.0.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-01-25 15:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-18 17:04 [PATCH] workqueue: Try to catch flush_work() without INIT_WORK() Tetsuo Handa
2019-01-18 19:48 ` Daniel Jordan
2019-01-19  2:41   ` Tetsuo Handa
2019-01-22 15:18     ` Daniel Jordan
2019-01-23  0:44       ` Tetsuo Handa
2019-01-23  2:05         ` Daniel Jordan
2019-01-25 15:31         ` Tejun Heo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.