/* READ_ONCE synchronizes with smp_store_release */
ldt = READ_ONCE(mm->context.ldt);
/* * Any change to mm->context.ldt is followed by an IPI to all * CPUs with the mm active. The LDT will not be freed until * after the IPI is handled by all such CPUs. This means that * if the ldt_struct changes before we return, the values we see * will be safe, and the new values will be loaded before we run * any user code. * * NB: don't try to convert this to use RCU without extreme care. * We would still need IRQs off, because we don't want to change * the local LDT after an IPI loaded a newer value than the one * that we can see.
*/
if (unlikely(ldt)) { if (static_cpu_has(X86_FEATURE_PTI)) { if (WARN_ON_ONCE((unsignedlong)ldt->slot > 1)) { /* * Whoops -- either the new LDT isn't mapped * (if slot == -1) or is mapped into a bogus * slot (if slot > 1).
*/
clear_LDT(); return;
}
/* * If page table isolation is enabled, ldt->entries * will not be mapped in the userspace pagetables. * Tell the CPU to access the LDT through the alias * at ldt_slot_va(ldt->slot).
*/
set_ldt(ldt_slot_va(ldt->slot), ldt->nr_entries);
} else {
set_ldt(ldt->entries, ldt->nr_entries);
}
} else {
clear_LDT();
}
}
void switch_ldt(struct mm_struct *prev, struct mm_struct *next)
{ /* * Load the LDT if either the old or new mm had an LDT. * * An mm will never go from having an LDT to not having an LDT. Two * mms never share an LDT, so we don't gain anything by checking to * see whether the LDT changed. There's also no guarantee that * prev->context.ldt actually matches LDTR, but, if LDTR is non-NULL, * then prev->context.ldt will also be non-NULL. * * If we really cared, we could optimize the case where prev == next * and we're exiting lazy mode. Most of the time, if this happens, * we don't actually need to reload LDTR, but modify_ldt() is mostly * used by legacy code and emulators where we don't need this level of * performance. * * This uses | instead of || because it generates better code.
*/ if (unlikely((unsignedlong)prev->context.ldt |
(unsignedlong)next->context.ldt))
load_mm_ldt(next);
/* * Make sure that the cached DS and ES descriptors match the updated * LDT.
*/
savesegment(ds, sel); if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT)
loadsegment(ds, sel);
/* * Xen is very picky: it requires a page-aligned LDT that has no * trailing nonzero bytes in any page that contains LDT descriptors. * Keep it simple: zero the whole allocation and never allocate less * than PAGE_SIZE.
*/ if (alloc_size > PAGE_SIZE)
new_ldt->entries = __vmalloc(alloc_size, GFP_KERNEL_ACCOUNT | __GFP_ZERO); else
new_ldt->entries = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
if (!new_ldt->entries) {
kfree(new_ldt); return NULL;
}
/* The new LDT isn't aliased for PTI yet. */
new_ldt->slot = -1;
staticvoid do_sanity_check(struct mm_struct *mm, bool had_kernel_mapping, bool had_user_mapping)
{ if (mm->context.ldt) { /* * We already had an LDT. The top-level entry should already * have been allocated and synchronized with the usermode * tables.
*/
WARN_ON(!had_kernel_mapping); if (boot_cpu_has(X86_FEATURE_PTI))
WARN_ON(!had_user_mapping);
} else { /* * This is the first time we're mapping an LDT for this process. * Sync the pgd to the usermode tables.
*/
WARN_ON(had_kernel_mapping); if (boot_cpu_has(X86_FEATURE_PTI))
WARN_ON(had_user_mapping);
}
}
/* * If PTI is enabled, this maps the LDT into the kernelmode and * usermode tables for the given mm.
*/ staticint
map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot)
{ unsignedlong va; bool is_vmalloc;
spinlock_t *ptl; int i, nr_pages;
if (!boot_cpu_has(X86_FEATURE_PTI)) return 0;
/* * Any given ldt_struct should have map_ldt_struct() called at most * once.
*/
WARN_ON(ldt->slot != -1);
/* Check if the current mappings are sane */
sanity_check_ldt_mapping(mm);
for (i = 0; i < nr_pages; i++) { unsignedlong offset = i << PAGE_SHIFT; constvoid *src = (char *)ldt->entries + offset; unsignedlong pfn;
pgprot_t pte_prot;
pte_t pte, *ptep;
va = (unsignedlong)ldt_slot_va(slot) + offset;
pfn = is_vmalloc ? vmalloc_to_pfn(src) :
page_to_pfn(virt_to_page(src)); /* * Treat the PTI LDT range as a *userspace* range. * get_locked_pte() will allocate all needed pagetables * and account for them in this mm.
*/
ptep = get_locked_pte(mm, va, &ptl); if (!ptep) return -ENOMEM; /* * Map it RO so the easy to find address is not a primary * target via some kernel interface which misses a * permission check.
*/
pte_prot = __pgprot(__PAGE_KERNEL_RO & ~_PAGE_GLOBAL); /* Filter out unsuppored __PAGE_KERNEL* bits: */
pgprot_val(pte_prot) &= __supported_pte_mask;
pte = pfn_pte(pfn, pte_prot);
set_pte_at(mm, va, ptep, pte);
pte_unmap_unlock(ptep, ptl);
}
/* Propagate LDT mapping to the user page-table */
map_ldt_struct_to_user(mm);
ldt->slot = slot; return 0;
}
staticvoid unmap_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt)
{ unsignedlong va; int i, nr_pages;
if (!ldt) return;
/* LDT map/unmap is only required for PTI */ if (!boot_cpu_has(X86_FEATURE_PTI)) return;
for (i = 0; i < nr_pages; i++) { unsignedlong offset = i << PAGE_SHIFT;
spinlock_t *ptl;
pte_t *ptep;
va = (unsignedlong)ldt_slot_va(ldt->slot) + offset;
ptep = get_locked_pte(mm, va, &ptl); if (!WARN_ON_ONCE(!ptep)) {
pte_clear(mm, va, ptep);
pte_unmap_unlock(ptep, ptl);
}
}
va = (unsignedlong)ldt_slot_va(ldt->slot);
flush_tlb_mm_range(mm, va, va + nr_pages * PAGE_SIZE, PAGE_SHIFT, false);
}
/* * Although free_pgd_range() is intended for freeing user * page-tables, it also works out for kernel mappings on x86. * We use tlb_gather_mmu_fullmm() to avoid confusing the * range-tracking logic in __tlb_adjust_range().
*/
tlb_gather_mmu_fullmm(&tlb, mm);
free_pgd_range(&tlb, start, end, start, end);
tlb_finish_mmu(&tlb); #endif
}
/* After calling this, the LDT is immutable. */ staticvoid finalize_ldt_struct(struct ldt_struct *ldt)
{
paravirt_alloc_ldt(ldt->entries, ldt->nr_entries);
}
/* * Called on fork from arch_dup_mmap(). Just copy the current LDT state, * the new task is not running, so nothing can be installed.
*/ int ldt_dup_context(struct mm_struct *old_mm, struct mm_struct *mm)
{ struct ldt_struct *new_ldt; int retval = 0;
if (!old_mm) return 0;
mutex_lock(&old_mm->context.lock); if (!old_mm->context.ldt) goto out_unlock;
/* * No need to lock the MM as we are the last user * * 64bit: Don't touch the LDT register - we're already in the next thread.
*/ void destroy_context_ldt(struct mm_struct *mm)
{
free_ldt_struct(mm->context.ldt);
mm->context.ldt = NULL;
}
staticint read_default_ldt(void __user *ptr, unsignedlong bytecount)
{ /* CHECKME: Can we use _one_ random number ? */ #ifdef CONFIG_X86_32 unsignedlong size = 5 * sizeof(struct desc_struct); #else unsignedlong size = 128; #endif if (bytecount > size)
bytecount = size; if (clear_user(ptr, bytecount)) return -EFAULT; return bytecount;
}
staticbool allow_16bit_segments(void)
{ if (!IS_ENABLED(CONFIG_X86_16BIT)) returnfalse;
#ifdef CONFIG_XEN_PV /* * Xen PV does not implement ESPFIX64, which means that 16-bit * segments will not work correctly. Until either Xen PV implements * ESPFIX64 and can signal this fact to the guest or unless someone * provides compelling evidence that allowing broken 16-bit segments * is worthwhile, disallow 16-bit segments under Xen PV.
*/ if (xen_pv_domain()) {
pr_info_once("Warning: 16-bit segments do not work correctly in a Xen PV guest\n"); returnfalse;
} #endif
/* * If we are using PTI, map the new LDT into the userspace pagetables. * If there is already an LDT, use the other slot so that other CPUs * will continue to use the old LDT until install_ldt() switches * them over to the new LDT.
*/
error = map_ldt_struct(mm, new_ldt, old_ldt ? !old_ldt->slot : 0); if (error) { /* * This only can fail for the first LDT setup. If an LDT is * already installed then the PTE page is already * populated. Mop up a half populated page table.
*/ if (!WARN_ON_ONCE(old_ldt))
free_ldt_pgtables(mm);
free_ldt_struct(new_ldt); goto out_unlock;
}
SYSCALL_DEFINE3(modify_ldt, int , func , void __user * , ptr , unsignedlong , bytecount)
{ int ret = -ENOSYS;
switch (func) { case 0:
ret = read_ldt(ptr, bytecount); break; case 1:
ret = write_ldt(ptr, bytecount, 1); break; case 2:
ret = read_default_ldt(ptr, bytecount); break; case 0x11:
ret = write_ldt(ptr, bytecount, 0); break;
} /* * The SYSCALL_DEFINE() macros give us an 'unsigned long' * return type, but the ABI for sys_modify_ldt() expects * 'int'. This cast gives us an int-sized value in %rax * for the return code. The 'unsigned' is necessary so * the compiler does not try to sign-extend the negative * return codes into the high half of the register when * taking the value from int->long.
*/ return (unsignedint)ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.