* [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
@ 2026-08-05 10:40 Ard Biesheuvel
2026-08-21 13:20 ` Kevin Brodsky
0 siblings, 1 reply; 8+ messages in thread
From: Ard Biesheuvel @ 2026-08-05 10:40 UTC (permalink / raw)
To: linux-arm-kernel
Cc: will, catalin.marinas, mark.rutland, Ard Biesheuvel, Ryan Roberts,
Anshuman Khandual, Kevin Brodsky, Liz Prucka, Seth Jenkins,
Kees Cook, Jann Horn, linux-hardening
From: Ard Biesheuvel <ardb@kernel.org>
Without physical KASLR, the fixmap page tables will appear at an a
priori known offset in the physical address space, and due to the lack
of randomization, the linear map carries a writeable alias of the fixmap
PTE pages, which appears at an offset in the kernel VA space that is
also predictable.
Given that the placement of the fixmap area is never randomized either,
a single store to this linear alias region is sufficient to map any
physical page with any permissions at a known offset in the kernel VA
space, including on top of the PTI trampoline.
Avoid this, by remapping the fixmap PTE pages read-only in the linear
map. This is possible because all updates to bm_pte[] occur via the
mapping of the kernel image in the vmap area. A read-only mapping is
still needed for things like ptdump that walk the page tables.
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Kevin Brodsky <kevin.brodsky@arm.com>
Cc: Liz Prucka <lizprucka@google.com>
Cc: Seth Jenkins <sethjenkins@google.com>
Cc: Kees Cook <kees@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: linux-hardening@vger.kernel.org
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/include/asm/set_memory.h | 2 ++
arch/arm64/mm/fixmap.c | 7 +++++++
arch/arm64/mm/pageattr.c | 10 ++++++++++
3 files changed, 19 insertions(+)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..a685fb534c3e 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -11,6 +11,8 @@ bool can_set_direct_map(void);
int set_memory_valid(unsigned long addr, int numpages, int enable);
+int set_direct_map_ro(unsigned long addr, int numpages);
+
int set_direct_map_invalid_noflush(struct page *page);
int set_direct_map_default_noflush(struct page *page);
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
index f66a0016dd02..fcb571dffe82 100644
--- a/arch/arm64/mm/fixmap.c
+++ b/arch/arm64/mm/fixmap.c
@@ -14,6 +14,7 @@
#include <asm/fixmap.h>
#include <asm/kernel-pgtable.h>
#include <asm/pgalloc.h>
+#include <asm/set_memory.h>
#include <asm/tlbflush.h>
/* ensure that the fixmap region does not grow down into the PCI I/O region */
@@ -173,3 +174,9 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
return dt_virt;
}
+
+static int __init fixmap_remap_ro(void)
+{
+ return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
+}
+late_initcall(fixmap_remap_ro);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..5072b14d4f9d 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -251,6 +251,16 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
__pgprot(PTE_PRESENT_VALID_KERNEL));
}
+int set_direct_map_ro(unsigned long addr, int numpages)
+{
+ if (!can_set_direct_map())
+ return 0;
+
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(PTE_RDONLY),
+ __pgprot(PTE_WRITE));
+}
+
int set_direct_map_invalid_noflush(struct page *page)
{
pgprot_t clear_mask = __pgprot(PTE_PRESENT_VALID_KERNEL);
--
2.55.0.571.g244d577d93-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-05 10:40 [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map Ard Biesheuvel
@ 2026-08-21 13:20 ` Kevin Brodsky
2026-08-26 9:07 ` Ard Biesheuvel
0 siblings, 1 reply; 8+ messages in thread
From: Kevin Brodsky @ 2026-08-21 13:20 UTC (permalink / raw)
To: Ard Biesheuvel, linux-arm-kernel
Cc: will, catalin.marinas, mark.rutland, Ard Biesheuvel, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On 05/08/2026 12:40, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> Without physical KASLR, the fixmap page tables will appear at an a
> priori known offset in the physical address space, and due to the lack
> of randomization, the linear map carries a writeable alias of the fixmap
> PTE pages, which appears at an offset in the kernel VA space that is
> also predictable.
>
> Given that the placement of the fixmap area is never randomized either,
> a single store to this linear alias region is sufficient to map any
> physical page with any permissions at a known offset in the kernel VA
> space, including on top of the PTI trampoline.
>
> Avoid this, by remapping the fixmap PTE pages read-only in the linear
> map.
Sounds good, logical next step after unmapping the rest of data/BSS from
the linear map :)
> This is possible because all updates to bm_pte[] occur via the
> mapping of the kernel image in the vmap area. A read-only mapping is
> still needed for things like ptdump that walk the page tables.
>
> Cc: Ryan Roberts <ryan.roberts@arm.com>
> Cc: Anshuman Khandual <anshuman.khandual@arm.com>
> Cc: Kevin Brodsky <kevin.brodsky@arm.com>
> Cc: Liz Prucka <lizprucka@google.com>
> Cc: Seth Jenkins <sethjenkins@google.com>
> Cc: Kees Cook <kees@kernel.org>
> Cc: Jann Horn <jannh@google.com>
> Cc: linux-hardening@vger.kernel.org
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/include/asm/set_memory.h | 2 ++
> arch/arm64/mm/fixmap.c | 7 +++++++
> arch/arm64/mm/pageattr.c | 10 ++++++++++
> 3 files changed, 19 insertions(+)
>
> diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
> index 90f61b17275e..a685fb534c3e 100644
> --- a/arch/arm64/include/asm/set_memory.h
> +++ b/arch/arm64/include/asm/set_memory.h
> @@ -11,6 +11,8 @@ bool can_set_direct_map(void);
>
> int set_memory_valid(unsigned long addr, int numpages, int enable);
>
> +int set_direct_map_ro(unsigned long addr, int numpages);
> +
> int set_direct_map_invalid_noflush(struct page *page);
> int set_direct_map_default_noflush(struct page *page);
> int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
> diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
> index f66a0016dd02..fcb571dffe82 100644
> --- a/arch/arm64/mm/fixmap.c
> +++ b/arch/arm64/mm/fixmap.c
> @@ -14,6 +14,7 @@
> #include <asm/fixmap.h>
> #include <asm/kernel-pgtable.h>
> #include <asm/pgalloc.h>
> +#include <asm/set_memory.h>
> #include <asm/tlbflush.h>
>
> /* ensure that the fixmap region does not grow down into the PCI I/O region */
> @@ -173,3 +174,9 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
>
> return dt_virt;
> }
> +
> +static int __init fixmap_remap_ro(void)
> +{
> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
Should we not also remap bm_pmd and bm_pud?
For that matter, do we need RW access via the linear map for any page
annotated with __bss_pgtbl? I suppose that might be the case for
kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
> +}
> +late_initcall(fixmap_remap_ro);
Is mark_rodata_ro() definitely too early to remap these pages RO?
- Kevin
> diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
> index bbe98ac9ad8c..5072b14d4f9d 100644
> --- a/arch/arm64/mm/pageattr.c
> +++ b/arch/arm64/mm/pageattr.c
> @@ -251,6 +251,16 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
> __pgprot(PTE_PRESENT_VALID_KERNEL));
> }
>
> +int set_direct_map_ro(unsigned long addr, int numpages)
> +{
> + if (!can_set_direct_map())
> + return 0;
> +
> + return __change_memory_common(addr, PAGE_SIZE * numpages,
> + __pgprot(PTE_RDONLY),
> + __pgprot(PTE_WRITE));
> +}
> +
> int set_direct_map_invalid_noflush(struct page *page)
> {
> pgprot_t clear_mask = __pgprot(PTE_PRESENT_VALID_KERNEL);
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-21 13:20 ` Kevin Brodsky
@ 2026-08-26 9:07 ` Ard Biesheuvel
2026-08-27 8:55 ` Kevin Brodsky
0 siblings, 1 reply; 8+ messages in thread
From: Ard Biesheuvel @ 2026-08-26 9:07 UTC (permalink / raw)
To: Kevin Brodsky, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On Fri, 21 Aug 2026, at 15:20, Kevin Brodsky wrote:
> On 05/08/2026 12:40, Ard Biesheuvel wrote:
>> From: Ard Biesheuvel <ardb@kernel.org>
>>
>> Without physical KASLR, the fixmap page tables will appear at an a
>> priori known offset in the physical address space, and due to the lack
>> of randomization, the linear map carries a writeable alias of the fixmap
>> PTE pages, which appears at an offset in the kernel VA space that is
>> also predictable.
>>
>> Given that the placement of the fixmap area is never randomized either,
>> a single store to this linear alias region is sufficient to map any
>> physical page with any permissions at a known offset in the kernel VA
>> space, including on top of the PTI trampoline.
>>
>> Avoid this, by remapping the fixmap PTE pages read-only in the linear
>> map.
>
> Sounds good, logical next step after unmapping the rest of data/BSS from
> the linear map :)
>
Indeed :-)
>> This is possible because all updates to bm_pte[] occur via the
>> mapping of the kernel image in the vmap area. A read-only mapping is
>> still needed for things like ptdump that walk the page tables.
>>
>> Cc: Ryan Roberts <ryan.roberts@arm.com>
>> Cc: Anshuman Khandual <anshuman.khandual@arm.com>
>> Cc: Kevin Brodsky <kevin.brodsky@arm.com>
>> Cc: Liz Prucka <lizprucka@google.com>
>> Cc: Seth Jenkins <sethjenkins@google.com>
>> Cc: Kees Cook <kees@kernel.org>
>> Cc: Jann Horn <jannh@google.com>
>> Cc: linux-hardening@vger.kernel.org
>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>> ---
>> arch/arm64/include/asm/set_memory.h | 2 ++
>> arch/arm64/mm/fixmap.c | 7 +++++++
>> arch/arm64/mm/pageattr.c | 10 ++++++++++
>> 3 files changed, 19 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
>> index 90f61b17275e..a685fb534c3e 100644
>> --- a/arch/arm64/include/asm/set_memory.h
>> +++ b/arch/arm64/include/asm/set_memory.h
>> @@ -11,6 +11,8 @@ bool can_set_direct_map(void);
>>
>> int set_memory_valid(unsigned long addr, int numpages, int enable);
>>
>> +int set_direct_map_ro(unsigned long addr, int numpages);
>> +
>> int set_direct_map_invalid_noflush(struct page *page);
>> int set_direct_map_default_noflush(struct page *page);
>> int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
>> diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
>> index f66a0016dd02..fcb571dffe82 100644
>> --- a/arch/arm64/mm/fixmap.c
>> +++ b/arch/arm64/mm/fixmap.c
>> @@ -14,6 +14,7 @@
>> #include <asm/fixmap.h>
>> #include <asm/kernel-pgtable.h>
>> #include <asm/pgalloc.h>
>> +#include <asm/set_memory.h>
>> #include <asm/tlbflush.h>
>>
>> /* ensure that the fixmap region does not grow down into the PCI I/O region */
>> @@ -173,3 +174,9 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
>>
>> return dt_virt;
>> }
>> +
>> +static int __init fixmap_remap_ro(void)
>> +{
>> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
>
> Should we not also remap bm_pmd and bm_pud?
>
> For that matter, do we need RW access via the linear map for any page
> annotated with __bss_pgtbl? I suppose that might be the case for
> kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
>
bm_pte[] is special because it is only ever written via the kernel mapping,
and never via the linear map. This is why it is being singled out in this
patch.
Whether or not bm_pmd[] can be treated as a special case depends on the page
size: with 4k pages, the whole array covers a virtual region of 1G, which is
currently guaranteed to be shared only with the PCI I/O space (but we could
move that out). With 16k pages, it covers 64G, and so it is shared with the
vmemmap and other virtual mappings in the vmalloc region, and so the current
kernel mapping code expects to be able to write those entries.
What we might do is generalize the logic that uses the fixmap to modify
pgd level entries in swapper_pg_dir, and use it for all modifications
at PMD level or higher if those tables are in .rodata
But this is a bit more complicated than this change, so I decided to
present this as a separate change.
>> +}
>> +late_initcall(fixmap_remap_ro);
>
> Is mark_rodata_ro() definitely too early to remap these pages RO?
>
No, we might just call this from there, afaict.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-26 9:07 ` Ard Biesheuvel
@ 2026-08-27 8:55 ` Kevin Brodsky
2026-08-27 9:04 ` Ard Biesheuvel
0 siblings, 1 reply; 8+ messages in thread
From: Kevin Brodsky @ 2026-08-27 8:55 UTC (permalink / raw)
To: Ard Biesheuvel, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On 26/08/2026 11:07, Ard Biesheuvel wrote:
>>> [...]
>>>
>>> +static int __init fixmap_remap_ro(void)
>>> +{
>>> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
>> Should we not also remap bm_pmd and bm_pud?
>>
>> For that matter, do we need RW access via the linear map for any page
>> annotated with __bss_pgtbl? I suppose that might be the case for
>> kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
>>
> bm_pte[] is special because it is only ever written via the kernel mapping,
> and never via the linear map. This is why it is being singled out in this
> patch.
Right I see __set_fixmap(). This is a very special case of calling
__set_pte() on a pointer derived from a global (i.e. pointing to the
kernel image and not the LM) so it makes sense to treat it differently.
I do wonder whether there is that much value in protecting bm_pte while
all page tables in init_pg_dir (also at a fixed offset in the LM) remain
writeable though.
Either way all this is interesting for my series protecting page tables
with pkeys [1] - it seems that to protect the fixmap, the easiest option
would be to change the pkey of .pgtlb in both the kernel image and LM
(the former for bm_pte, and the latter for everything else).
[1] https://lore.kernel.org/all/20260818-kpkeys-v9-0-743ad31b2c8f@arm.com/
> Whether or not bm_pmd[] can be treated as a special case depends on the page
> size: with 4k pages, the whole array covers a virtual region of 1G, which is
> currently guaranteed to be shared only with the PCI I/O space (but we could
> move that out). With 16k pages, it covers 64G, and so it is shared with the
> vmemmap and other virtual mappings in the vmalloc region, and so the current
> kernel mapping code expects to be able to write those entries.
>
> What we might do is generalize the logic that uses the fixmap to modify
> pgd level entries in swapper_pg_dir, and use it for all modifications
> at PMD level or higher if those tables are in .rodata
>
> But this is a bit more complicated than this change, so I decided to
> present this as a separate change.
Definitely, this is orthogonal to this patch. I wonder what the
performance impact would be if we used the fixmap for setting all kernel
entries at PMD and above. It would be nice to keep that logic easily
togglable, because with the kpkeys approach I mentioned above we have a
(most likely) much cheaper way of protecting these page tables.
>>> +}
>>> +late_initcall(fixmap_remap_ro);
>> Is mark_rodata_ro() definitely too early to remap these pages RO?
>>
> No, we might just call this from there, afaict.
Sounds good, could we then use update_mapping_prot()? I suppose not as
that would require mapping bm_pte separately in map_mem()?
- Kevin
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-27 8:55 ` Kevin Brodsky
@ 2026-08-27 9:04 ` Ard Biesheuvel
2026-08-27 10:00 ` Kevin Brodsky
0 siblings, 1 reply; 8+ messages in thread
From: Ard Biesheuvel @ 2026-08-27 9:04 UTC (permalink / raw)
To: Kevin Brodsky, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On Thu, 27 Aug 2026, at 10:55, Kevin Brodsky wrote:
> On 26/08/2026 11:07, Ard Biesheuvel wrote:
>>>> [...]
>>>>
>>>> +static int __init fixmap_remap_ro(void)
>>>> +{
>>>> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
>>> Should we not also remap bm_pmd and bm_pud?
>>>
>>> For that matter, do we need RW access via the linear map for any page
>>> annotated with __bss_pgtbl? I suppose that might be the case for
>>> kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
>>>
>> bm_pte[] is special because it is only ever written via the kernel mapping,
>> and never via the linear map. This is why it is being singled out in this
>> patch.
>
> Right I see __set_fixmap(). This is a very special case of calling
> __set_pte() on a pointer derived from a global (i.e. pointing to the
> kernel image and not the LM) so it makes sense to treat it differently.
>
> I do wonder whether there is that much value in protecting bm_pte while
> all page tables in init_pg_dir (also at a fixed offset in the LM) remain
> writeable though.
>
Yes, so those need to be taken care of as well.
> Either way all this is interesting for my series protecting page tables
> with pkeys [1] - it seems that to protect the fixmap, the easiest option
> would be to change the pkey of .pgtlb in both the kernel image and LM
> (the former for bm_pte, and the latter for everything else).
>
> [1] https://lore.kernel.org/all/20260818-kpkeys-v9-0-743ad31b2c8f@arm.com/
>
Thanks, I've been meaning to go through those but they are a bit overwhelming :-)
>> Whether or not bm_pmd[] can be treated as a special case depends on the page
>> size: with 4k pages, the whole array covers a virtual region of 1G, which is
>> currently guaranteed to be shared only with the PCI I/O space (but we could
>> move that out). With 16k pages, it covers 64G, and so it is shared with the
>> vmemmap and other virtual mappings in the vmalloc region, and so the current
>> kernel mapping code expects to be able to write those entries.
>>
>> What we might do is generalize the logic that uses the fixmap to modify
>> pgd level entries in swapper_pg_dir, and use it for all modifications
>> at PMD level or higher if those tables are in .rodata
>>
>> But this is a bit more complicated than this change, so I decided to
>> present this as a separate change.
>
> Definitely, this is orthogonal to this patch. I wonder what the
> performance impact would be if we used the fixmap for setting all kernel
> entries at PMD and above. It would be nice to keep that logic easily
> togglable, because with the kpkeys approach I mentioned above we have a
> (most likely) much cheaper way of protecting these page tables.
>
I'm currently experimenting with using __put_kernel_nofault() to update
the descriptors at all levels (including PTE) and handling the fault
using the fixmap approach. That way, all statically allocated page
tables could move to .rodata (except bm_pte[]) and there should be no
substantial performance impact.
>>>> +}
>>>> +late_initcall(fixmap_remap_ro);
>>> Is mark_rodata_ro() definitely too early to remap these pages RO?
>>>
>> No, we might just call this from there, afaict.
>
> Sounds good, could we then use update_mapping_prot()? I suppose not as
> that would require mapping bm_pte separately in map_mem()?
>
Yeah, but perhaps we should do the latter anyway, so we can remap the
region even if !can_set_direct_map().
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-27 9:04 ` Ard Biesheuvel
@ 2026-08-27 10:00 ` Kevin Brodsky
2026-08-27 10:16 ` Ard Biesheuvel
0 siblings, 1 reply; 8+ messages in thread
From: Kevin Brodsky @ 2026-08-27 10:00 UTC (permalink / raw)
To: Ard Biesheuvel, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On 27/08/2026 11:04, Ard Biesheuvel wrote:
> On Thu, 27 Aug 2026, at 10:55, Kevin Brodsky wrote:
>> On 26/08/2026 11:07, Ard Biesheuvel wrote:
>>>>> [...]
>>>>>
>>>>> +static int __init fixmap_remap_ro(void)
>>>>> +{
>>>>> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
>>>> Should we not also remap bm_pmd and bm_pud?
>>>>
>>>> For that matter, do we need RW access via the linear map for any page
>>>> annotated with __bss_pgtbl? I suppose that might be the case for
>>>> kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
>>>>
>>> bm_pte[] is special because it is only ever written via the kernel mapping,
>>> and never via the linear map. This is why it is being singled out in this
>>> patch.
>> Right I see __set_fixmap(). This is a very special case of calling
>> __set_pte() on a pointer derived from a global (i.e. pointing to the
>> kernel image and not the LM) so it makes sense to treat it differently.
>>
>> I do wonder whether there is that much value in protecting bm_pte while
>> all page tables in init_pg_dir (also at a fixed offset in the LM) remain
>> writeable though.
>>
> Yes, so those need to be taken care of as well.
But init_pg_dir needs to remain writeable in the LM, right?
>> Either way all this is interesting for my series protecting page tables
>> with pkeys [1] - it seems that to protect the fixmap, the easiest option
>> would be to change the pkey of .pgtlb in both the kernel image and LM
>> (the former for bm_pte, and the latter for everything else).
>>
>> [1] https://lore.kernel.org/all/20260818-kpkeys-v9-0-743ad31b2c8f@arm.com/
>>
> Thanks, I've been meaning to go through those but they are a bit overwhelming :-)
Yes there's a fair bit to go through, sorry for that... You'll probably
be most interested in patch 12, 13 and 19-21 - that's where page tables
are being mapped with a special pkey. The cover letter should give
enough context for them to make sense on their own.
>>> Whether or not bm_pmd[] can be treated as a special case depends on the page
>>> size: with 4k pages, the whole array covers a virtual region of 1G, which is
>>> currently guaranteed to be shared only with the PCI I/O space (but we could
>>> move that out). With 16k pages, it covers 64G, and so it is shared with the
>>> vmemmap and other virtual mappings in the vmalloc region, and so the current
>>> kernel mapping code expects to be able to write those entries.
>>>
>>> What we might do is generalize the logic that uses the fixmap to modify
>>> pgd level entries in swapper_pg_dir, and use it for all modifications
>>> at PMD level or higher if those tables are in .rodata
>>>
>>> But this is a bit more complicated than this change, so I decided to
>>> present this as a separate change.
>> Definitely, this is orthogonal to this patch. I wonder what the
>> performance impact would be if we used the fixmap for setting all kernel
>> entries at PMD and above. It would be nice to keep that logic easily
>> togglable, because with the kpkeys approach I mentioned above we have a
>> (most likely) much cheaper way of protecting these page tables.
>>
> I'm currently experimenting with using __put_kernel_nofault() to update
> the descriptors at all levels (including PTE) and handling the fault
> using the fixmap approach. That way, all statically allocated page
> tables could move to .rodata (except bm_pte[]) and there should be no
> substantial performance impact.
Doesn't that still mean modifying a fixmap PTE every time we update
kernel page tables? That doesn't exactly sound cheap.
>>>>> +}
>>>>> +late_initcall(fixmap_remap_ro);
>>>> Is mark_rodata_ro() definitely too early to remap these pages RO?
>>>>
>>> No, we might just call this from there, afaict.
>> Sounds good, could we then use update_mapping_prot()? I suppose not as
>> that would require mapping bm_pte separately in map_mem()?
>>
> Yeah, but perhaps we should do the latter anyway, so we can remap the
> region even if !can_set_direct_map().
Yep makes sense. Not having to add yet another set_direct_map_* function
is definitely welcome ;)
- Kevin
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-27 10:00 ` Kevin Brodsky
@ 2026-08-27 10:16 ` Ard Biesheuvel
2026-08-27 12:34 ` Kevin Brodsky
0 siblings, 1 reply; 8+ messages in thread
From: Ard Biesheuvel @ 2026-08-27 10:16 UTC (permalink / raw)
To: Kevin Brodsky, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On Thu, 27 Aug 2026, at 12:00, Kevin Brodsky wrote:
> On 27/08/2026 11:04, Ard Biesheuvel wrote:
>> On Thu, 27 Aug 2026, at 10:55, Kevin Brodsky wrote:
>>> On 26/08/2026 11:07, Ard Biesheuvel wrote:
>>>>>> [...]
>>>>>>
>>>>>> +static int __init fixmap_remap_ro(void)
>>>>>> +{
>>>>>> + return set_direct_map_ro((unsigned long)lm_alias(&bm_pte), NR_BM_PTE_TABLES);
>>>>> Should we not also remap bm_pmd and bm_pud?
>>>>>
>>>>> For that matter, do we need RW access via the linear map for any page
>>>>> annotated with __bss_pgtbl? I suppose that might be the case for
>>>>> kasan_early_shadow_* but I don't know enough about KASAN to tell for sure.
>>>>>
>>>> bm_pte[] is special because it is only ever written via the kernel mapping,
>>>> and never via the linear map. This is why it is being singled out in this
>>>> patch.
>>> Right I see __set_fixmap(). This is a very special case of calling
>>> __set_pte() on a pointer derived from a global (i.e. pointing to the
>>> kernel image and not the LM) so it makes sense to treat it differently.
>>>
>>> I do wonder whether there is that much value in protecting bm_pte while
>>> all page tables in init_pg_dir (also at a fixed offset in the LM) remain
>>> writeable though.
>>>
>> Yes, so those need to be taken care of as well.
>
> But init_pg_dir needs to remain writeable in the LM, right?
>
>>> Either way all this is interesting for my series protecting page tables
>>> with pkeys [1] - it seems that to protect the fixmap, the easiest option
>>> would be to change the pkey of .pgtlb in both the kernel image and LM
>>> (the former for bm_pte, and the latter for everything else).
>>>
>>> [1] https://lore.kernel.org/all/20260818-kpkeys-v9-0-743ad31b2c8f@arm.com/
>>>
>> Thanks, I've been meaning to go through those but they are a bit overwhelming :-)
>
> Yes there's a fair bit to go through, sorry for that... You'll probably
> be most interested in patch 12, 13 and 19-21 - that's where page tables
> are being mapped with a special pkey. The cover letter should give
> enough context for them to make sense on their own.
>
OK, that helps ... :-)
>>>> Whether or not bm_pmd[] can be treated as a special case depends on the page
>>>> size: with 4k pages, the whole array covers a virtual region of 1G, which is
>>>> currently guaranteed to be shared only with the PCI I/O space (but we could
>>>> move that out). With 16k pages, it covers 64G, and so it is shared with the
>>>> vmemmap and other virtual mappings in the vmalloc region, and so the current
>>>> kernel mapping code expects to be able to write those entries.
>>>>
>>>> What we might do is generalize the logic that uses the fixmap to modify
>>>> pgd level entries in swapper_pg_dir, and use it for all modifications
>>>> at PMD level or higher if those tables are in .rodata
>>>>
>>>> But this is a bit more complicated than this change, so I decided to
>>>> present this as a separate change.
>>> Definitely, this is orthogonal to this patch. I wonder what the
>>> performance impact would be if we used the fixmap for setting all kernel
>>> entries at PMD and above. It would be nice to keep that logic easily
>>> togglable, because with the kpkeys approach I mentioned above we have a
>>> (most likely) much cheaper way of protecting these page tables.
>>>
>> I'm currently experimenting with using __put_kernel_nofault() to update
>> the descriptors at all levels (including PTE) and handling the fault
>> using the fixmap approach. That way, all statically allocated page
>> tables could move to .rodata (except bm_pte[]) and there should be no
>> substantial performance impact.
>
> Doesn't that still mean modifying a fixmap PTE every time we update
> kernel page tables? That doesn't exactly sound cheap.
>
Only the ones that were allocated statically, which cover the region
around the kernel image, and the special cases (fixmap, kasan). Everything
else is allocated dynamically, using memblock_alloc() if very early
during the boot.
>>>>>> +}
>>>>>> +late_initcall(fixmap_remap_ro);
>>>>> Is mark_rodata_ro() definitely too early to remap these pages RO?
>>>>>
>>>> No, we might just call this from there, afaict.
>>> Sounds good, could we then use update_mapping_prot()? I suppose not as
>>> that would require mapping bm_pte separately in map_mem()?
>>>
>> Yeah, but perhaps we should do the latter anyway, so we can remap the
>> region even if !can_set_direct_map().
>
> Yep makes sense. Not having to add yet another set_direct_map_* function
> is definitely welcome ;)
>
Ack.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map
2026-08-27 10:16 ` Ard Biesheuvel
@ 2026-08-27 12:34 ` Kevin Brodsky
0 siblings, 0 replies; 8+ messages in thread
From: Kevin Brodsky @ 2026-08-27 12:34 UTC (permalink / raw)
To: Ard Biesheuvel, Ard Biesheuvel, linux-arm-kernel
Cc: Will Deacon, Catalin Marinas, Mark Rutland, Ryan Roberts,
Anshuman Khandual, Liz Prucka, Seth Jenkins, Kees Cook, Jann Horn,
linux-hardening
On 27/08/2026 12:16, Ard Biesheuvel wrote:
>>> I'm currently experimenting with using __put_kernel_nofault() to update
>>> the descriptors at all levels (including PTE) and handling the fault
>>> using the fixmap approach. That way, all statically allocated page
>>> tables could move to .rodata (except bm_pte[]) and there should be no
>>> substantial performance impact.
>> Doesn't that still mean modifying a fixmap PTE every time we update
>> kernel page tables? That doesn't exactly sound cheap.
>>
> Only the ones that were allocated statically, which cover the region
> around the kernel image, and the special cases (fixmap, kasan). Everything
> else is allocated dynamically, using memblock_alloc() if very early
> during the boot.
Ah yes I see! Then it does sound very reasonable, those page tables
should rarely be written if at all. For kpkeys that would mean we would
just need to map bm_pte with the special pkey to complete the
protection, the rest being already RO or pkey-protected.
- Kevin
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-27 12:34 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 10:40 [RFC PATCH] arm64: mm: Map fixmap PTE tables r/o in the linear map Ard Biesheuvel
2026-08-21 13:20 ` Kevin Brodsky
2026-08-26 9:07 ` Ard Biesheuvel
2026-08-27 8:55 ` Kevin Brodsky
2026-08-27 9:04 ` Ard Biesheuvel
2026-08-27 10:00 ` Kevin Brodsky
2026-08-27 10:16 ` Ard Biesheuvel
2026-08-27 12:34 ` Kevin Brodsky
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox