/* * This is really a simplified "vm_mmap". it only handles MPX * bounds tables (the bounds directory is user-allocated). */ static unsigned long mpx_mmap(unsigned long len) { unsigned long ret; unsigned long addr, pgoff; struct mm_struct *mm = current->mm; vm_flags_t vm_flags; struct vm_area_struct *vma; /* Only bounds table can be allocated here */ if (len != mpx_bt_size_bytes(mm)) return -EINVAL; down_write(&mm->mmap_sem); /* Too many mappings? */ if (mm->map_count > sysctl_max_map_count) { ret = -ENOMEM; goto out; } /* Obtain the address to map to. we verify (or select) it and ensure * that it represents a valid section of the address space. */ addr = get_unmapped_area(NULL, 0, len, 0, MAP_ANONYMOUS | MAP_PRIVATE); if (addr & ~PAGE_MASK) { ret = addr; goto out; } vm_flags = VM_READ | VM_WRITE | VM_MPX | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; /* Set pgoff according to addr for anon_vma */ pgoff = addr >> PAGE_SHIFT; ret = mmap_region(NULL, addr, len, vm_flags, pgoff); if (IS_ERR_VALUE(ret)) goto out; vma = find_vma(mm, ret); if (!vma) { ret = -ENOMEM; goto out; } if (vm_flags & VM_LOCKED) { up_write(&mm->mmap_sem); mm_populate(ret, len); return ret; } out: up_write(&mm->mmap_sem); return ret; }
/* * This is really a simplified "vm_mmap". it only handles MPX * bounds tables (the bounds directory is user-allocated). */ static unsigned long mpx_mmap(unsigned long len) { struct mm_struct *mm = current->mm; unsigned long addr, populate; /* Only bounds table can be allocated here */ if (len != mpx_bt_size_bytes(mm)) return -EINVAL; down_write(&mm->mmap_sem); addr = do_mmap(NULL, 0, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, VM_MPX, 0, &populate, NULL); up_write(&mm->mmap_sem); if (populate) mm_populate(addr, populate); return addr; }
/* * With 32-bit mode, a bounds directory is 4MB, and the size of each * bounds table is 16KB. With 64-bit mode, a bounds directory is 2GB, * and the size of each bounds table is 4MB. */ static int allocate_bt(struct mm_struct *mm, long __user *bd_entry) { unsigned long expected_old_val = 0; unsigned long actual_old_val = 0; unsigned long bt_addr; unsigned long bd_new_entry; int ret = 0; /* * Carve the virtual space out of userspace for the new * bounds table: */ bt_addr = mpx_mmap(mpx_bt_size_bytes(mm)); if (IS_ERR((void *)bt_addr)) return PTR_ERR((void *)bt_addr); /* * Set the valid flag (kinda like _PAGE_PRESENT in a pte) */ bd_new_entry = bt_addr | MPX_BD_ENTRY_VALID_FLAG; /* * Go poke the address of the new bounds table in to the * bounds directory entry out in userspace memory. Note: * we may race with another CPU instantiating the same table. * In that case the cmpxchg will see an unexpected * 'actual_old_val'. * * This can fault, but that's OK because we do not hold * mmap_sem at this point, unlike some of the other part * of the MPX code that have to pagefault_disable(). */ ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, bd_entry, expected_old_val, bd_new_entry); if (ret) goto out_unmap; /* * The user_atomic_cmpxchg_inatomic() will only return nonzero * for faults, *not* if the cmpxchg itself fails. Now we must * verify that the cmpxchg itself completed successfully. */ /* * We expected an empty 'expected_old_val', but instead found * an apparently valid entry. Assume we raced with another * thread to instantiate this table and desclare succecss. */ if (actual_old_val & MPX_BD_ENTRY_VALID_FLAG) { ret = 0; goto out_unmap; } /* * We found a non-empty bd_entry but it did not have the * VALID_FLAG set. Return an error which will result in * a SEGV since this probably means that somebody scribbled * some invalid data in to a bounds table. */ if (expected_old_val != actual_old_val) { ret = -EINVAL; goto out_unmap; } trace_mpx_new_bounds_table(bt_addr); return 0; out_unmap: vm_munmap(bt_addr, mpx_bt_size_bytes(mm)); return ret; }