Adds a test to verify workqueue stack recording and print it in
KASAN report.
The KASAN report was as follows(cleaned up slightly):
BUG: KASAN: use-after-free in kasan_workqueue_uaf
Freed by task 54:
kasan_save_stack+0x24/0x50
kasan_set_track+0x24/0x38
kasan_set_free_info+0x20/0x40
__kasan_slab_free+0x10c/0x170
kasan_slab_free+0x10/0x18
kfree+0x98/0x270
kasan_workqueue_work+0xc/0x18
Last potentially related work creation:
kasan_save_stack+0x24/0x50
kasan_record_wq_stack+0xa8/0xb8
insert_work+0x48/0x288
__queue_work+0x3e8/0xc40
queue_work_on+0xf4/0x118
kasan_workqueue_uaf+0xfc/0x190
Signed-off-by: Walter Wu <[email protected]>
Acked-by: Marco Elver <[email protected]>
Reviewed-by: Dmitry Vyukov <[email protected]>
Reviewed-by: Andrey Konovalov <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Alexander Potapenko <[email protected]>
Cc: Matthias Brugger <[email protected]>
---
v4:
- testcase has merge conflict, so that rebase onto the KASAN-KUNIT
---
lib/test_kasan_module.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/lib/test_kasan_module.c b/lib/test_kasan_module.c
index 2d68db6ae67b..62a87854b120 100644
--- a/lib/test_kasan_module.c
+++ b/lib/test_kasan_module.c
@@ -91,6 +91,34 @@ static noinline void __init kasan_rcu_uaf(void)
call_rcu(&global_rcu_ptr->rcu, kasan_rcu_reclaim);
}
+static noinline void __init kasan_workqueue_work(struct work_struct *work)
+{
+ kfree(work);
+}
+
+static noinline void __init kasan_workqueue_uaf(void)
+{
+ struct workqueue_struct *workqueue;
+ struct work_struct *work;
+
+ workqueue = create_workqueue("kasan_wq_test");
+ if (!workqueue) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+ work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
+ if (!work) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ INIT_WORK(work, kasan_workqueue_work);
+ queue_work(workqueue, work);
+ destroy_workqueue(workqueue);
+
+ pr_info("use-after-free on workqueue\n");
+ ((volatile struct work_struct *)work)->data;
+}
static int __init test_kasan_module_init(void)
{
@@ -102,6 +130,7 @@ static int __init test_kasan_module_init(void)
copy_user_test();
kasan_rcu_uaf();
+ kasan_workqueue_uaf();
kasan_restore_multi_shot(multishot);
return -EAGAIN;
--
2.18.0
On Thu, 3 Dec 2020 at 03:27, Walter Wu <[email protected]> wrote:
>
> Adds a test to verify workqueue stack recording and print it in
> KASAN report.
>
> The KASAN report was as follows(cleaned up slightly):
>
> BUG: KASAN: use-after-free in kasan_workqueue_uaf
>
> Freed by task 54:
> kasan_save_stack+0x24/0x50
> kasan_set_track+0x24/0x38
> kasan_set_free_info+0x20/0x40
> __kasan_slab_free+0x10c/0x170
> kasan_slab_free+0x10/0x18
> kfree+0x98/0x270
> kasan_workqueue_work+0xc/0x18
>
> Last potentially related work creation:
> kasan_save_stack+0x24/0x50
> kasan_record_wq_stack+0xa8/0xb8
> insert_work+0x48/0x288
> __queue_work+0x3e8/0xc40
> queue_work_on+0xf4/0x118
> kasan_workqueue_uaf+0xfc/0x190
>
> Signed-off-by: Walter Wu <[email protected]>
> Acked-by: Marco Elver <[email protected]>
> Reviewed-by: Dmitry Vyukov <[email protected]>
> Reviewed-by: Andrey Konovalov <[email protected]>
> Cc: Andrey Ryabinin <[email protected]>
> Cc: Alexander Potapenko <[email protected]>
> Cc: Matthias Brugger <[email protected]>
> ---
>
> v4:
> - testcase has merge conflict, so that rebase onto the KASAN-KUNIT
>
> ---
> lib/test_kasan_module.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/lib/test_kasan_module.c b/lib/test_kasan_module.c
> index 2d68db6ae67b..62a87854b120 100644
> --- a/lib/test_kasan_module.c
> +++ b/lib/test_kasan_module.c
> @@ -91,6 +91,34 @@ static noinline void __init kasan_rcu_uaf(void)
> call_rcu(&global_rcu_ptr->rcu, kasan_rcu_reclaim);
> }
>
> +static noinline void __init kasan_workqueue_work(struct work_struct *work)
> +{
> + kfree(work);
> +}
> +
> +static noinline void __init kasan_workqueue_uaf(void)
> +{
> + struct workqueue_struct *workqueue;
> + struct work_struct *work;
> +
> + workqueue = create_workqueue("kasan_wq_test");
> + if (!workqueue) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> + work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
> + if (!work) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + INIT_WORK(work, kasan_workqueue_work);
> + queue_work(workqueue, work);
> + destroy_workqueue(workqueue);
> +
> + pr_info("use-after-free on workqueue\n");
> + ((volatile struct work_struct *)work)->data;
> +}
>
> static int __init test_kasan_module_init(void)
> {
> @@ -102,6 +130,7 @@ static int __init test_kasan_module_init(void)
>
> copy_user_test();
> kasan_rcu_uaf();
> + kasan_workqueue_uaf();
Why can't this go into the KUnit based KASAN test?
On Thu, 2020-12-03 at 11:29 +0100, Marco Elver wrote:
> On Thu, 3 Dec 2020 at 03:27, Walter Wu <[email protected]> wrote:
> >
> > Adds a test to verify workqueue stack recording and print it in
> > KASAN report.
> >
> > The KASAN report was as follows(cleaned up slightly):
> >
> > BUG: KASAN: use-after-free in kasan_workqueue_uaf
> >
> > Freed by task 54:
> > kasan_save_stack+0x24/0x50
> > kasan_set_track+0x24/0x38
> > kasan_set_free_info+0x20/0x40
> > __kasan_slab_free+0x10c/0x170
> > kasan_slab_free+0x10/0x18
> > kfree+0x98/0x270
> > kasan_workqueue_work+0xc/0x18
> >
> > Last potentially related work creation:
> > kasan_save_stack+0x24/0x50
> > kasan_record_wq_stack+0xa8/0xb8
> > insert_work+0x48/0x288
> > __queue_work+0x3e8/0xc40
> > queue_work_on+0xf4/0x118
> > kasan_workqueue_uaf+0xfc/0x190
> >
> > Signed-off-by: Walter Wu <[email protected]>
> > Acked-by: Marco Elver <[email protected]>
> > Reviewed-by: Dmitry Vyukov <[email protected]>
> > Reviewed-by: Andrey Konovalov <[email protected]>
> > Cc: Andrey Ryabinin <[email protected]>
> > Cc: Alexander Potapenko <[email protected]>
> > Cc: Matthias Brugger <[email protected]>
> > ---
> >
> > v4:
> > - testcase has merge conflict, so that rebase onto the KASAN-KUNIT
> >
> > ---
> > lib/test_kasan_module.c | 29 +++++++++++++++++++++++++++++
> > 1 file changed, 29 insertions(+)
> >
> > diff --git a/lib/test_kasan_module.c b/lib/test_kasan_module.c
> > index 2d68db6ae67b..62a87854b120 100644
> > --- a/lib/test_kasan_module.c
> > +++ b/lib/test_kasan_module.c
> > @@ -91,6 +91,34 @@ static noinline void __init kasan_rcu_uaf(void)
> > call_rcu(&global_rcu_ptr->rcu, kasan_rcu_reclaim);
> > }
> >
> > +static noinline void __init kasan_workqueue_work(struct work_struct *work)
> > +{
> > + kfree(work);
> > +}
> > +
> > +static noinline void __init kasan_workqueue_uaf(void)
> > +{
> > + struct workqueue_struct *workqueue;
> > + struct work_struct *work;
> > +
> > + workqueue = create_workqueue("kasan_wq_test");
> > + if (!workqueue) {
> > + pr_err("Allocation failed\n");
> > + return;
> > + }
> > + work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
> > + if (!work) {
> > + pr_err("Allocation failed\n");
> > + return;
> > + }
> > +
> > + INIT_WORK(work, kasan_workqueue_work);
> > + queue_work(workqueue, work);
> > + destroy_workqueue(workqueue);
> > +
> > + pr_info("use-after-free on workqueue\n");
> > + ((volatile struct work_struct *)work)->data;
> > +}
> >
> > static int __init test_kasan_module_init(void)
> > {
> > @@ -102,6 +130,7 @@ static int __init test_kasan_module_init(void)
> >
> > copy_user_test();
> > kasan_rcu_uaf();
> > + kasan_workqueue_uaf();
>
>
> Why can't this go into the KUnit based KASAN test?
This test case has not been ported to KUnit, because KUnit's expect
failure will not check whether the work stack is exist. So it remains in
test_kasan_module, it is the same with kasan_rcu_uaf()[1].
[1]https://lkml.org/lkml/2020/8/1/45
Thanks.
Walter