Skip to content

Commit e059853

Browse files
ctmarinasMarc Zyngier
authored andcommitted
arm64: mte: Fix/clarify the PG_mte_tagged semantics
Currently the PG_mte_tagged page flag mostly means the page contains valid tags and it should be set after the tags have been cleared or restored. However, in mte_sync_tags() it is set before setting the tags to avoid, in theory, a race with concurrent mprotect(PROT_MTE) for shared pages. However, a concurrent mprotect(PROT_MTE) with a copy on write in another thread can cause the new page to have stale tags. Similarly, tag reading via ptrace() can read stale tags if the PG_mte_tagged flag is set before actually clearing/restoring the tags. Fix the PG_mte_tagged semantics so that it is only set after the tags have been cleared or restored. This is safe for swap restoring into a MAP_SHARED or CoW page since the core code takes the page lock. Add two functions to test and set the PG_mte_tagged flag with acquire and release semantics. The downside is that concurrent mprotect(PROT_MTE) on a MAP_SHARED page may cause tag loss. This is already the case for KVM guests if a VMM changes the page protection while the guest triggers a user_mem_abort(). Signed-off-by: Catalin Marinas <[email protected]> [[email protected]: fix build with CONFIG_ARM64_MTE disabled] Signed-off-by: Peter Collingbourne <[email protected]> Reviewed-by: Cornelia Huck <[email protected]> Reviewed-by: Steven Price <[email protected]> Cc: Will Deacon <[email protected]> Cc: Marc Zyngier <[email protected]> Cc: Peter Collingbourne <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent b0284cd commit e059853

File tree

11 files changed

+56
-18
lines changed

11 files changed

+56
-18
lines changed

arch/arm64/include/asm/mte.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,29 @@ void mte_free_tag_storage(char *storage);
3737
/* track which pages have valid allocation tags */
3838
#define PG_mte_tagged PG_arch_2
3939

40+
static inline void set_page_mte_tagged(struct page *page)
41+
{
42+
/*
43+
* Ensure that the tags written prior to this function are visible
44+
* before the page flags update.
45+
*/
46+
smp_wmb();
47+
set_bit(PG_mte_tagged, &page->flags);
48+
}
49+
50+
static inline bool page_mte_tagged(struct page *page)
51+
{
52+
bool ret = test_bit(PG_mte_tagged, &page->flags);
53+
54+
/*
55+
* If the page is tagged, ensure ordering with a likely subsequent
56+
* read of the tags.
57+
*/
58+
if (ret)
59+
smp_rmb();
60+
return ret;
61+
}
62+
4063
void mte_zero_clear_page_tags(void *addr);
4164
void mte_sync_tags(pte_t old_pte, pte_t pte);
4265
void mte_copy_page_tags(void *kto, const void *kfrom);
@@ -56,6 +79,13 @@ size_t mte_probe_user_range(const char __user *uaddr, size_t size);
5679
/* unused if !CONFIG_ARM64_MTE, silence the compiler */
5780
#define PG_mte_tagged 0
5881

82+
static inline void set_page_mte_tagged(struct page *page)
83+
{
84+
}
85+
static inline bool page_mte_tagged(struct page *page)
86+
{
87+
return false;
88+
}
5989
static inline void mte_zero_clear_page_tags(void *addr)
6090
{
6191
}

arch/arm64/include/asm/pgtable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,7 @@ static inline void arch_swap_invalidate_area(int type)
10501050
static inline void arch_swap_restore(swp_entry_t entry, struct folio *folio)
10511051
{
10521052
if (system_supports_mte() && mte_restore_tags(entry, &folio->page))
1053-
set_bit(PG_mte_tagged, &folio->flags);
1053+
set_page_mte_tagged(&folio->page);
10541054
}
10551055

10561056
#endif /* CONFIG_ARM64_MTE */

arch/arm64/kernel/cpufeature.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2050,8 +2050,10 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)
20502050
* Clear the tags in the zero page. This needs to be done via the
20512051
* linear map which has the Tagged attribute.
20522052
*/
2053-
if (!test_and_set_bit(PG_mte_tagged, &ZERO_PAGE(0)->flags))
2053+
if (!page_mte_tagged(ZERO_PAGE(0))) {
20542054
mte_clear_page_tags(lm_alias(empty_zero_page));
2055+
set_page_mte_tagged(ZERO_PAGE(0));
2056+
}
20552057

20562058
kasan_init_hw_tags_cpu();
20572059
}

