Skip to content

Commit e80d390

Browse files
joergroedeltorvalds
authored andcommitted
mm: track page table modifications in __apply_to_page_range()
__apply_to_page_range() is also used to change and/or allocate page-table pages in the vmalloc area of the address space. Make sure these changes get synchronized to other page-tables in the system by calling arch_sync_kernel_mappings() when necessary. The impact appears limited to x86-32, where apply_to_page_range may miss updating the PMD. That leads to explosions in drivers like BUG: unable to handle page fault for address: fe036000 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page *pde = 00000000 Oops: 0002 [#1] SMP CPU: 3 PID: 1300 Comm: gem_concurrent_ Not tainted 5.9.0-rc1+ #16 Hardware name: /NUC6i3SYB, BIOS SYSKLi35.86A.0024.2015.1027.2142 10/27/2015 EIP: __execlists_context_alloc+0x132/0x2d0 [i915] Code: 31 d2 89 f0 e8 2f 55 02 00 89 45 e8 3d 00 f0 ff ff 0f 87 11 01 00 00 8b 4d e8 03 4b 30 b8 5a 5a 5a 5a ba 01 00 00 00 8d 79 04 <c7> 01 5a 5a 5a 5a c7 81 fc 0f 00 00 5a 5a 5a 5a 83 e7 fc 29 f9 81 EAX: 5a5a5a5a EBX: f60ca000 ECX: fe036000 EDX: 00000001 ESI: f43b7340 EDI: fe036004 EBP: f6389cb8 ESP: f6389c9c DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 EFLAGS: 00010286 CR0: 80050033 CR2: fe036000 CR3: 2d361000 CR4: 001506d DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 DR6: fffe0ff0 DR7: 00000400 Call Trace: execlists_context_alloc+0x10/0x20 [i915] intel_context_alloc_state+0x3f/0x70 [i915] __intel_context_do_pin+0x117/0x170 [i915] i915_gem_do_execbuffer+0xcc7/0x2500 [i915] i915_gem_execbuffer2_ioctl+0xcd/0x1f0 [i915] drm_ioctl_kernel+0x8f/0xd0 drm_ioctl+0x223/0x3d0 __ia32_sys_ioctl+0x1ab/0x760 __do_fast_syscall_32+0x3f/0x70 do_fast_syscall_32+0x29/0x60 do_SYSENTER_32+0x15/0x20 entry_SYSENTER_32+0x9f/0xf2 EIP: 0xb7f28559 Code: 03 74 c0 01 10 05 03 74 b8 01 10 06 03 74 b4 01 10 07 03 74 b0 01 10 08 03 74 d8 01 00 00 00 00 00 51 52 55 89 e5 0f 34 cd 80 <5d> 5a 59 c3 90 90 90 90 8d 76 00 58 b8 77 00 00 00 cd 80 90 8d 76 EAX: ffffffda EBX: 00000005 ECX: c0406469 EDX: bf95556c ESI: b7e68000 EDI: c0406469 EBP: 00000005 ESP: bf9554d8 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b EFLAGS: 00000296 Modules linked in: i915 x86_pkg_temp_thermal intel_powerclamp crc32_pclmul crc32c_intel intel_cstate intel_uncore intel_gtt drm_kms_helper intel_pch_thermal video button autofs4 i2c_i801 i2c_smbus fan CR2: 00000000fe036000 It looks like kasan, xen and i915 are vulnerable. Actual impact is "on thinkpad X60 in 5.9-rc1, screen starts blinking after 30-or-so minutes, and machine is unusable" [[email protected]: ARCH_PAGE_TABLE_SYNC_MASK needs vmalloc.h] Link: https://lkml.kernel.org/r/[email protected] [[email protected]: changelog addition] [[email protected]: changelog addition] Fixes: 2ba3e69 ("mm/vmalloc: track which page-table levels were modified") Fixes: 86cf69f ("x86/mm/32: implement arch_sync_kernel_mappings()") Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Stephen Rothwell <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Tested-by: Chris Wilson <[email protected]> [x86-32] Tested-by: Pavel Machek <[email protected]> Acked-by: Linus Torvalds <[email protected]> Cc: <[email protected]> [5.8+] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9d90dd1 commit e80d390

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

mm/memory.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include <linux/numa.h>
7474
#include <linux/perf_event.h>
7575
#include <linux/ptrace.h>
76+
#include <linux/vmalloc.h>
7677

7778
#include <trace/events/kmem.h>
7879

@@ -83,6 +84,7 @@
8384
#include <asm/tlb.h>
8485
#include <asm/tlbflush.h>
8586

87+
#include "pgalloc-track.h"
8688
#include "internal.h"
8789

8890
#if defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS) && !defined(CONFIG_COMPILE_TEST)
@@ -2206,15 +2208,16 @@ EXPORT_SYMBOL(vm_iomap_memory);
22062208

22072209
static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd,
22082210
unsigned long addr, unsigned long end,
2209-
pte_fn_t fn, void *data, bool create)
2211+
pte_fn_t fn, void *data, bool create,
2212+
pgtbl_mod_mask *mask)
22102213
{
22112214
pte_t *pte;
22122215
int err = 0;
22132216
spinlock_t *ptl;
22142217

22152218
if (create) {
22162219
pte = (mm == &init_mm) ?
2217-
pte_alloc_kernel(pmd, addr) :
2220+
pte_alloc_kernel_track(pmd, addr, mask) :
22182221
pte_alloc_map_lock(mm, pmd, addr, &ptl);
22192222
if (!pte)
22202223
return -ENOMEM;
@@ -2235,6 +2238,7 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd,
22352238
break;
22362239
}
22372240
} while (addr += PAGE_SIZE, addr != end);
2241+
*mask |= PGTBL_PTE_MODIFIED;
22382242

