Re: [PATCH RFC 0/9] PKS write protected page tables

Kees Cook
2021-05-05T06:25:39Z

On Tue, May 04, 2021 at 05:30:23PM -0700, Rick Edgecombe wrote:
> This is a POC for write protecting page tables with PKS (Protection Keys for
> Supervisor) [1]. The basic idea is to make the page tables read only, except
> temporarily on a per-cpu basis when they need to be modified. I’m looking for
> opinions on whether people like the general direction of this in terms of
> value and implementation.

Yay!

> Why would people want this?
> ===========================
> Page tables are the basis for many types of protections and as such, are a
> juicy target for attackers. Mapping them read-only will make them harder to
> use in attacks.
>
> This protects against an attacker that has acquired the ability to write to
> the page tables. It's not foolproof because an attacker who can execute
> arbitrary code can either disable PKS directly, or simply call the same
> functions that the kernel uses for legitimate page table writes.

I think it absolutely has value. The exploit techniques I'm aware of that
target the page table are usually attempting to upgrade an arbitrary
write into execution (e.g. write to kernel text after setting kernel
text writable in the page table) or similar "data only" attacks (make
sensitive page writable).

It looks like PKS-protected page tables would be much like the
RO-protected text pages in the sense that there is already code in
the kernel to do things to make it writable, change text, and set it
read-only again (alternatives, ftrace, etc). That said, making the PKS
manipulation code be inline to page-writing code would make it less
available for ROP/JOP, if an attack DID want to go that route.

> Why use PKS for this?
> =====================
> PKS is an upcoming CPU feature that allows supervisor virtual memory
> permissions to be changed without flushing the TLB, like PKU does for user
> memory. Protecting page tables would normally be really expensive because you
> would have to do it with paging itself. PKS helps by providing a way to toggle
> the writability of the page tables with just a per-cpu MSR.

The per-cpu-ness is really important for both performance and for avoiding
temporal attacks where an arbitrary write in one CPU is timed against
a page table write in another CPU.

> Performance impacts
> ===================
> Setting direct map permissions on whatever random page gets allocated for a
> page table would result in a lot of kernel range shootdowns and direct map
> large page shattering. So the way the PKS page table memory is created is
> similar to this module page clustering series[2], where a cache of pages is
> replenished from 2MB pages such that the direct map permissions and associated
> breakage is localized on the direct map. In the PKS page tables case, a PKS
> key is pre-applied to the direct map for pages in the cache.
>
> There would be some costs of memory overhead in order to protect the direct
> map page tables. There would also be some extra kernel range shootdowns to
> replenish the cache on occasion, from setting the PKS key on the direct map of
> the new pages. I don’t have any actual performance data yet.

What CPU models are expected to have PKS?

> This is based on V6 [1] of the core PKS infrastructure patches. PKS
> infrastructure follow-on’s are planned to enable keys to be set to the same
> permissions globally. Since this usage needs a key to be set globally
> read-only by default, a small temporary solution is hacked up in patch 8. Long
> term, PKS protected page tables would use a better and more generic solution
> to achieve this.
>
> [1] https://lore.kernel.org/lkml/20210401225833.566238-1-ira.weiny@intel.com/

Ah, neat!

> [2] https://lore.kernel.org/lkml/20210405203711.1095940-1-rick.p.edgecombe@intel.com/

Ooh. What does this do for performance? It sounds like less TLB
pressure, IIUC?

--
Kees Cook