2017-08-16 23:21:01

by Kees Cook

[permalink] [raw]
Subject: refactoring timers to avoid init_timer*()

Hi,

So, my earlier patch[1] to add canaries to the timer struct did not
handle many many cases, as I've uncovered. To have a sensible canary,
it needs to be written at function-assignment time, but right now
things are extremely ad-hoc in the timer API, making it non-trivial to
figure out if the code changed the function or if some attacker is
exploiting a flaw to overwrite the timer struct function pointer.

What I'd like to do is to eliminate all the uses of init_timer*() in
favor of setup_timer*() which already includes the function as part of
the initialization (which is where a canary could be generated). There
are a couple cases where a timer user switches the function out
intentionally after setup, but that is the very rare case. Those could
use a new helper that would set the function (and canary) after an
earlier setup_timer() call (or maybe just re-call setup_timer()?).

In the process I noticed that we already have
scripts/coccinelle/api/setup_timer.cocci to detect existing cases of:

init_timer(t);
t->function = func;
t->data = data;

And replace it with: setup_timer(t, func, data);

Another pattern was:

t->expires = when;
add_timer(t);

Which can be replaced with mod_timer(t, when);

So, I've created scripts/coccinelle/api/mod_timer.cocci for the
latter, and done a few passes with manual review. The current result
doesn't fully eliminate init_timer() yet, but it gets much closer. I
just wanted to be sure that this whole clean-up would actually be
welcome before I try to nail down the last many cases.

You can see it here:
https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=kspp/timer/refactor-exploded

135 commits (split by maintainer):
243 files changed, 583 insertions(+), 1055 deletions(-)

-Kees

[1] http://www.openwall.com/lists/kernel-hardening/2017/08/08/2

--
Kees Cook
Pixel Security


2017-08-17 14:31:00

by Thomas Gleixner

[permalink] [raw]
Subject: Re: refactoring timers to avoid init_timer*()

On Wed, 16 Aug 2017, Kees Cook wrote:
> In the process I noticed that we already have
> scripts/coccinelle/api/setup_timer.cocci to detect existing cases of:
>
> init_timer(t);
> t->function = func;
> t->data = data;
>
> And replace it with: setup_timer(t, func, data);
>
> Another pattern was:
>
> t->expires = when;
> add_timer(t);
>
> Which can be replaced with mod_timer(t, when);
>
> So, I've created scripts/coccinelle/api/mod_timer.cocci for the
> latter, and done a few passes with manual review. The current result
> doesn't fully eliminate init_timer() yet, but it gets much closer. I
> just wanted to be sure that this whole clean-up would actually be
> welcome before I try to nail down the last many cases.

I think it's worth the trouble, but rather than having a gazillion of
commits with the same changelog, we should do that based on a cocci script
right before the next rc1 in one go and be done with it.

That will cover most of the init_timer() cases and we can fixup the
remaining few oddballs manually after that.

I just noticed that we have the same pattern with hrtimer_init(). I had a
stab on adding hrtimer_setup() and friends which takes a function argument
and converted the bulk with coccinelle.

82 files changed, 186 insertions(+), 210 deletions(-)

We can do that in the same sweep as the init_timer() one and then you can
do the canary magic on hrtimers as well.

Thanks,

tglx


2017-08-17 14:39:28

by Kees Cook

[permalink] [raw]
Subject: Re: refactoring timers to avoid init_timer*()

On Thu, Aug 17, 2017 at 7:30 AM, Thomas Gleixner <[email protected]> wrote:
> On Wed, 16 Aug 2017, Kees Cook wrote:
>> In the process I noticed that we already have
>> scripts/coccinelle/api/setup_timer.cocci to detect existing cases of:
>>
>> init_timer(t);
>> t->function = func;
>> t->data = data;
>>
>> And replace it with: setup_timer(t, func, data);
>>
>> Another pattern was:
>>
>> t->expires = when;
>> add_timer(t);
>>
>> Which can be replaced with mod_timer(t, when);
>>
>> So, I've created scripts/coccinelle/api/mod_timer.cocci for the
>> latter, and done a few passes with manual review. The current result
>> doesn't fully eliminate init_timer() yet, but it gets much closer. I
>> just wanted to be sure that this whole clean-up would actually be
>> welcome before I try to nail down the last many cases.
>
> I think it's worth the trouble, but rather than having a gazillion of
> commits with the same changelog, we should do that based on a cocci script
> right before the next rc1 in one go and be done with it.
>
> That will cover most of the init_timer() cases and we can fixup the
> remaining few oddballs manually after that.

Okay, that sounds good. I'll work on improving the cocci scripts. For
example, I noticed that it doesn't notice having assignments _before_
the init_timer() call:

timer->function = func;
timer->data = data;
init_timer(timer);

Whee. :)

BTW, I may back off on doing ->expires = when; add_timer(timer) -->
mod_timer(timer, when) changes since that actually removes a sanity
check in add_timer().

My plans currently are collapsing all the open-coded init_timer*()s
into setup_*timer() and then adding something like aim_timer(timer,
func, data) for post-setup function/data changes.

-Kees

>
> I just noticed that we have the same pattern with hrtimer_init(). I had a
> stab on adding hrtimer_setup() and friends which takes a function argument
> and converted the bulk with coccinelle.
>
> 82 files changed, 186 insertions(+), 210 deletions(-)
>
> We can do that in the same sweep as the init_timer() one and then you can
> do the canary magic on hrtimers as well.
>
> Thanks,
>
> tglx
>
>



--
Kees Cook
Pixel Security

2017-08-17 14:40:46

by Christoph Hellwig

[permalink] [raw]
Subject: Re: refactoring timers to avoid init_timer*()

Instea of bloating the timer even more we should kill off
the data field eventually, which should give you the same
protection.

See my proposal and the related discussion here:

http://www.mail-archive.com/[email protected]/msg1397209.html

2017-08-17 15:08:41

by Kees Cook

[permalink] [raw]
Subject: Re: refactoring timers to avoid init_timer*()

On Thu, Aug 17, 2017 at 7:40 AM, Christoph Hellwig <[email protected]> wrote:
> Instea of bloating the timer even more we should kill off
> the data field eventually, which should give you the same
> protection.
>
> See my proposal and the related discussion here:
>
> http://www.mail-archive.com/[email protected]/msg1397209.html

Ah! Yes, very cool. Most callbacks could be converted to the new timer
callback pretty easily. Some, though, pass non-pointer data in the
"data" argument. Those can likely be moved to somewhere else, though.
I think it should be possible to just perform all the conversions.

Regardless, getting rid of init_timer*() and eliminating the
open-coded callback assignments should help. I'll keep grinding on
that.

-Kees

--
Kees Cook
Pixel Security