mm: use compare-exchange operation to set KASAN page tag
Subsystems (3)
PATCH v3 1/1 mm: use compare-exchange operation to set KASAN page tag
It has been reported that the tag setting operation on newly-allocated pages can cause the page flags to be corrupted when performed concurrently with other flag updates as a result of the use of non-atomic operations. Fix the problem by using a compare-exchange loop to update the tag.
---
v3:
- use try_cmpxchg() as suggested by Peter Zijlstra on another patch
v2:
- use READ_ONCE()
include/linux/mm.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
include/linux/mm.h +12 -5
@@ -1531,11 +1531,18 @@ static inline u8 page_kasan_tag(const struct page *page)static inline void page_kasan_tag_set (struct page *page,u8 tag) { -if (kasan_enabled ()) { - tag ^=0xff ; - page->flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); - page->flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; - } +unsigned long old_flags, flags; + +if (!kasan_enabled ()) +return ; + + tag ^=0xff ; + old_flags =READ_ONCE (page->flags); +do { + flags = old_flags; + flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); + flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; + }while (unlikely (!try_cmpxchg (&page->flags, &old_flags, flags))); }static inline void page_kasan_tag_reset (struct page *page)
Signature · 1 line
2.34.1.703.g22d0c6ccf7-goog
Quoted 51 lines · Peter Collingbourne
It has been reported that the tag setting operation on newly-allocated pages can cause the page flags to be corrupted when performed concurrently with other flag updates as a result of the use of non-atomic operations. Fix the problem by using a compare-exchange loop to update the tag.
Signed-off-by Peter Collingbourne <[email protected]>Fixes 2813b9c02962 ("kasan, mm, arm64: tag non slab memory allocated via pagealloc")---
v3:
- usetry_cmpxchg()as suggested by Peter Zijlstra on another patchv2:
- useREAD_ONCE()include/linux/mm.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)include/linux/mm.h +12
-6@@ -1531,11 +1531,18 @@ static inline u8 page_kasan_tag(const struct page *page)static inline void page_kasan_tag_set (struct page *page,u8 tag) { -if (kasan_enabled ()) { - tag ^=0xff ; - page->flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); - page->flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; - } +unsigned long old_flags, flags; + +if (!kasan_enabled ()) +return ; + + tag ^=0xff ; + old_flags =READ_ONCE (page->flags); +do { + flags = old_flags; + flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); + flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; + }while (unlikely (!try_cmpxchg (&page->flags, &old_flags, flags))); }static inline void page_kasan_tag_reset (struct page *page) --
Quoted 2 lines
2.34.1.703.g22d0c6ccf7-goog
FWIW, try_cmpxchg() doesn't seem to be doing annotated atomic accesses when accessing old_flags, so using READ_ONCE() in page_kasan_tag_set() seems pointless after all.
Quoted 58 lines · Andrey Konovalov
Quoted 51 lines · Peter Collingbourne
It has been reported that the tag setting operation on newly-allocated pages can cause the page flags to be corrupted when performed concurrently with other flag updates as a result of the use of non-atomic operations. Fix the problem by using a compare-exchange loop to update the tag.
Signed-off-by Peter Collingbourne <[email protected]>Fixes 2813b9c02962 ("kasan, mm, arm64: tag non slab memory allocated via pagealloc")---
v3:
- usetry_cmpxchg()as suggested by Peter Zijlstra on another patchv2:
- useREAD_ONCE()include/linux/mm.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)include/linux/mm.h +12
-6@@ -1531,11 +1531,18 @@ static inline u8 page_kasan_tag(const struct page *page)static inline void page_kasan_tag_set (struct page *page,u8 tag) { -if (kasan_enabled ()) { - tag ^=0xff ; - page->flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); - page->flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; - } +unsigned long old_flags, flags; + +if (!kasan_enabled ()) +return ; + + tag ^=0xff ; + old_flags =READ_ONCE (page->flags); +do { + flags = old_flags; + flags &= ~(KASAN_TAG_MASK <<KASAN_TAG_PGSHIFT ); + flags |= (tag &KASAN_TAG_MASK ) <<KASAN_TAG_PGSHIFT ; + }while (unlikely (!try_cmpxchg (&page->flags, &old_flags, flags))); }static inline void page_kasan_tag_reset (struct page *page) --Quoted 2 lines
2.34.1.703.g22d0c6ccf7-goog
Reviewed-by Andrey Konovalov <[email protected]>
Quoted 3 lines
FWIW,
try_cmpxchg()doesn't seem to be doing annotated atomic accesses when accessingold_flags, so usingREAD_ONCE()inpage_kasan_tag_set()seems pointless after all.
Ah, nevermind. For a second I thought that try_cmpxchg() is actually accessing page->flags through its second argument.