[PATCH v9 6/6] arm64: expose FAR_EL1 tag bits in siginfo

Peter Collingbourne pcc at google.com
Wed Aug 19 21:49:01 EDT 2020


On Wed, Aug 19, 2020 at 8:56 AM Dave Martin <Dave.Martin at arm.com> wrote:
>
> On Mon, Aug 17, 2020 at 08:33:51PM -0700, Peter Collingbourne wrote:
> > The kernel currently clears the tag bits (i.e. bits 56-63) in the fault
> > address exposed via siginfo.si_addr and sigcontext.fault_address. However,
> > the tag bits may be needed by tools in order to accurately diagnose
> > memory errors, such as HWASan [1] or future tools based on the Memory
> > Tagging Extension (MTE).
> >
> > We should not stop clearing these bits in the existing fault address
> > fields, because there may be existing userspace applications that are
> > expecting the tag bits to be cleared. Instead, create a new pair of
> > union fields in siginfo._sigfault, and store the tag bits of FAR_EL1
> > there, together with a mask specifying which bits are valid.
> >
> > A flag is added to si_xflags to allow userspace to determine whether
> > the values in the fields are valid.
> >
> > [1] http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
> >
> > Signed-off-by: Peter Collingbourne <pcc at google.com>
> > ---
> > View this change in Gerrit: https://linux-review.googlesource.com/q/Ia8876bad8c798e0a32df7c2ce1256c4771c81446
> >
> > v9:
> > - make the ignored bits fields generic
> > - add some new dependent patches that prepare us to store the
> >   field in such a way that userspace can detect their presence
> >
> > v8:
> > - rebase onto 5.8rc2
> >
> > v7:
> > - switch to a new siginfo field instead of using sigcontext
> > - merge the patch back into one since the other patches are now
> >   unnecessary
> >
> > v6:
> > - move fault address and fault code into the kernel_siginfo data structure
> > - split the patch in three since it was getting large and now has
> >   generic and arch-specific parts
> >
> > v5:
> > - add padding to fault_addr_top_byte_context in order to ensure the correct
> >   size and preserve sp alignment
> >
> > v4:
> > - expose only the tag bits in the context instead of the entire FAR_EL1
> > - remove mention of the new context from the sigcontext.__reserved[] note
> >
> > v3:
> > - add documentation to tagged-pointers.rst
> > - update comments in sigcontext.h
> >
> > v2:
> > - revert changes to hw_breakpoint.c
> > - rename set_thread_esr to set_thread_far_esr
> >
> >  Documentation/arm64/tagged-pointers.rst | 21 +++++++---
> >  arch/arm64/include/asm/exception.h      |  2 +-
> >  arch/arm64/include/asm/traps.h          |  7 +++-
> >  arch/arm64/kernel/debug-monitors.c      |  4 +-
> >  arch/arm64/kernel/entry-common.c        |  2 -
> >  arch/arm64/kernel/ptrace.c              |  2 +-
> >  arch/arm64/kernel/traps.c               | 15 ++++---
> >  arch/arm64/mm/fault.c                   | 54 ++++++++++++++-----------
> >  arch/mips/kernel/traps.c                |  2 +-
> >  arch/parisc/kernel/ptrace.c             |  2 +-
> >  arch/parisc/mm/fault.c                  |  2 +-
> >  arch/powerpc/mm/fault.c                 |  2 +-
> >  arch/x86/mm/fault.c                     |  3 +-
> >  include/linux/compat.h                  |  2 +
> >  include/linux/sched/signal.h            | 12 +++++-
> >  include/uapi/asm-generic/siginfo.h      | 10 +++++
> >  kernel/signal.c                         | 50 +++++++++++++++++++----
> >  mm/memory-failure.c                     |  2 +-
> >  18 files changed, 136 insertions(+), 58 deletions(-)
> >
> > diff --git a/Documentation/arm64/tagged-pointers.rst b/Documentation/arm64/tagged-pointers.rst
> > index eab4323609b9..bd23039841a5 100644
> > --- a/Documentation/arm64/tagged-pointers.rst
> > +++ b/Documentation/arm64/tagged-pointers.rst
> > @@ -53,12 +53,21 @@ visibility.
> >  Preserving tags
> >  ---------------
> >
> > -Non-zero tags are not preserved when delivering signals. This means that
> > -signal handlers in applications making use of tags cannot rely on the
> > -tag information for user virtual addresses being maintained for fields
> > -inside siginfo_t. One exception to this rule is for signals raised in
> > -response to watchpoint debug exceptions, where the tag information will
> > -be preserved.
> > +Non-zero tags are not preserved in the fault address fields
> > +siginfo.si_addr or sigcontext.fault_address when delivering
> > +signals. This means that signal handlers in applications making use
> > +of tags cannot rely on the tag information for user virtual addresses
> > +being maintained in these fields. One exception to this rule is for
> > +signals raised in response to watchpoint debug exceptions, where the
> > +tag information will be preserved.
> > +
> > +The fault address tag is preserved in the si_addr_ignored_bits field
> > +of siginfo, which is set for signals raised in response to data aborts
> > +and instruction aborts. The si_addr_ignored_bits_mask field indicates
> > +which bits of the field are valid. The validity of these fields is
> > +indicated by the SI_XFLAG_IGNORED_BITS flag in siginfo.si_xflags,
> > +and the validity of si_xflags in turn is indicated by the kernel
> > +indicating support for the sigaction.sa_flags flag SA_XFLAGS.
> >
> >  The architecture prevents the use of a tagged PC, so the upper byte will
> >  be set to a sign-extension of bit 55 on exception return.
> > diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
> > index 7577a754d443..950d55dae948 100644
> > --- a/arch/arm64/include/asm/exception.h
> > +++ b/arch/arm64/include/asm/exception.h
> > @@ -32,7 +32,7 @@ static inline u32 disr_to_esr(u64 disr)
> >  }
> >
> >  asmlinkage void enter_from_user_mode(void);
> > -void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
> > +void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs);
> >  void do_undefinstr(struct pt_regs *regs);
> >  void do_bti(struct pt_regs *regs);
> >  asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
> > diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
> > index cee5928e1b7d..8e4f6c5b97af 100644
> > --- a/arch/arm64/include/asm/traps.h
> > +++ b/arch/arm64/include/asm/traps.h
> > @@ -26,8 +26,11 @@ void register_undef_hook(struct undef_hook *hook);
> >  void unregister_undef_hook(struct undef_hook *hook);
> >  void force_signal_inject(int signal, int code, unsigned long address);
> >  void arm64_notify_segfault(unsigned long addr);
> > -void arm64_force_sig_fault(int signo, int code, void __user *addr, const char *str);
> > -void arm64_force_sig_mceerr(int code, void __user *addr, short lsb, const char *str);
> > +void arm64_force_sig_fault(int signo, int code, void __user *addr,
> > +                        unsigned long far, unsigned char far_tb_mask,
> > +                        const char *str);
> > +void arm64_force_sig_mceerr(int code, void __user *addr, short lsb,
> > +                         unsigned long far, const char *str);
> >  void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr, const char *str);
> >
> >  /*
> > diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
> > index 7310a4f7f993..bc9e02fbb710 100644
> > --- a/arch/arm64/kernel/debug-monitors.c
> > +++ b/arch/arm64/kernel/debug-monitors.c
> > @@ -235,8 +235,8 @@ static void send_user_sigtrap(int si_code)
> >               local_irq_enable();
> >
> >       arm64_force_sig_fault(SIGTRAP, si_code,
> > -                          (void __user *)instruction_pointer(regs),
> > -                          "User debug trap");
> > +                           (void __user *)instruction_pointer(regs), 0, 0,
> > +                           "User debug trap");
> >  }
> >
> >  static int single_step_handler(unsigned long unused, unsigned int esr,
> > diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
> > index d3be9dbf5490..65ed01606480 100644
> > --- a/arch/arm64/kernel/entry-common.c
> > +++ b/arch/arm64/kernel/entry-common.c
> > @@ -22,7 +22,6 @@ static void notrace el1_abort(struct pt_regs *regs, unsigned long esr)
> >       unsigned long far = read_sysreg(far_el1);
> >
> >       local_daif_inherit(regs);
> > -     far = untagged_addr(far);
> >       do_mem_abort(far, esr, regs);
> >  }
> >  NOKPROBE_SYMBOL(el1_abort);
> > @@ -104,7 +103,6 @@ static void notrace el0_da(struct pt_regs *regs, unsigned long esr)
> >
> >       user_exit_irqoff();
> >       local_daif_restore(DAIF_PROCCTX);
> > -     far = untagged_addr(far);
> >       do_mem_abort(far, esr, regs);
> >  }
> >  NOKPROBE_SYMBOL(el0_da);
> > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> > index d8ebfd813e28..c73312064220 100644
> > --- a/arch/arm64/kernel/ptrace.c
> > +++ b/arch/arm64/kernel/ptrace.c
> > @@ -197,7 +197,7 @@ static void ptrace_hbptriggered(struct perf_event *bp,
> >       }
> >  #endif
> >       arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT,
> > -                           (void __user *)(bkpt->trigger),
> > +                           (void __user *)(bkpt->trigger), 0, 0,
> >                             desc);
> >  }
> >
> > diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> > index 13ebd5ca2070..c945c5cc1312 100644
> > --- a/arch/arm64/kernel/traps.c
> > +++ b/arch/arm64/kernel/traps.c
> > @@ -235,20 +235,25 @@ static void arm64_show_signal(int signo, const char *str)
> >  }
> >
> >  void arm64_force_sig_fault(int signo, int code, void __user *addr,
> > +                        unsigned long far, unsigned char far_tb_mask,
> >                          const char *str)
> >  {
> > +     unsigned long ignored_bits_mask = ((unsigned long)far_tb_mask) << 56;
> > +
> >       arm64_show_signal(signo, str);
> >       if (signo == SIGKILL)
> >               force_sig(SIGKILL);
> >       else
> > -             force_sig_fault(signo, code, addr);
> > +             force_sig_fault_with_ignored_bits(signo, code, addr,
> > +                                               far & ignored_bits_mask,
> > +                                               ignored_bits_mask);
> >  }
> >
> >  void arm64_force_sig_mceerr(int code, void __user *addr, short lsb,
> > -                         const char *str)
> > +                         unsigned long far, const char *str)
> >  {
> >       arm64_show_signal(SIGBUS, str);
> > -     force_sig_mceerr(code, addr, lsb);
> > +     force_sig_mceerr(code, addr, lsb, far & (0xffUL << 56), 0xffUL << 56);
> >  }
> >
> >  void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr,
> > @@ -267,7 +272,7 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
> >               current->thread.fault_address = 0;
> >               current->thread.fault_code = err;
> >
> > -             arm64_force_sig_fault(signo, sicode, addr, str);
> > +             arm64_force_sig_fault(signo, sicode, addr, 0, 0, str);
> >       } else {
> >               die(str, regs, err);
> >       }
> > @@ -829,7 +834,7 @@ void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
> >       current->thread.fault_address = 0;
> >       current->thread.fault_code = esr;
> >
> > -     arm64_force_sig_fault(SIGILL, ILL_ILLOPC, pc,
> > +     arm64_force_sig_fault(SIGILL, ILL_ILLOPC, pc, 0, 0,
> >                             "Bad EL0 synchronous exception");
> >  }
> >
> > diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> > index f07333e86c2f..82f05644417e 100644
> > --- a/arch/arm64/mm/fault.c
> > +++ b/arch/arm64/mm/fault.c
> > @@ -40,7 +40,7 @@
> >  #include <asm/traps.h>
> >
> >  struct fault_info {
> > -     int     (*fn)(unsigned long addr, unsigned int esr,
> > +     int     (*fn)(unsigned long far, unsigned int esr,
> >                     struct pt_regs *regs);
> >       int     sig;
> >       int     code;
> > @@ -383,8 +383,11 @@ static void set_thread_esr(unsigned long address, unsigned int esr)
> >       current->thread.fault_code = esr;
> >  }
> >
> > -static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> > +static void do_bad_area(unsigned long far, unsigned int esr,
> > +                     struct pt_regs *regs)
> >  {
> > +     unsigned long addr = untagged_addr(far);
> > +
> >       /*
> >        * If we are in kernel mode at this point, we have no context to
> >        * handle this fault with.
> > @@ -394,7 +397,7 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
> >
> >               set_thread_esr(addr, esr);
> >               arm64_force_sig_fault(inf->sig, inf->code, (void __user *)addr,
> > -                                   inf->name);
> > +                                   far, 0xff, inf->name);
> >       } else {
> >               __do_kernel_fault(addr, esr, regs);
> >       }
> > @@ -446,7 +449,7 @@ static bool is_write_abort(unsigned int esr)
> >       return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM);
> >  }
> >
> > -static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> > +static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
> >                                  struct pt_regs *regs)
> >  {
> >       const struct fault_info *inf;
> > @@ -454,6 +457,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> >       vm_fault_t fault;
> >       unsigned long vm_flags = VM_ACCESS_FLAGS;
> >       unsigned int mm_flags = FAULT_FLAG_DEFAULT;
> > +     unsigned long addr = untagged_addr(far);
> >
> >       if (kprobe_page_fault(regs, esr))
> >               return 0;
> > @@ -566,7 +570,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> >                * this page fault.
> >                */
> >               arm64_force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)addr,
> > -                                   inf->name);
> > +                                   far, 0xff, inf->name);
> >       } else if (fault & (VM_FAULT_HWPOISON_LARGE | VM_FAULT_HWPOISON)) {
> >               unsigned int lsb;
> >
> > @@ -575,7 +579,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> >                       lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
> >
> >               arm64_force_sig_mceerr(BUS_MCEERR_AR, (void __user *)addr, lsb,
> > -                                    inf->name);
> > +                                    far, inf->name);
> >       } else {
> >               /*
> >                * Something tried to access memory that isn't in our memory
> > @@ -583,8 +587,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> >                */
> >               arm64_force_sig_fault(SIGSEGV,
> >                                     fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR,
> > -                                   (void __user *)addr,
> > -                                   inf->name);
> > +                                   (void __user *)addr, far, 0xff, inf->name);
> >       }
> >
> >       return 0;
> > @@ -594,30 +597,32 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> >       return 0;
> >  }
> >
> > -static int __kprobes do_translation_fault(unsigned long addr,
> > +static int __kprobes do_translation_fault(unsigned long far,
> >                                         unsigned int esr,
> >                                         struct pt_regs *regs)
> >  {
> > +     unsigned long addr = untagged_addr(far);
> > +
> >       if (is_ttbr0_addr(addr))
> > -             return do_page_fault(addr, esr, regs);
> > +             return do_page_fault(far, esr, regs);
> >
> > -     do_bad_area(addr, esr, regs);
> > +     do_bad_area(far, esr, regs);
> >       return 0;
> >  }
> >
> > -static int do_alignment_fault(unsigned long addr, unsigned int esr,
> > +static int do_alignment_fault(unsigned long far, unsigned int esr,
> >                             struct pt_regs *regs)
> >  {
> > -     do_bad_area(addr, esr, regs);
> > +     do_bad_area(far, esr, regs);
> >       return 0;
> >  }
> >
> > -static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> > +static int do_bad(unsigned long far, unsigned int esr, struct pt_regs *regs)
> >  {
> >       return 1; /* "fault" */
> >  }
> >
> > -static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> > +static int do_sea(unsigned long far, unsigned int esr, struct pt_regs *regs)
> >  {
> >       const struct fault_info *inf;
> >       void __user *siaddr;
> > @@ -635,7 +640,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> >       if (esr & ESR_ELx_FnV)
> >               siaddr = NULL;
> >       else
> > -             siaddr  = (void __user *)addr;
> > +             siaddr  = (void __user *)untagged_addr(far);
> >       arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr);
> >
> >       return 0;
> > @@ -708,11 +713,12 @@ static const struct fault_info fault_info[] = {
> >       { do_bad,               SIGKILL, SI_KERNEL,     "unknown 63"                    },
> >  };
> >
> > -void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> > +void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs)
> >  {
> >       const struct fault_info *inf = esr_to_fault_info(esr);
> > +     unsigned long addr = untagged_addr(far);
> >
> > -     if (!inf->fn(addr, esr, regs))
> > +     if (!inf->fn(far, esr, regs))
> >               return;
> >
> >       if (!user_mode(regs)) {
> > @@ -721,8 +727,8 @@ void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> >               show_pte(addr);
> >       }
> >
> > -     arm64_notify_die(inf->name, regs,
> > -                      inf->sig, inf->code, (void __user *)addr, esr);
> > +     arm64_notify_die(inf->name, regs, inf->sig, inf->code,
> > +                      (void __user *)addr, esr);
> >  }
> >  NOKPROBE_SYMBOL(do_mem_abort);
> >
> > @@ -735,8 +741,8 @@ NOKPROBE_SYMBOL(do_el0_irq_bp_hardening);
> >
> >  void do_sp_pc_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
> >  {
> > -     arm64_notify_die("SP/PC alignment exception", regs,
> > -                      SIGBUS, BUS_ADRALN, (void __user *)addr, esr);
> > +     arm64_notify_die("SP/PC alignment exception", regs, SIGBUS, BUS_ADRALN,
> > +                      (void __user *)addr, esr);
> >  }
> >  NOKPROBE_SYMBOL(do_sp_pc_abort);
> >
> > @@ -862,8 +868,8 @@ void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
> >               arm64_apply_bp_hardening();
> >
> >       if (inf->fn(addr_if_watchpoint, esr, regs)) {
> > -             arm64_notify_die(inf->name, regs,
> > -                              inf->sig, inf->code, (void __user *)pc, esr);
> > +             arm64_notify_die(inf->name, regs, inf->sig, inf->code,
> > +                              (void __user *)pc, esr);
> >       }
> >
> >       debug_exception_exit(regs);
> > diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> > index 38aa07ccdbcc..73a181c904b0 100644
> > --- a/arch/mips/kernel/traps.c
> > +++ b/arch/mips/kernel/traps.c
> > @@ -775,7 +775,7 @@ void force_fcr31_sig(unsigned long fcr31, void __user *fault_addr,
> >       else if (fcr31 & FPU_CSR_INE_X)
> >               si_code = FPE_FLTRES;
> >
> > -     force_sig_fault_to_task(SIGFPE, si_code, fault_addr, tsk);
> > +     force_sig_fault_to_task(SIGFPE, si_code, fault_addr, 0, 0, tsk);
> >  }
> >
> >  int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
> > diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
> > index 2127974982df..62c8bb0d7d31 100644
> > --- a/arch/parisc/kernel/ptrace.c
> > +++ b/arch/parisc/kernel/ptrace.c
> > @@ -89,7 +89,7 @@ void user_enable_single_step(struct task_struct *task)
> >                  parent know something happened. */
> >               force_sig_fault_to_task(SIGTRAP, TRAP_TRACE,
> >                                       (void __user *) (task_regs(task)->iaoq[0] & ~3),
> > -                                     task);
> > +                                     0, 0, task);
> >               /* notify_parent(task, SIGCHLD); */
> >               return;
> >       }
> > diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
> > index 4bfe2da9fbe3..2a6064ea2dfc 100644
> > --- a/arch/parisc/mm/fault.c
> > +++ b/arch/parisc/mm/fault.c
> > @@ -400,7 +400,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,
> >                               lsb = PAGE_SHIFT;
> >
> >                       force_sig_mceerr(BUS_MCEERR_AR, (void __user *) address,
> > -                                      lsb);
> > +                                      lsb, 0, 0);
> >                       return;
> >               }
> >  #endif
> > diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> > index 0add963a849b..4ffe2fb8e31a 100644
> > --- a/arch/powerpc/mm/fault.c
> > +++ b/arch/powerpc/mm/fault.c
> > @@ -152,7 +152,7 @@ static int do_sigbus(struct pt_regs *regs, unsigned long address,
> >               if (fault & VM_FAULT_HWPOISON)
> >                       lsb = PAGE_SHIFT;
> >
> > -             force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb);
> > +             force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb, 0, 0);
> >               return 0;
> >       }
> >
> > diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> > index 35f1498e9832..a443eec157f6 100644
> > --- a/arch/x86/mm/fault.c
> > +++ b/arch/x86/mm/fault.c
> > @@ -898,7 +898,8 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
> >                       lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
> >               if (fault & VM_FAULT_HWPOISON)
> >                       lsb = PAGE_SHIFT;
> > -             force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb);
> > +             force_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb, 0,
> > +                              0);
> >               return;
> >       }
> >  #endif
> > diff --git a/include/linux/compat.h b/include/linux/compat.h
> > index 55d4228dfd88..273146cf30fd 100644
> > --- a/include/linux/compat.h
> > +++ b/include/linux/compat.h
> > @@ -234,6 +234,8 @@ typedef struct compat_siginfo {
> >                               compat_uptr_t _pad[6];
> >                       };
> >                       compat_uptr_t _xflags;
> > +                     compat_uptr_t _addr_ignored_bits;
> > +                     compat_uptr_t _addr_ignored_bits_mask;
> >               } _sigfault;
> >
> >               /* SIGPOLL */
> > diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
> > index 1bad18a1d8ba..f62a014a5a4d 100644
> > --- a/include/linux/sched/signal.h
> > +++ b/include/linux/sched/signal.h
> > @@ -311,16 +311,24 @@ static inline void kernel_signal_stop(void)
> >  int force_sig_fault_to_task(int sig, int code, void __user *addr
> >       ___ARCH_SI_TRAPNO(int trapno)
> >       ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
> > -     , struct task_struct *t);
> > +     , unsigned long addr_ignored_bits, unsigned long addr_ignored_bits_mask,
> > +     struct task_struct *t);
> >  int force_sig_fault(int sig, int code, void __user *addr
> >       ___ARCH_SI_TRAPNO(int trapno)
> >       ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr));
> > +int force_sig_fault_with_ignored_bits(int sig, int code, void __user *addr
> > +     ___ARCH_SI_TRAPNO(int trapno)
> > +     ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
> > +     , unsigned long addr_ignored_bits,
> > +     unsigned long addr_ignored_bits_mask);
> >  int send_sig_fault(int sig, int code, void __user *addr
> >       ___ARCH_SI_TRAPNO(int trapno)
> >       ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
> >       , struct task_struct *t);
> >
> > -int force_sig_mceerr(int code, void __user *, short);
> > +int force_sig_mceerr(int code, void __user *addr, short addr_lsb,
> > +                  unsigned long addr_ignored_bits,
> > +                  unsigned long addr_ignored_bits_mask);
> >  int send_sig_mceerr(int code, void __user *, short, struct task_struct *);
> >
> >  int force_sig_bnderr(void __user *addr, void __user *lower, void __user *upper);
> > diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
> > index 413d804623c0..1fc6482b0ad4 100644
> > --- a/include/uapi/asm-generic/siginfo.h
> > +++ b/include/uapi/asm-generic/siginfo.h
> > @@ -94,6 +94,8 @@ union __sifields {
> >                       void *_pad[6];
> >               };
> >               unsigned long _xflags;
> > +             unsigned long _addr_ignored_bits;
> > +             unsigned long _addr_ignored_bits_mask;
> >       } _sigfault;
> >
> >       /* SIGPOLL */
> > @@ -155,6 +157,8 @@ typedef struct siginfo {
> >  #endif
> >  #define si_addr_lsb  _sifields._sigfault._addr_lsb
> >  #define si_xflags    _sifields._sigfault._xflags
> > +#define si_addr_ignored_bits         _sifields._sigfault._addr_ignored_bits
> > +#define si_addr_ignored_bits_mask    _sifields._sigfault._addr_ignored_bits_mask
> >  #define si_lower     _sifields._sigfault._addr_bnd._lower
> >  #define si_upper     _sifields._sigfault._addr_bnd._upper
> >  #define si_pkey              _sifields._sigfault._addr_pkey._pkey
> > @@ -295,6 +299,12 @@ typedef struct siginfo {
> >  #define EMT_TAGOVF   1       /* tag overflow */
> >  #define NSIGEMT              1
> >
> > +/*
> > + * SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT si_xflags
> > + */
> > +#define SI_XFLAG_IGNORED_BITS        1
> > +/* si_addr_ignored_bits{,_mask} fields valid */
> > +
> >  /*
> >   * sigevent definitions
> >   *
> > diff --git a/kernel/signal.c b/kernel/signal.c
> > index 72182eed1b8d..1f1e42adc57d 100644
> > --- a/kernel/signal.c
> > +++ b/kernel/signal.c
> > @@ -1652,7 +1652,8 @@ void force_sigsegv(int sig)
> >  int force_sig_fault_to_task(int sig, int code, void __user *addr
> >       ___ARCH_SI_TRAPNO(int trapno)
> >       ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
> > -     , struct task_struct *t)
> > +     , unsigned long addr_ignored_bits, unsigned long addr_ignored_bits_mask,
> > +     struct task_struct *t)
> >  {
> >       struct kernel_siginfo info;
> >
> > @@ -1669,17 +1670,32 @@ int force_sig_fault_to_task(int sig, int code, void __user *addr
> >       info.si_flags = flags;
> >       info.si_isr = isr;
> >  #endif
> > -     info.si_xflags = 0;
> > +     info.si_xflags = SI_XFLAG_IGNORED_BITS;
> > +     info.si_addr_ignored_bits = addr_ignored_bits;
> > +     info.si_addr_ignored_bits_mask = addr_ignored_bits_mask;
> >       return force_sig_info_to_task(&info, t);
> >  }
> >
> > -int force_sig_fault(int sig, int code, void __user *addr
> > +int force_sig_fault_with_ignored_bits(int sig, int code, void __user *addr
> >       ___ARCH_SI_TRAPNO(int trapno)
> > -     ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr))
> > +     ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr),
> > +     unsigned long addr_ignored_bits, unsigned long addr_ignored_bits_mask)
> >  {
> >       return force_sig_fault_to_task(sig, code, addr
> >                                      ___ARCH_SI_TRAPNO(trapno)
> > -                                    ___ARCH_SI_IA64(imm, flags, isr), current);
> > +                                    ___ARCH_SI_IA64(imm, flags, isr),
> > +                                    addr_ignored_bits,
> > +                                    addr_ignored_bits_mask, current);
> > +}
> > +
> > +int force_sig_fault(int sig, int code, void __user *addr
> > +     ___ARCH_SI_TRAPNO(int trapno)
> > +     ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr))
> > +{
> > +     return force_sig_fault_with_ignored_bits(sig, code, addr
> > +                                              ___ARCH_SI_TRAPNO(trapno)
> > +                                              ___ARCH_SI_IA64(imm, flags,
> > +                                                              isr), 0, 0);
> >  }
> >
> >  int send_sig_fault(int sig, int code, void __user *addr
> > @@ -1706,7 +1722,9 @@ int send_sig_fault(int sig, int code, void __user *addr
> >       return send_sig_info(info.si_signo, &info, t);
> >  }
> >
> > -int force_sig_mceerr(int code, void __user *addr, short lsb)
> > +int force_sig_mceerr(int code, void __user *addr, short lsb,
> > +                  unsigned long addr_ignored_bits,
> > +                  unsigned long addr_ignored_bits_mask)
>
> Rather than having to pass these extra arguments all over the place, can
> we just pass the far for addr, and have an arch-specific hook for
> splitting the ignored from non-ignored bits.  For now at least, I expect
> that ignored bits mask to be constant for a given platform and kernel.

That sounds like a good idea. I think that for MTE we will want to
make it conditional on the si_code (so SEGV_MTESERR would get 0xf <<
56 while everything else would get 0xff << 56) so I can hook that up
at the same time.

> >  {
> >       struct kernel_siginfo info;
> >
> > @@ -1717,7 +1735,9 @@ int force_sig_mceerr(int code, void __user *addr, short lsb)
> >       info.si_code = code;
> >       info.si_addr = addr;
> >       info.si_addr_lsb = lsb;
> > -     info.si_xflags = 0;
> > +     info.si_xflags = SI_XFLAG_IGNORED_BITS;
>
> Maybe drop the first _, so that this becomes
>
>         SIXFLAG_IGNORED_BITS
>
> ?  This may help to avoid anyone thinking this might be an si_code
> value.  Maybe ..._IGNORED_ADDR_BITS would also help make it clearer what
> this bit is referring to.

Yes, it should have been spelled the same way as the field name (i.e.
SIXFLAG_ADDR_IGNORED_BITS), that was my mistake. I don't have a strong
opinion on whether to keep the underscore in the middle or not.

> > +     info.si_addr_ignored_bits = addr_ignored_bits;
> > +     info.si_addr_ignored_bits_mask = addr_ignored_bits_mask;
> >       return force_sig_info(&info);
> >  }
> >
> > @@ -3298,6 +3318,8 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
> >               to->si_trapno = from->si_trapno;
> >  #endif
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_MCEERR:
> >               to->si_addr = ptr_to_compat(from->si_addr);
> > @@ -3306,6 +3328,8 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
> >  #endif
> >               to->si_addr_lsb = from->si_addr_lsb;
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_BNDERR:
> >               to->si_addr = ptr_to_compat(from->si_addr);
> > @@ -3315,6 +3339,8 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
> >               to->si_lower = ptr_to_compat(from->si_lower);
> >               to->si_upper = ptr_to_compat(from->si_upper);
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_PKUERR:
> >               to->si_addr = ptr_to_compat(from->si_addr);
> > @@ -3323,6 +3349,8 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
> >  #endif
> >               to->si_pkey = from->si_pkey;
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;>         case SIL_CHLD:
> >               to->si_pid = from->si_pid;
> > @@ -3382,6 +3410,8 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
> >               to->si_trapno = from->si_trapno;
> >  #endif
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_MCEERR:
> >               to->si_addr = compat_ptr(from->si_addr);
> > @@ -3390,6 +3420,8 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
> >  #endif
> >               to->si_addr_lsb = from->si_addr_lsb;
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_BNDERR:
> >               to->si_addr = compat_ptr(from->si_addr);
> > @@ -3399,6 +3431,8 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
> >               to->si_lower = compat_ptr(from->si_lower);
> >               to->si_upper = compat_ptr(from->si_upper);
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
> >               break;
> >       case SIL_FAULT_PKUERR:
> >               to->si_addr = compat_ptr(from->si_addr);
> > @@ -3407,6 +3441,8 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
> >  #endif
> >               to->si_pkey = from->si_pkey;
> >               to->si_xflags = from->si_xflags;
> > +             to->si_addr_ignored_bits = from->si_addr_ignored_bits;
> > +             to->si_addr_ignored_bits_mask = from->si_addr_ignored_bits_mask;
>
> Again, can you justify why this is exhaustive?  If there any way to
> factor this so as to reduce the number of places we need to hack this
> in?

Addressed on the other patch. Once I've factored the various
SIL_FAULT* cases there should be only a handful of places requiring
changes.

Peter



More information about the linux-arm-kernel mailing list