/* * Create a restore token on the shadow stack. A token is always 8-byte * and aligned to 8.
*/ staticint create_rstor_token(unsignedlong ssp, unsignedlong *token_addr)
{ unsignedlong addr;
/* Token must be aligned */ if (!IS_ALIGNED(ssp, 8)) return -EINVAL;
addr = ssp - SS_FRAME_SIZE;
/* * SSP is aligned, so reserved bits and mode bit are a zero, just mark * the token 64-bit.
*/
ssp |= BIT(0);
if (write_user_shstk_64((u64 __user *)addr, (u64)ssp)) return -EFAULT;
if (token_addr)
*token_addr = addr;
return 0;
}
/* * VM_SHADOW_STACK will have a guard page. This helps userspace protect * itself from attacks. The reasoning is as follows: * * The shadow stack pointer(SSP) is moved by CALL, RET, and INCSSPQ. The * INCSSP instruction can increment the shadow stack pointer. It is the * shadow stack analog of an instruction like: * * addq $0x80, %rsp * * However, there is one important difference between an ADD on %rsp * and INCSSP. In addition to modifying SSP, INCSSP also reads from the * memory of the first and last elements that were "popped". It can be * thought of as acting like this: * * READ_ONCE(ssp); // read+discard top element on stack * ssp += nr_to_pop * 8; // move the shadow stack * READ_ONCE(ssp-8); // read+discard last popped stack element * * The maximum distance INCSSP can move the SSP is 2040 bytes, before * it would read the memory. Therefore a single page gap will be enough * to prevent any operation from shifting the SSP to an adjacent stack, * since it would have to land in the gap at least once, causing a * fault.
*/ staticunsignedlong alloc_shstk(unsignedlong addr, unsignedlong size, unsignedlong token_offset, bool set_res_tok)
{ int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_ABOVE4G; struct mm_struct *mm = current->mm; unsignedlong mapped_addr, unused;
staticvoid unmap_shadow_stack(u64 base, u64 size)
{ int r;
r = vm_munmap(base, size);
/* * mmap_write_lock_killable() failed with -EINTR. This means * the process is about to die and have it's MM cleaned up. * This task shouldn't ever make it back to userspace. In this * case it is ok to leak a shadow stack, so just exit out.
*/ if (r == -EINTR) return;
/* * For all other types of vm_munmap() failure, either the * system is out of memory or there is bug.
*/
WARN_ON_ONCE(r);
}
/* * If shadow stack is not enabled on the new thread, skip any * switch to a new shadow stack.
*/ if (!features_enabled(ARCH_SHSTK_SHSTK)) return 0;
/* * For CLONE_VFORK the child will share the parents shadow stack. * Make sure to clear the internal tracking of the thread shadow * stack so the freeing logic run for child knows to leave it alone.
*/ if (clone_flags & CLONE_VFORK) {
shstk->base = 0;
shstk->size = 0; return 0;
}
/* * For !CLONE_VM the child will use a copy of the parents shadow * stack.
*/ if (!(clone_flags & CLONE_VM)) return 0;
/* * Mark the high bit so that the sigframe can't be processed as a * return address.
*/ if (write_user_shstk_64(addr, data | SHSTK_DATA_BIT)) return -EFAULT; return 0;
}
/* * It is possible for the SSP to be off the end of a shadow stack by 4 * or 8 bytes. If the shadow stack is at the start of a page or 4 bytes * before it, it might be this case, so check that the address being * read is actually shadow stack.
*/ if (!IS_ALIGNED(*ssp, 8)) return -EINVAL;
need_to_check_vma = PAGE_ALIGN(*ssp) == *ssp;
if (need_to_check_vma)
mmap_read_lock_killable(current->mm);
err = get_shstk_data(&token_addr, (unsignedlong __user *)*ssp); if (unlikely(err)) goto out_err;
if (need_to_check_vma) {
vma = find_vma(current->mm, *ssp); if (!vma || !(vma->vm_flags & VM_SHADOW_STACK)) {
err = -EFAULT; goto out_err;
}
mmap_read_unlock(current->mm);
}
/* Restore SSP aligned? */ if (unlikely(!IS_ALIGNED(token_addr, 8))) return -EINVAL;
/* SSP in userspace? */ if (unlikely(token_addr >= TASK_SIZE_MAX)) return -EINVAL;
*ssp = token_addr;
return 0;
out_err: if (need_to_check_vma)
mmap_read_unlock(current->mm); return err;
}
int setup_signal_shadow_stack(struct ksignal *ksig)
{ void __user *restorer = ksig->ka.sa.sa_restorer; unsignedlong ssp; int err;
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
!features_enabled(ARCH_SHSTK_SHSTK)) return 0;
if (!restorer) return -EINVAL;
ssp = get_user_shstk_addr(); if (unlikely(!ssp)) return -EINVAL;
err = shstk_push_sigframe(&ssp); if (unlikely(err)) return err;
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
!features_enabled(ARCH_SHSTK_SHSTK)) return;
/* * When fork() with CLONE_VM fails, the child (tsk) already has a * shadow stack allocated, and exit_thread() calls this function to * free it. In this case the parent (current) and the child share * the same mm struct.
*/ if (!tsk->mm || tsk->mm != current->mm) return;
/* * If shstk->base is NULL, then this task is not managing its * own shadow stack (CLONE_VFORK). So skip freeing it.
*/ if (!shstk->base) return;
/* * shstk->base is NULL for CLONE_VFORK child tasks, and so is * normal. But size = 0 on a shstk->base is not normal and * indicated an attempt to free the thread shadow stack twice. * Warn about it.
*/ if (WARN_ON(!shstk->size)) return;
unmap_shadow_stack(shstk->base, shstk->size);
shstk->size = 0;
}
staticint wrss_control(bool enable)
{
u64 msrval;
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK)) return -EOPNOTSUPP;
/* * Only enable WRSS if shadow stack is enabled. If shadow stack is not * enabled, WRSS will already be disabled, so don't bother clearing it * when disabling.
*/ if (!features_enabled(ARCH_SHSTK_SHSTK)) return -EPERM;
/* Already enabled/disabled? */ if (features_enabled(ARCH_SHSTK_WRSS) == enable) return 0;
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK)) return -EOPNOTSUPP;
if (flags & ~SHADOW_STACK_SET_TOKEN) return -EINVAL;
/* If there isn't space for a token */ if (set_tok && size < 8) return -ENOSPC;
if (addr && addr < SZ_4G) return -ERANGE;
/* * An overflow would result in attempting to write the restore token * to the wrong location. Not catastrophic, but just return the right * error code and block it.
*/
aligned_size = PAGE_ALIGN(size); if (aligned_size < size) return -EOVERFLOW;
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.