The problem presented here is related to NUMA systems and tag-based KASAN mode. Getting to it can be explained in the following points: 1. A new chunk is created with pcpu_create_chunk() and vm_structs are allocated. On systems with one NUMA node only one is allocated, but with more NUMA nodes at least a second one will be allocated too. 2. chunk->base_addr is assigned the modified value of vms[0]->addr and thus inherits the tag of this allocated structure. 3. In pcpu_alloc() for each possible cpu pcpu_chunk_addr() is executed which calculates per cpu pointers that correspond to the vms structure addresses. The calculations are based on adding an offset from a table to chunk->base_addr. Here the problem presents itself since for addresses based on vms[1] and up, the tag will be different than the ones based on vms[0] (base_addr). The tag mismatch happens and an error is reported. Unpoison all the vms[]->addr with the same tag to resolve the mismatch. Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx> --- Changelog v3: - Remove last version of this patch that just resets the tag on base_addr and add this patch that unpoisons all areas with the same tag instead. include/linux/kasan.h | 10 ++++++++++ mm/kasan/shadow.c | 11 +++++++++++ mm/vmalloc.c | 3 +-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 54481f8c30c5..bd033b2ba383 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -613,6 +613,13 @@ static __always_inline void kasan_poison_vmalloc(const void *start, __kasan_poison_vmalloc(start, size); } +void __kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms); +static __always_inline void kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms) +{ + if (kasan_enabled()) + __kasan_unpoison_vmap_areas(vms, nr_vms); +} + #else /* CONFIG_KASAN_VMALLOC */ static inline void kasan_populate_early_vm_area_shadow(void *start, @@ -637,6 +644,9 @@ static inline void *kasan_unpoison_vmalloc(const void *start, static inline void kasan_poison_vmalloc(const void *start, unsigned long size) { } +static inline void kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms) +{ } + #endif /* CONFIG_KASAN_VMALLOC */ #if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \ diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c index 88d1c9dcb507..9496f256bc0f 100644 --- a/mm/kasan/shadow.c +++ b/mm/kasan/shadow.c @@ -582,6 +582,17 @@ void __kasan_poison_vmalloc(const void *start, unsigned long size) kasan_poison(start, size, KASAN_VMALLOC_INVALID, false); } +void __kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms) +{ + int area; + + for (area = 0 ; area < nr_vms ; area++) { + kasan_poison(vms[area]->addr, vms[area]->size, + arch_kasan_get_tag(vms[0]->addr), false); + arch_kasan_set_tag(vms[area]->addr, arch_kasan_get_tag(vms[0]->addr)); + } +} + #else /* CONFIG_KASAN_VMALLOC */ int kasan_alloc_module_shadow(void *addr, size_t size, gfp_t gfp_mask) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 61981ee1c9d2..fbd56bf8aeb2 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4783,8 +4783,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets, * non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc(). */ for (area = 0; area < nr_vms; area++) - vms[area]->addr = kasan_unpoison_vmalloc(vms[area]->addr, - vms[area]->size, KASAN_VMALLOC_PROT_NORMAL); + kasan_unpoison_vmap_areas(vms, nr_vms); kfree(vas); return vms; -- 2.49.0