Skip to content

Commit fdea03e

Browse files
ramosian-gliderakpm00
authored andcommitted
mm: kmsan: handle alloc failures in kmsan_ioremap_page_range()
Similarly to kmsan_vmap_pages_range_noflush(), kmsan_ioremap_page_range() must also properly handle allocation/mapping failures. In the case of such, it must clean up the already created metadata mappings and return an error code, so that the error can be propagated to ioremap_page_range(). Without doing so, KMSAN may silently fail to bring the metadata for the page range into a consistent state, which will result in user-visible crashes when trying to access them. Link: https://lkml.kernel.org/r/[email protected] Fixes: b073d7f ("mm: kmsan: maintain KMSAN metadata for page operations") Signed-off-by: Alexander Potapenko <[email protected]> Reported-by: Dipanjan Das <[email protected]> Link: https://lore.kernel.org/linux-mm/CANX2M5ZRrRA64k0hOif02TjmY9kbbO2aCBPyq79es34RXZ=cAw@mail.gmail.com/ Reviewed-by: Marco Elver <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Dmitry Vyukov <[email protected]> Cc: Uladzislau Rezki (Sony) <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 47ebd03 commit fdea03e

File tree

3 files changed

+59
-19
lines changed

3 files changed

+59
-19
lines changed

include/linux/kmsan.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,12 @@ void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end);
160160
* @page_shift: page_shift argument passed to vmap_range_noflush().
161161
*
162162
* KMSAN creates new metadata pages for the physical pages mapped into the
163-
* virtual memory.
163+
* virtual memory. Returns 0 on success, callers must check for non-zero return
164+
* value.
164165
*/
165-
void kmsan_ioremap_page_range(unsigned long addr, unsigned long end,
166-
phys_addr_t phys_addr, pgprot_t prot,
167-
unsigned int page_shift);
166+
int kmsan_ioremap_page_range(unsigned long addr, unsigned long end,
167+
phys_addr_t phys_addr, pgprot_t prot,
168+
unsigned int page_shift);
168169

169170
/**
170171
* kmsan_iounmap_page_range() - Notify KMSAN about a iounmap_page_range() call.
@@ -296,12 +297,12 @@ static inline void kmsan_vunmap_range_noflush(unsigned long start,
296297
{
297298
}
298299

299-
static inline void kmsan_ioremap_page_range(unsigned long start,
300-
unsigned long end,
301-
phys_addr_t phys_addr,
302-
pgprot_t prot,
303-
unsigned int page_shift)
300+
static inline int kmsan_ioremap_page_range(unsigned long start,
301+
unsigned long end,
302+
phys_addr_t phys_addr, pgprot_t prot,
303+
unsigned int page_shift)
304304
{
305+
return 0;
305306
}
306307

307308
static inline void kmsan_iounmap_page_range(unsigned long start,

mm/kmsan/hooks.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,35 +148,74 @@ void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)
148148
* into the virtual memory. If those physical pages already had shadow/origin,
149149
* those are ignored.
150150
*/
151-
void kmsan_ioremap_page_range(unsigned long start, unsigned long end,
152-
phys_addr_t phys_addr, pgprot_t prot,
153-
unsigned int page_shift)
151+
int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
152+
phys_addr_t phys_addr, pgprot_t prot,
153+
unsigned int page_shift)
154154
{
155155
gfp_t gfp_mask = GFP_KERNEL | __GFP_ZERO;
156156
struct page *shadow, *origin;
157157
unsigned long off = 0;
158-
int nr;
158+
int nr, err = 0, clean = 0, mapped;
159159

160160
if (!kmsan_enabled || kmsan_in_runtime())
161-
return;
161+
return 0;
162162

163163
nr = (end - start) / PAGE_SIZE;
164164
kmsan_enter_runtime();
165-
for (int i = 0; i < nr; i++, off += PAGE_SIZE) {
165+
for (int i = 0; i < nr; i++, off += PAGE_SIZE, clean = i) {
166166
shadow = alloc_pages(gfp_mask, 1);
167167
origin = alloc_pages(gfp_mask, 1);
168-
__vmap_pages_range_noflush(
168+
if (!shadow || !origin) {
169+
err = -ENOMEM;
170+
goto ret;
171+
}
172+
mapped = __vmap_pages_range_noflush(
169173
vmalloc_shadow(start + off),
170174
vmalloc_shadow(start + off + PAGE_SIZE), prot, &shadow,
171175
PAGE_SHIFT);
172-
__vmap_pages_range_noflush(
176+
if (mapped) {
177+
err = mapped;
178+
goto ret;
179+
}
180+
shadow = NULL;
181+
mapped = __vmap_pages_range_noflush(
173182
vmalloc_origin(start + off),
174183
vmalloc_origin(start + off + PAGE_SIZE), prot, &origin,
175184
PAGE_SHIFT);
185+
if (mapped) {
186+
__vunmap_range_noflush(
187+
vmalloc_shadow(start + off),
188+
vmalloc_shadow(start + off + PAGE_SIZE));
189+
err = mapped;
190+
goto ret;
191+
}
192+
origin = NULL;
193+
}
194+
/* Page mapping loop finished normally, nothing to clean up. */
195+
clean = 0;
196+
197+
ret:
198+
if (clean > 0) {
199+
/*
200+
* Something went wrong. Clean up shadow/origin pages allocated
201+
* on the last loop iteration, then delete mappings created
202+
* during the previous iterations.
203+
*/
204+
if (shadow)
205+
__free_pages(shadow, 1);
206+
if (origin)
207+
__free_pages(origin, 1);
208+
__vunmap_range_noflush(
209+
vmalloc_shadow(start),
210+
vmalloc_shadow(start + clean * PAGE_SIZE));
211+
__vunmap_range_noflush(
212+
vmalloc_origin(start),
213+
vmalloc_origin(start + clean * PAGE_SIZE));
176214
}
177215
flush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));
178216
flush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));
179217
kmsan_leave_runtime();
218+
return err;
180219
}
181220

182221
void kmsan_iounmap_page_range(unsigned long start, unsigned long end)

mm/vmalloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ int ioremap_page_range(unsigned long addr, unsigned long end,
313313
ioremap_max_page_shift);
314314
flush_cache_vmap(addr, end);
315315
if (!err)
316-
kmsan_ioremap_page_range(addr, end, phys_addr, prot,
317-
ioremap_max_page_shift);
316+
err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
317+
ioremap_max_page_shift);
318318
return err;
319319
}
320320

0 commit comments

Comments
 (0)