Skip to content

Commit

Permalink
Merge branch 'siginfo-linus' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/ebiederm/user-namespace

Pull siginfo updates from Eric Biederman:
 "I have been slowly sorting out siginfo and this is the culmination of
  that work.

  The primary result is in several ways the signal infrastructure has
  been made less error prone. The code has been updated so that manually
  specifying SEND_SIG_FORCED is never necessary. The conversion to the
  new siginfo sending functions is now complete, which makes it
  difficult to send a signal without filling in the proper siginfo
  fields.

  At the tail end of the patchset comes the optimization of decreasing
  the size of struct siginfo in the kernel from 128 bytes to about 48
  bytes on 64bit. The fundamental observation that enables this is by
  definition none of the known ways to use struct siginfo uses the extra
  bytes.

  This comes at the cost of a small user space observable difference.
  For the rare case of siginfo being injected into the kernel only what
  can be copied into kernel_siginfo is delivered to the destination, the
  rest of the bytes are set to 0. For cases where the signal and the
  si_code are known this is safe, because we know those bytes are not
  used. For cases where the signal and si_code combination is unknown
  the bits that won't fit into struct kernel_siginfo are tested to
  verify they are zero, and the send fails if they are not.

  I made an extensive search through userspace code and I could not find
  anything that would break because of the above change. If it turns out
  I did break something it will take just the revert of a single change
  to restore kernel_siginfo to the same size as userspace siginfo.

  Testing did reveal dependencies on preferring the signo passed to
  sigqueueinfo over si->signo, so bit the bullet and added the
  complexity necessary to handle that case.

  Testing also revealed bad things can happen if a negative signal
  number is passed into the system calls. Something no sane application
  will do but something a malicious program or a fuzzer might do. So I
  have fixed the code that performs the bounds checks to ensure negative
  signal numbers are handled"

* 'siginfo-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: (80 commits)
  signal: Guard against negative signal numbers in copy_siginfo_from_user32
  signal: Guard against negative signal numbers in copy_siginfo_from_user
  signal: In sigqueueinfo prefer sig not si_signo
  signal: Use a smaller struct siginfo in the kernel
  signal: Distinguish between kernel_siginfo and siginfo
  signal: Introduce copy_siginfo_from_user and use it's return value
  signal: Remove the need for __ARCH_SI_PREABLE_SIZE and SI_PAD_SIZE
  signal: Fail sigqueueinfo if si_signo != sig
  signal/sparc: Move EMT_TAGOVF into the generic siginfo.h
  signal/unicore32: Use force_sig_fault where appropriate
  signal/unicore32: Generate siginfo in ucs32_notify_die
  signal/unicore32: Use send_sig_fault where appropriate
  signal/arc: Use force_sig_fault where appropriate
  signal/arc: Push siginfo generation into unhandled_exception
  signal/ia64: Use force_sig_fault where appropriate
  signal/ia64: Use the force_sig(SIGSEGV,...) in ia64_rt_sigreturn
  signal/ia64: Use the generic force_sigsegv in setup_frame
  signal/arm/kvm: Use send_sig_mceerr
  signal/arm: Use send_sig_fault where appropriate
  signal/arm: Use force_sig_fault where appropriate
  ...
  • Loading branch information
torvalds committed Oct 24, 2018
2 parents a978a5b + a367005 commit ba9f6f8
Show file tree
Hide file tree
Showing 98 changed files with 925 additions and 1,328 deletions.
1 change: 0 additions & 1 deletion arch/alpha/include/uapi/asm/siginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#ifndef _ALPHA_SIGINFO_H
#define _ALPHA_SIGINFO_H

#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
#define __ARCH_SI_TRAPNO

#include <asm-generic/siginfo.h>
Expand Down
22 changes: 8 additions & 14 deletions arch/arc/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,38 +42,32 @@ void die(const char *str, struct pt_regs *regs, unsigned long address)
* -for kernel, chk if due to copy_(to|from)_user, otherwise die()
*/
static noinline int
unhandled_exception(const char *str, struct pt_regs *regs, siginfo_t *info)
unhandled_exception(const char *str, struct pt_regs *regs,
int signo, int si_code, void __user *addr)
{
if (user_mode(regs)) {
struct task_struct *tsk = current;

tsk->thread.fault_address = (__force unsigned int)info->si_addr;
tsk->thread.fault_address = (__force unsigned int)addr;

force_sig_info(info->si_signo, info, tsk);
force_sig_fault(signo, si_code, addr, tsk);

} else {
/* If not due to copy_(to|from)_user, we are doomed */
if (fixup_exception(regs))
return 0;

die(str, regs, (unsigned long)info->si_addr);
die(str, regs, (unsigned long)addr);
}

return 1;
}

