Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 277447
b: refs/heads/master
c: 4fc3490
h: refs/heads/master
i:
  277445: a231ffd
  277443: d94dfe0
  277439: 273101f
v: v3
  • Loading branch information
Andy Lutomirski authored and Ingo Molnar committed Dec 5, 2011
1 parent 2f63d52 commit b655c87
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 18 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 01acc269083015e2f78407f59dc8d6378fce22ee
refs/heads/master: 4fc3490114bb159bd4fff1b3c96f4320fe6fb08f
3 changes: 2 additions & 1 deletion trunk/arch/x86/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ struct thread_info {
*/
__u8 supervisor_stack[0];
#endif
int uaccess_err;
int sig_on_uaccess_error:1;
int uaccess_err:1; /* uaccess failed */
};

#define INIT_THREAD_INFO(tsk) \
Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/x86/include/asm/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ struct __large_struct { unsigned long buf[100]; };
barrier();

#define uaccess_catch(err) \
(err) |= current_thread_info()->uaccess_err; \
(err) |= (current_thread_info()->uaccess_err ? -EFAULT : 0); \
current_thread_info()->uaccess_err = prev_err; \
} while (0)

Expand Down
75 changes: 67 additions & 8 deletions trunk/arch/x86/kernel/vsyscall_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,40 @@ static int addr_to_vsyscall_nr(unsigned long addr)
return nr;
}

static bool write_ok_or_segv(unsigned long ptr, size_t size)
{
/*
* XXX: if access_ok, get_user, and put_user handled
* sig_on_uaccess_error, this could go away.
*/

if (!access_ok(VERIFY_WRITE, (void __user *)ptr, size)) {
siginfo_t info;
struct thread_struct *thread = &current->thread;

thread->error_code = 6; /* user fault, no page, write */
thread->cr2 = ptr;
thread->trap_no = 14;

memset(&info, 0, sizeof(info));
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = SEGV_MAPERR;
info.si_addr = (void __user *)ptr;

force_sig_info(SIGSEGV, &info, current);
return false;
} else {
return true;
}
}

bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
{
struct task_struct *tsk;
unsigned long caller;
int vsyscall_nr;
int prev_sig_on_uaccess_error;
long ret;

/*
Expand Down Expand Up @@ -180,35 +209,65 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
if (seccomp_mode(&tsk->seccomp))
do_exit(SIGKILL);

/*
* With a real vsyscall, page faults cause SIGSEGV. We want to
* preserve that behavior to make writing exploits harder.
*/
prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error;
current_thread_info()->sig_on_uaccess_error = 1;

/*
* 0 is a valid user pointer (in the access_ok sense) on 32-bit and
* 64-bit, so we don't need to special-case it here. For all the
* vsyscalls, 0 means "don't write anything" not "write it at
* address 0".
*/
ret = -EFAULT;
switch (vsyscall_nr) {
case 0:
if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) ||
!write_ok_or_segv(regs->si, sizeof(struct timezone)))
break;

ret = sys_gettimeofday(
(struct timeval __user *)regs->di,
(struct timezone __user *)regs->si);
break;

case 1:
if (!write_ok_or_segv(regs->di, sizeof(time_t)))
break;

ret = sys_time((time_t __user *)regs->di);
break;

case 2:
if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
!write_ok_or_segv(regs->si, sizeof(unsigned)))
break;

ret = sys_getcpu((unsigned __user *)regs->di,
(unsigned __user *)regs->si,
0);
break;
}

current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error;

if (ret == -EFAULT) {
/*
* Bad news -- userspace fed a bad pointer to a vsyscall.
*
* With a real vsyscall, that would have caused SIGSEGV.
* To make writing reliable exploits using the emulated
* vsyscalls harder, generate SIGSEGV here as well.
*/
/* Bad news -- userspace fed a bad pointer to a vsyscall. */
warn_bad_vsyscall(KERN_INFO, regs,
"vsyscall fault (exploit attempt?)");
goto sigsegv;

/*
* If we failed to generate a signal for any reason,
* generate one here. (This should be impossible.)
*/
if (WARN_ON_ONCE(!sigismember(&tsk->pending.signal, SIGBUS) &&
!sigismember(&tsk->pending.signal, SIGSEGV)))
goto sigsegv;

return true; /* Don't emulate the ret. */
}

regs->ax = ret;
Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/x86/mm/extable.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ int fixup_exception(struct pt_regs *regs)
if (fixup) {
/* If fixup is less than 16, it means uaccess error */
if (fixup->fixup < 16) {
current_thread_info()->uaccess_err = -EFAULT;
current_thread_info()->uaccess_err = 1;
regs->ip += fixup->fixup;
return 1;
}
Expand Down
22 changes: 16 additions & 6 deletions trunk/arch/x86/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -626,16 +626,25 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code,

static noinline void
no_context(struct pt_regs *regs, unsigned long error_code,
unsigned long address)
unsigned long address, int signal, int si_code)
{
struct task_struct *tsk = current;
unsigned long *stackend;
unsigned long flags;
int sig;

/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs))
if (fixup_exception(regs)) {
if (current_thread_info()->sig_on_uaccess_error && signal) {
tsk->thread.trap_no = 14;
tsk->thread.error_code = error_code | PF_USER;
tsk->thread.cr2 = address;

/* XXX: hwpoison faults will set the wrong code. */
force_sig_info_fault(signal, si_code, address, tsk, 0);
}
return;
}

/*
* 32-bit:
Expand Down Expand Up @@ -755,7 +764,7 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
if (is_f00f_bug(regs, address))
return;

no_context(regs, error_code, address);
no_context(regs, error_code, address, SIGSEGV, si_code);
}

static noinline void
Expand Down Expand Up @@ -819,7 +828,7 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,

/* Kernel mode? Handle exceptions or die: */
if (!(error_code & PF_USER)) {
no_context(regs, error_code, address);
no_context(regs, error_code, address, SIGBUS, BUS_ADRERR);
return;
}

Expand Down Expand Up @@ -854,7 +863,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
if (!(fault & VM_FAULT_RETRY))
up_read(&current->mm->mmap_sem);
if (!(error_code & PF_USER))
no_context(regs, error_code, address);
no_context(regs, error_code, address, 0, 0);
return 1;
}
if (!(fault & VM_FAULT_ERROR))
Expand All @@ -864,7 +873,8 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
/* Kernel mode? Handle exceptions or die: */
if (!(error_code & PF_USER)) {
up_read(&current->mm->mmap_sem);
no_context(regs, error_code, address);
no_context(regs, error_code, address,
SIGSEGV, SEGV_MAPERR);
return 1;
}

Expand Down

0 comments on commit b655c87

Please sign in to comment.