• Ingo Molnar's avatar
    x86/pti/32: Calculate the various PTI cpu_entry_area sizes correctly, make the... · 05b042a1
    Ingo Molnar authored
    x86/pti/32: Calculate the various PTI cpu_entry_area sizes correctly, make the CPU_ENTRY_AREA_PAGES assert precise
    
    When two recent commits that increased the size of the 'struct cpu_entry_area'
    were merged in -tip, the 32-bit defconfig build started failing on the following
    build time assert:
    
      ./include/linux/compiler.h:391:38: error: call to ‘__compiletime_assert_189’ declared with attribute error: BUILD_BUG_ON failed: CPU_ENTRY_AREA_PAGES * PAGE_SIZE < CPU_ENTRY_AREA_MAP_SIZE
      arch/x86/mm/cpu_entry_area.c:189:2: note: in expansion of macro ‘BUILD_BUG_ON’
      In function ‘setup_cpu_entry_area_ptes’,
    
    Which corresponds to the following build time assert:
    
    	BUILD_BUG_ON(CPU_ENTRY_AREA_PAGES * PAGE_SIZE < CPU_ENTRY_AREA_MAP_SIZE);
    
    The purpose of this assert is to sanity check the fixed-value definition of
    CPU_ENTRY_AREA_PAGES arch/x86/include/asm/pgtable_32_types.h:
    
    	#define CPU_ENTRY_AREA_PAGES    (NR_CPUS * 41)
    
    The '41' is supposed to match sizeof(struct cpu_entry_area)/PAGE_SIZE, which value
    we didn't want to define in such a low level header, because it would cause
    dependency hell.
    
    Every time the size of cpu_entry_area is changed, we have to adjust CPU_ENTRY_AREA_PAGES
    accordingly - and this assert is checking that constraint.
    
    But the assert is both imprecise and buggy, primarily because it doesn't
    include the single readonly IDT page that is mapped at CPU_ENTRY_AREA_BASE
    (which begins at a PMD boundary).
    
    This bug was hidden by the fact that by accident CPU_ENTRY_AREA_PAGES is defined
    too large upstream (v5.4-rc8):
    
    	#define CPU_ENTRY_AREA_PAGES    (NR_CPUS * 40)
    
    While 'struct cpu_entry_area' is 155648 bytes, or 38 pages. So we had two extra
    pages, which hid the bug.
    
    The following commit (not yet upstream) increased the size to 40 pages:
    
      x86/iopl: ("Restrict iopl() permission scope")
    
    ... but increased CPU_ENTRY_AREA_PAGES only 41 - i.e. shortening the gap
    to just 1 extra page.
    
    Then another not-yet-upstream commit changed the size again:
    
      880a98c3: ("x86/cpu_entry_area: Add guard page for entry stack on 32bit")
    
    Which increased the cpu_entry_area size from 38 to 39 pages, but
    didn't change CPU_ENTRY_AREA_PAGES (kept it at 40). This worked
    fine, because we still had a page left from the accidental 'reserve'.
    
    But when these two commits were merged into the same tree, the
    combined size of cpu_entry_area grew from 38 to 40 pages, while
    CPU_ENTRY_AREA_PAGES finally caught up to 40 as well.
    
    Which is fine in terms of functionality, but the assert broke:
    
    	BUILD_BUG_ON(CPU_ENTRY_AREA_PAGES * PAGE_SIZE < CPU_ENTRY_AREA_MAP_SIZE);
    
    because CPU_ENTRY_AREA_MAP_SIZE is the total size of the area,
    which is 1 page larger due to the IDT page.
    
    To fix all this, change the assert to two precise asserts:
    
    	BUILD_BUG_ON((CPU_ENTRY_AREA_PAGES+1)*PAGE_SIZE != CPU_ENTRY_AREA_MAP_SIZE);
    	BUILD_BUG_ON(CPU_ENTRY_AREA_TOTAL_SIZE != CPU_ENTRY_AREA_MAP_SIZE);
    
    This takes the IDT page into account, and also connects the size-based
    define of CPU_ENTRY_AREA_TOTAL_SIZE with the address-subtraction based
    define of CPU_ENTRY_AREA_MAP_SIZE.
    
    Also clean up some of the names which made it rather confusing:
    
     - 'CPU_ENTRY_AREA_TOT_SIZE' wasn't actually the 'total' size of
       the cpu-entry-area, but the per-cpu array size, so rename this
       to CPU_ENTRY_AREA_ARRAY_SIZE.
    
     - Introduce CPU_ENTRY_AREA_TOTAL_SIZE that _is_ the total mapping
       size, with the IDT included.
    
     - Add comments where '+1' denotes the IDT mapping - it wasn't
       obvious and took me about 3 hours to decode...
    
    Finally, because this particular commit is actually applied after
    this patch:
    
      880a98c3: ("x86/cpu_entry_area: Add guard page for entry stack on 32bit")
    
    Fix the CPU_ENTRY_AREA_PAGES value from 40 pages to the correct 39 pages.
    
    All future commits that change cpu_entry_area will have to adjust
    this value precisely.
    
    As a side note, we should probably attempt to remove CPU_ENTRY_AREA_PAGES
    and derive its value directly from the structure, without causing
    header hell - but that is an adventure for another day! :-)
    
    Fixes: 880a98c3: ("x86/cpu_entry_area: Add guard page for entry stack on 32bit")
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Andy Lutomirski <luto@kernel.org>
    Cc: stable@kernel.org
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    05b042a1
cpu_entry_area.h 4.03 KB