#define DO_ERROR_INFO(signr, str, name, sicode) \
int name(unsigned long address, struct pt_regs *regs) \
{ \
siginfo_t info; \
\
clear_siginfo(&info); \
info.si_signo = signr; \
info.si_errno = 0; \
info.si_code = sicode; \
info.si_addr = (void __user *)address; \
\
return unhandled_exception(str, regs, &info);\
{ \
return unhandled_exception(str, regs, signr, sicode, \
(void __user *)address); \
}

/*
Expand Down
20 changes: 5 additions & 15 deletions arch/arc/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,12 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
struct vm_area_struct *vma = NULL;
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
siginfo_t info;
int si_code;
int ret;
vm_fault_t fault;
int write = regs->ecr_cause & ECR_C_PROTV_STORE; /* ST/EX */
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;

clear_siginfo(&info);

/*
* We fault-in kernel-space virtual memory on-demand. The
* 'reference' page table is init_mm.pgd.
Expand All @@ -91,7 +89,7 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
return;
}

info.si_code = SEGV_MAPERR;
si_code = SEGV_MAPERR;

/*
* If we're in an interrupt or have no user
Expand Down Expand Up @@ -119,7 +117,7 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
* we can handle it..
*/
good_area:
info.si_code = SEGV_ACCERR;
si_code = SEGV_ACCERR;

/* Handle protection violation, execute on heap or stack */

Expand Down Expand Up @@ -199,11 +197,7 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
/* User mode accesses just cause a SIGSEGV */
if (user_mode(regs)) {
tsk->thread.fault_address = address;
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* info.si_code has been set above */
info.si_addr = (void __user *)address;
force_sig_info(SIGSEGV, &info, tsk);
force_sig_fault(SIGSEGV, si_code, (void __user *)address, tsk);
return;
}

Expand Down Expand Up @@ -238,9 +232,5 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
goto no_context;

tsk->thread.fault_address = address;
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void __user *)address;
force_sig_info(SIGBUS, &info, tsk);
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address, tsk);
}
4 changes: 2 additions & 2 deletions arch/arm/include/asm/bug.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ do { \
struct pt_regs;
void die(const char *msg, struct pt_regs *regs, int err);

struct siginfo;
void arm_notify_die(const char *str, struct pt_regs *regs, struct siginfo *info,
void arm_notify_die(const char *str, struct pt_regs *regs,
int signo, int si_code, void __user *addr,
unsigned long err, unsigned long trap);

#ifdef CONFIG_ARM_LPAE
Expand Down
11 changes: 2 additions & 9 deletions arch/arm/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,8 @@ void ptrace_disable(struct task_struct *child)
*/
void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
{
siginfo_t info;

clear_siginfo(&info);
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void __user *)instruction_pointer(regs);

force_sig_info(SIGTRAP, &info, tsk);
force_sig_fault(SIGTRAP, TRAP_BRKPT,
(void __user *)instruction_pointer(regs), tsk);
}

static int break_trap(struct pt_regs *regs, unsigned int instr)
Expand Down
16 changes: 7 additions & 9 deletions arch/arm/kernel/swp_emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,20 @@ static int proc_status_show(struct seq_file *m, void *v)
*/
static void set_segfault(struct pt_regs *regs, unsigned long addr)
{
siginfo_t info;
int si_code;

clear_siginfo(&info);
down_read(&current->mm->mmap_sem);
if (find_vma(current->mm, addr) == NULL)
info.si_code = SEGV_MAPERR;
si_code = SEGV_MAPERR;
else
info.si_code = SEGV_ACCERR;
si_code = SEGV_ACCERR;
up_read(&current->mm->mmap_sem);

info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_addr = (void *) instruction_pointer(regs);

pr_debug("SWP{B} emulation: access caused memory abort!\n");
arm_notify_die("Illegal memory access", regs, &info, 0, 0);
arm_notify_die("Illegal memory access", regs,
SIGSEGV, si_code,
(void __user *)instruction_pointer(regs),
0, 0);

abtcounter++;
}
Expand Down
63 changes: 17 additions & 46 deletions arch/arm/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,14 @@ void die(const char *str, struct pt_regs *regs, int err)
}

void arm_notify_die(const char *str, struct pt_regs *regs,
struct siginfo *info, unsigned long err, unsigned long trap)
int signo, int si_code, void __user *addr,
unsigned long err, unsigned long trap)
{
if (user_mode(regs)) {
current->thread.error_code = err;
current->thread.trap_no = trap;

force_sig_info(info->si_signo, info, current);
force_sig_fault(signo, si_code, addr, current);
} else {
die(str, regs, err);
}
Expand Down Expand Up @@ -438,10 +439,8 @@ int call_undef_hook(struct pt_regs *regs, unsigned int instr)
asmlinkage void do_undefinstr(struct pt_regs *regs)
{
unsigned int instr;
siginfo_t info;
void __user *pc;

clear_siginfo(&info);
pc = (void __user *)instruction_pointer(regs);

if (processor_mode(regs) == SVC_MODE) {
Expand Down Expand Up @@ -485,13 +484,8 @@ asmlinkage void do_undefinstr(struct pt_regs *regs)
dump_instr(KERN_INFO, regs);
}
#endif

info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = pc;

arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
arm_notify_die("Oops - undefined instruction", regs,
SIGILL, ILL_ILLOPC, pc, 0, 6);
}
NOKPROBE_SYMBOL(do_undefinstr)

Expand Down Expand Up @@ -539,9 +533,6 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason)