22392243
arch_leave_lazy_mmu_mode();
22402244

@@ -2245,7 +2249,8 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd,
22452249

22462250
static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
22472251
unsigned long addr, unsigned long end,
2248-
pte_fn_t fn, void *data, bool create)
2252+
pte_fn_t fn, void *data, bool create,
2253+
pgtbl_mod_mask *mask)
22492254
{
22502255
pmd_t *pmd;
22512256
unsigned long next;
@@ -2254,7 +2259,7 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
22542259
BUG_ON(pud_huge(*pud));
22552260

22562261
if (create) {
2257-
pmd = pmd_alloc(mm, pud, addr);
2262+
pmd = pmd_alloc_track(mm, pud, addr, mask);
22582263
if (!pmd)
22592264
return -ENOMEM;
22602265
} else {
@@ -2264,7 +2269,7 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
22642269
next = pmd_addr_end(addr, end);
22652270
if (create || !pmd_none_or_clear_bad(pmd)) {
22662271
err = apply_to_pte_range(mm, pmd, addr, next, fn, data,
2267-
create);
2272+
create, mask);
22682273
if (err)
22692274
break;
22702275
}
@@ -2274,14 +2279,15 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
22742279

22752280
static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d,
22762281
unsigned long addr, unsigned long end,
2277-
pte_fn_t fn, void *data, bool create)
2282+
pte_fn_t fn, void *data, bool create,
2283+
pgtbl_mod_mask *mask)
22782284
{
22792285
pud_t *pud;
22802286
unsigned long next;
22812287
int err = 0;
22822288

22832289
if (create) {
2284-
pud = pud_alloc(mm, p4d, addr);
2290+
pud = pud_alloc_track(mm, p4d, addr, mask);
22852291
if (!pud)
22862292
return -ENOMEM;
22872293
} else {
@@ -2291,7 +2297,7 @@ static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d,
22912297
next = pud_addr_end(addr, end);
22922298
if (create || !pud_none_or_clear_bad(pud)) {
22932299
err = apply_to_pmd_range(mm, pud, addr, next, fn, data,
2294-
create);
2300+
create, mask);
22952301
if (err)
22962302
break;
22972303
}
@@ -2301,14 +2307,15 @@ static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d,
23012307

23022308
static int apply_to_p4d_range(struct mm_struct *mm, pgd_t *pgd,
23032309
unsigned long addr, unsigned long end,
2304-
pte_fn_t fn, void *data, bool create)
2310+
pte_fn_t fn, void *data, bool create,
2311+
pgtbl_mod_mask *mask)
23052312
{
23062313
p4d_t *p4d;
23072314
unsigned long next;
23082315
int err = 0;
23092316

23102317
if (create) {
2311-
p4d = p4d_alloc(mm, pgd, addr);
2318+
p4d = p4d_alloc_track(mm, pgd, addr, mask);
23122319
if (!p4d)
23132320
return -ENOMEM;
23142321
} else {
@@ -2318,7 +2325,7 @@ static int apply_to_p4d_range(struct mm_struct *mm, pgd_t *pgd,
23182325
next = p4d_addr_end(addr, end);
23192326
if (create || !p4d_none_or_clear_bad(p4d)) {
23202327
err = apply_to_pud_range(mm, p4d, addr, next, fn, data,
2321-
create);
2328+
create, mask);
23222329
if (err)
23232330
break;
23242331
}
@@ -2331,8 +2338,9 @@ static int __apply_to_page_range(struct mm_struct *mm, unsigned long addr,
23312338
void *data, bool create)
23322339
{
23332340
pgd_t *pgd;
2334-
unsigned long next;
2341+
unsigned long start = addr, next;
23352342
unsigned long end = addr + size;
2343+
pgtbl_mod_mask mask = 0;
23362344
int err = 0;
23372345

23382346
if (WARN_ON(addr >= end))
@@ -2343,11 +2351,14 @@ static int __apply_to_page_range(struct mm_struct *mm, unsigned long addr,
23432351
next = pgd_addr_end(addr, end);
23442352
if (!create && pgd_none_or_clear_bad(pgd))
23452353
continue;
2346-
err = apply_to_p4d_range(mm, pgd, addr, next, fn, data, create);
2354+
err = apply_to_p4d_range(mm, pgd, addr, next, fn, data, create, &mask);
23472355
if (err)
23482356
break;
23492357
} while (pgd++, addr = next, addr != end);
23502358

2359+
if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
2360+
arch_sync_kernel_mappings(start, start + size);
2361+
23512362
return err;
23522363
}
23532364

0 commit comments

Comments
 (0)