arch/arm64/kernel/elfcore.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static int mte_dump_tag_range(struct coredump_params *cprm,
4747
* Pages mapped in user space as !pte_access_permitted() (e.g.
4848
* PROT_EXEC only) may not have the PG_mte_tagged flag set.
4949
*/
50-
if (!test_bit(PG_mte_tagged, &page->flags)) {
50+
if (!page_mte_tagged(page)) {
5151
put_page(page);
5252
dump_skip(cprm, MTE_PAGE_TAG_STORAGE);
5353
continue;

arch/arm64/kernel/hibernate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static int swsusp_mte_save_tags(void)
271271
if (!page)
272272
continue;
273273

274-
if (!test_bit(PG_mte_tagged, &page->flags))
274+
if (!page_mte_tagged(page))
275275
continue;
276276

277277
ret = save_tags(page, pfn);

arch/arm64/kernel/mte.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ static void mte_sync_page_tags(struct page *page, pte_t old_pte,
4141
if (check_swap && is_swap_pte(old_pte)) {
4242
swp_entry_t entry = pte_to_swp_entry(old_pte);
4343

44-
if (!non_swap_entry(entry) && mte_restore_tags(entry, page))
44+
if (!non_swap_entry(entry) && mte_restore_tags(entry, page)) {
45+
set_page_mte_tagged(page);
4546
return;
47+
}
4648
}
4749

4850
if (!pte_is_tagged)
@@ -52,8 +54,10 @@ static void mte_sync_page_tags(struct page *page, pte_t old_pte,
5254
* Test PG_mte_tagged again in case it was racing with another
5355
* set_pte_at().
5456
*/
55-
if (!test_and_set_bit(PG_mte_tagged, &page->flags))
57+
if (!page_mte_tagged(page)) {
5658
mte_clear_page_tags(page_address(page));
59+
set_page_mte_tagged(page);
60+
}
5761
}
5862

5963
void mte_sync_tags(pte_t old_pte, pte_t pte)
@@ -69,9 +73,11 @@ void mte_sync_tags(pte_t old_pte, pte_t pte)
6973

7074
/* if PG_mte_tagged is set, tags have already been initialised */
7175
for (i = 0; i < nr_pages; i++, page++) {
72-
if (!test_bit(PG_mte_tagged, &page->flags))
76+
if (!page_mte_tagged(page)) {
7377
mte_sync_page_tags(page, old_pte, check_swap,
7478
pte_is_tagged);
79+
set_page_mte_tagged(page);
80+
}
7581
}
7682

7783
/* ensure the tags are visible before the PTE is set */
@@ -96,8 +102,7 @@ int memcmp_pages(struct page *page1, struct page *page2)
96102
* pages is tagged, set_pte_at() may zero or change the tags of the
97103
* other page via mte_sync_tags().
98104
*/
99-
if (test_bit(PG_mte_tagged, &page1->flags) ||
100-
test_bit(PG_mte_tagged, &page2->flags))
105+
if (page_mte_tagged(page1) || page_mte_tagged(page2))
101106
return addr1 != addr2;
102107

103108
return ret;
@@ -454,7 +459,7 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr,
454459
put_page(page);
455460
break;
456461
}
457-
WARN_ON_ONCE(!test_bit(PG_mte_tagged, &page->flags));
462+
WARN_ON_ONCE(!page_mte_tagged(page));
458463

459464
/* limit access to the end of the page */
460465
offset = offset_in_page(addr);

arch/arm64/kvm/guest.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
10591059
maddr = page_address(page);
10601060

10611061
if (!write) {
1062-
if (test_bit(PG_mte_tagged, &page->flags))
1062+
if (page_mte_tagged(page))
10631063
num_tags = mte_copy_tags_to_user(tags, maddr,
10641064
MTE_GRANULES_PER_PAGE);
10651065
else
@@ -1076,7 +1076,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
10761076
* completed fully
10771077
*/
10781078
if (num_tags == MTE_GRANULES_PER_PAGE)
1079-
set_bit(PG_mte_tagged, &page->flags);
1079+
set_page_mte_tagged(page);
10801080

10811081
kvm_release_pfn_dirty(pfn);
10821082
}

arch/arm64/kvm/mmu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,9 +1110,9 @@ static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn,
11101110
return -EFAULT;
11111111

11121112
for (i = 0; i < nr_pages; i++, page++) {
1113-
if (!test_bit(PG_mte_tagged, &page->flags)) {
1113+
if (!page_mte_tagged(page)) {
11141114
mte_clear_page_tags(page_address(page));
1115-
set_bit(PG_mte_tagged, &page->flags);
1115+
set_page_mte_tagged(page);
11161116
}
11171117
}
11181118

arch/arm64/mm/copypage.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ void copy_highpage(struct page *to, struct page *from)
2121

2222
copy_page(kto, kfrom);
2323

24-
if (system_supports_mte() && test_bit(PG_mte_tagged, &from->flags)) {
25-
set_bit(PG_mte_tagged, &to->flags);
24+
if (system_supports_mte() && page_mte_tagged(from)) {
25+
page_kasan_tag_reset(to);
2626
mte_copy_page_tags(kto, kfrom);
27+
set_page_mte_tagged(to);
2728
}
2829
}
2930
EXPORT_SYMBOL(copy_highpage);

arch/arm64/mm/fault.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -934,5 +934,5 @@ struct page *alloc_zeroed_user_highpage_movable(struct vm_area_struct *vma,
934934
void tag_clear_highpage(struct page *page)
935935
{
936936
mte_zero_clear_page_tags(page_address(page));
937-
set_bit(PG_mte_tagged, &page->flags);
937+
set_page_mte_tagged(page);
938938
}

0 commit comments

Comments
 (0)