static int bad_syscall(int n, struct pt_regs *regs)
{
siginfo_t info;

clear_siginfo(&info);
if ((current->personality & PER_MASK) != PER_LINUX) {
send_sig(SIGSEGV, current, 1);
return regs->ARM_r0;
Expand All @@ -555,13 +546,10 @@ static int bad_syscall(int n, struct pt_regs *regs)
}
#endif

info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLTRP;
info.si_addr = (void __user *)instruction_pointer(regs) -
(thumb_mode(regs) ? 2 : 4);

arm_notify_die("Oops - bad syscall", regs, &info, n, 0);
arm_notify_die("Oops - bad syscall", regs, SIGILL, ILL_ILLTRP,
(void __user *)instruction_pointer(regs) -
(thumb_mode(regs) ? 2 : 4),
n, 0);

return regs->ARM_r0;
}
Expand Down Expand Up @@ -607,20 +595,13 @@ do_cache_op(unsigned long start, unsigned long end, int flags)
#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
asmlinkage int arm_syscall(int no, struct pt_regs *regs)
{
siginfo_t info;

clear_siginfo(&info);
if ((no >> 16) != (__ARM_NR_BASE>> 16))
return bad_syscall(no, regs);

switch (no & 0xffff) {
case 0: /* branch through 0 */
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = SEGV_MAPERR;
info.si_addr = NULL;

arm_notify_die("branch through zero", regs, &info, 0, 0);
arm_notify_die("branch through zero", regs,
SIGSEGV, SEGV_MAPERR, NULL, 0, 0);
return 0;

case NR(breakpoint): /* SWI BREAK_POINT */
Expand Down Expand Up @@ -688,13 +669,10 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
}
}
#endif
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLTRP;
info.si_addr = (void __user *)instruction_pointer(regs) -
(thumb_mode(regs) ? 2 : 4);

arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0);
arm_notify_die("Oops - bad syscall(2)", regs, SIGILL, ILL_ILLTRP,
(void __user *)instruction_pointer(regs) -
(thumb_mode(regs) ? 2 : 4),
no, 0);
return 0;
}

Expand Down Expand Up @@ -744,9 +722,6 @@ asmlinkage void
baddataabort(int code, unsigned long instr, struct pt_regs *regs)
{
unsigned long addr = instruction_pointer(regs);
siginfo_t info;

clear_siginfo(&info);

#ifdef CONFIG_DEBUG_USER
if (user_debug & UDBG_BADABORT) {
Expand All @@ -757,12 +732,8 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs)
}
#endif

info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = (void __user *)addr;

arm_notify_die("unknown data abort code", regs, &info, instr, 0);
arm_notify_die("unknown data abort code", regs,
SIGILL, ILL_ILLOPC, (void __user *)addr, instr, 0);
}

void __readwrite_bug(const char *fn)
Expand Down
10 changes: 1 addition & 9 deletions arch/arm/mm/alignment.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,15 +948,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
goto fixup;

if (ai_usermode & UM_SIGNAL) {
siginfo_t si;

clear_siginfo(&si);
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRALN;
si.si_addr = (void __user *)addr;

force_sig_info(si.si_signo, &si, current);
force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current);
} else {
/*
* We're about to disable the alignment trap and return to
Expand Down
28 changes: 5 additions & 23 deletions arch/arm/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,9 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
unsigned int fsr, unsigned int sig, int code,
struct pt_regs *regs)
{
struct siginfo si;

if (addr > TASK_SIZE)
harden_branch_predictor();

clear_siginfo(&si);

#ifdef CONFIG_DEBUG_USER
if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||
((user_debug & UDBG_BUS) && (sig == SIGBUS))) {
Expand All @@ -181,11 +177,7 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
tsk->thread.address = addr;
tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14;
si.si_signo = sig;
si.si_errno = 0;
si.si_code = code;
si.si_addr = (void __user *)addr;
force_sig_info(sig, &si, tsk);
force_sig_fault(sig, code, (void __user *)addr, tsk);
}

void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
Expand Down Expand Up @@ -554,7 +546,6 @@ asmlinkage void
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
struct siginfo info;

if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
return;
Expand All @@ -563,12 +554,8 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
inf->name, fsr, addr);
show_pte(current->mm, addr);

clear_siginfo(&info);
info.si_signo = inf->sig;
info.si_errno = 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm_notify_die("", regs, &info, fsr, 0);
arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
fsr, 0);
}

void __init
Expand All @@ -588,20 +575,15 @@ asmlinkage void
do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
{
const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
struct siginfo info;

if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
return;

pr_alert("Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
inf->name, ifsr, addr);

clear_siginfo(&info);
info.si_signo = inf->sig;
info.si_errno = 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm_notify_die("", regs, &info, ifsr, 0);
arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
ifsr, 0);
}

/*
Expand Down
Loading

0 comments on commit ba9f6f8

Please sign in to comment.