[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAGS_qxrf6zYkBaY29Z3QGw13vf7QK8SDgLpLC71a6FvwV5Sa6A@mail.gmail.com>
Date: Thu, 1 Dec 2022 22:52:48 -0800
From: Daniel Latypov <dlatypov@...gle.com>
To: Kees Cook <keescook@...omium.org>
Cc: David Gow <davidgow@...gle.com>,
Brendan Higgins <brendanhiggins@...gle.com>,
kunit-dev@...glegroups.com, Petr Skocik <pskocik@...il.com>,
linux-hardening@...r.kernel.org
Subject: Re: mocking init_task ?
On Thu, Dec 1, 2022 at 8:28 PM Kees Cook <keescook@...omium.org> wrote:
>
> Hi,
>
> I want to make a unit test for kill_something_info(), as there is a patch
> to fix a bug with it not working as expected under a specific process
> tree arrangement[1]. This seems like a great candidate for a unit test:
> given a specific state, return a specific result. Emboldened, I applied
> the "kunit: Support redirecting function calls" series[2], preparing to
> mock group_send_sig_info(), and ran head-long into for_each_process()
> ... which uses the address of the global init_task:
>
> #define for_each_process(p) \
> for (p = &init_task ; (p = next_task(p)) != &init_task ; )
>
> :(
>
> I'm curious what you think might be the right approach to mock
> init_task, or for_each_process(), so I can apply unit tests to some of
> the "simple" process tree walkers...
>
> One idea I had was using the "kunit: Provide a static key to check if
> KUnit is actively running tests" series[3], and do something like this:
>
> #ifndef CONFIG_KUNIT
> #define init_task_ptr &init_task
> #else
> #define init_task_ptr ({ \
> struct task_struct *task = &init_task; \
> if (static_branch_unlikely(&kunit_running)) { \
nit: if(kunit_get_current_test()) is probably a bit safer.
For UML, it will never make a difference (IIUC), but we probably want
to ensure that current == the task_struct executing kunit tests.
> struct kunit *test; \
> test = current->kunit_test; \
> if (test->mock_init_task) \
> task = test->mock_init_task; \
> } \
> task; \
> })
> #endif
>
> #define for_each_process(p) \
> for (p = init_task_ptr ; (p = next_task(p)) != init_task_ptr ; )
I think this is pretty close to being the best you can do.
I have a terrible idea that afaict should work.
If it does work, I like it a lot better since it's less intrusive.
== my_test.c ==
#include <linux/sched/signal.h>
#undef for_each_process
#define for_each_process(p) \
for (p = test_init_task ; (p = next_task(p)) != test_init_task ; )
struct task_struct _test_init_task = {
.tasks = LIST_HEAD_INIT(_test_init_task.tasks),
};
#define test_init_task ({ \
struct task_struct *task = &init_task;\
if (kunit_get_current_test()) {\
task = &_test_init_task;\
}\
task;\
})
#include <kunit/test.h>
static void my_test(struct kunit *test)
{
struct task_struct *p;
for_each_process(p)
KUNIT_FAIL(test, "should not be called");
/* can modify test_init_task as you see fit here */
}
And then you explicitly keep nothing else in this translation unit so
we limit the damage we've done.
Daniel
>
> And then tests can hang a mock init_task off the test? It seems really
> horrible, but there is a LOT of global state in the kernel, so I figured
> I had to start somewhere? :P
>
> Thoughts?
>
> -Kees
>
> [1] https://lore.kernel.org/lkml/20221122161240.137570-1-pskocik@gmail.com/
> [2] https://lore.kernel.org/lkml/20220910212804.670622-1-davidgow@google.com/
> [3] https://lore.kernel.org/lkml/20221125084306.1063074-1-davidgow@google.com/
>
> --
> Kees Cook
Powered by blists - more mailing lists