/* * Get the base of bounds tables pointed by specific bounds * directory entry. */ static int get_bt_addr(struct mm_struct *mm, long __user *bd_entry_ptr, unsigned long *bt_addr_result) { int ret; int valid_bit; unsigned long bd_entry; unsigned long bt_addr; if (!access_ok(VERIFY_READ, (bd_entry_ptr), sizeof(*bd_entry_ptr))) return -EFAULT; while (1) { int need_write = 0; pagefault_disable(); ret = get_user_bd_entry(mm, &bd_entry, bd_entry_ptr); pagefault_enable(); if (!ret) break; if (ret == -EFAULT) ret = mpx_resolve_fault(bd_entry_ptr, need_write); /* * If we could not resolve the fault, consider it * userspace's fault and error out. */ if (ret) return ret; } valid_bit = bd_entry & MPX_BD_ENTRY_VALID_FLAG; bt_addr = mpx_bd_entry_to_bt_addr(mm, bd_entry); /* * When the kernel is managing bounds tables, a bounds directory * entry will either have a valid address (plus the valid bit) * *OR* be completely empty. If we see a !valid entry *and* some * data in the address field, we know something is wrong. This * -EINVAL return will cause a SIGSEGV. */ if (!valid_bit && bt_addr) return -EINVAL; /* * Do we have an completely zeroed bt entry? That is OK. It * just means there was no bounds table for this memory. Make * sure to distinguish this from -EINVAL, which will cause * a SEGV. */ if (!valid_bit) return -ENOENT; *bt_addr_result = bt_addr; return 0; }
static int unmap_single_bt(struct mm_struct *mm, long __user *bd_entry, unsigned long bt_addr) { unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG; unsigned long actual_old_val = 0; int ret; while (1) { int need_write = 1; pagefault_disable(); ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry, expected_old_val, 0); pagefault_enable(); if (!ret) break; if (ret == -EFAULT) ret = mpx_resolve_fault(bd_entry, need_write); /* * If we could not resolve the fault, consider it * userspace's fault and error out. */ if (ret) return ret; } /* * The cmpxchg was performed, check the results. */ if (actual_old_val != expected_old_val) { /* * Someone else raced with us to unmap the table. * There was no bounds table pointed to by the * directory, so declare success. Somebody freed * it. */ if (!actual_old_val) return 0; /* * Something messed with the bounds directory * entry. We hold mmap_sem for read or write * here, so it could not be a _new_ bounds table * that someone just allocated. Something is * wrong, so pass up the error and SIGSEGV. */ return -EINVAL; } /* * Note, we are likely being called under do_munmap() already. To * avoid recursion, do_munmap() will check whether it comes * from one bounds table through VM_MPX flag. */ return do_munmap(mm, bt_addr, MPX_BT_SIZE_BYTES); }