Re: [PATCH 10/17] prmem: documentation

From: Andy Lutomirski
Date: Tue Nov 13 2018 - 13:49:14 EST


On Tue, Nov 13, 2018 at 10:31 AM Igor Stoppa <igor.stoppa@xxxxxxxxxx> wrote:
>
> On 13/11/2018 19:47, Andy Lutomirski wrote:
>
> > For general rare-writish stuff, I don't think we want IRQs running
> > with them mapped anywhere for write. For AVC and IMA, I'm less sure.
>
> Why would these be less sensitive?

I'm not really saying they're less sensitive so much as that the
considerations are different. I think the original rare-write code is
based on ideas from grsecurity, and it was intended to protect static
data like structs full of function pointers. Those targets have some
different properties:

- Static targets are at addresses that are much more guessable, so
they're easier targets for most attacks. (Not spraying attacks like
the ones you're interested in, though.)

- Static targets are higher value. No offense to IMA or AVC, but
outright execution of shellcode, hijacking of control flow, or compete
disablement of core security features is higher impact than bypassing
SELinux or IMA. Why would you bother corrupting the AVC if you could
instead just set enforcing=0? (I suppose that corrupting the AVC is
less likely to be noticed by monitoring tools.)

- Static targets are small. This means that the interrupt latency
would be negligible, especially in comparison to the latency of
replacing the entire SELinux policy object.

Anyway, I'm not all that familiar with SELinux under the hood, but I'm
wondering if a different approach to thinks like the policy database
might be appropriate. When the policy is changed, rather than
allocating rare-write memory and writing to it, what if we instead
allocated normal memory, wrote to it, write-protected it, and then
used the rare-write infrastructure to do a much smaller write to
replace the pointer?

Admittedly, this creates a window where another core could corrupt the
data as it's being written. That may not matter so much if an
attacker can't force a policy update. Alternatively, the update code
could re-verify the policy after write-protecting it, or there could
be a fancy API to allocate some temporarily-writable memory (by
creating a whole new mm_struct, mapping the memory writable just in
that mm_struct, and activating it) so that only the actual policy
loader could touch the memory. But I'm mostly speculating here, since
I'm not familiar with the code in question.

Anyway, I tend to think that the right way to approach mainlining all
this is to first get the basic rare write support for static data into
place and then to build on that. I think it's great that you're
pushing this effort, but doing this for SELinux and IMA is a bigger
project than doing it for static data, and it might make sense to do
it in bite-sized pieces.

Does any of this make sense?