On Tue, Sep 27, 2022 at 3:20 AM Peter Collingbourne <pcc@xxxxxxxxxx> wrote: > > It is sometimes useful to know the values of the registers when a KASAN > report is generated. We can do this easily for reports that resulted from > a hardware exception by passing the struct pt_regs from the exception into > the report function; do so, but only in HW tags mode because registers > may have been corrupted during the check in other modes. > > Signed-off-by: Peter Collingbourne <pcc@xxxxxxxxxx> > --- > Applies to -next. > > v2: > - only do this in HW tags mode > - move pr_err to caller > > arch/arm64/mm/fault.c | 2 +- > include/linux/kasan.h | 10 ++++++++++ > mm/kasan/kasan.h | 1 + > mm/kasan/report.c | 30 +++++++++++++++++++++++------- > 4 files changed, 35 insertions(+), 8 deletions(-) > > diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c > index 5b391490e045..c4b91f5d8cc8 100644 > --- a/arch/arm64/mm/fault.c > +++ b/arch/arm64/mm/fault.c > @@ -316,7 +316,7 @@ static void report_tag_fault(unsigned long addr, unsigned long esr, > * find out access size. > */ > bool is_write = !!(esr & ESR_ELx_WNR); > - kasan_report(addr, 0, is_write, regs->pc); > + kasan_report_regs(addr, 0, is_write, regs); > } > #else > /* Tag faults aren't enabled without CONFIG_KASAN_HW_TAGS. */ > diff --git a/include/linux/kasan.h b/include/linux/kasan.h > index d811b3d7d2a1..381aea149353 100644 > --- a/include/linux/kasan.h > +++ b/include/linux/kasan.h > @@ -353,6 +353,16 @@ static inline void *kasan_reset_tag(const void *addr) > bool kasan_report(unsigned long addr, size_t size, > bool is_write, unsigned long ip); > > +/** > + * kasan_report_regs - print a report about a bad memory access detected by KASAN > + * @addr: address of the bad access > + * @size: size of the bad access > + * @is_write: whether the bad access is a write or a read > + * @regs: register values at the point of the bad memory access > + */ > +bool kasan_report_regs(unsigned long addr, size_t size, bool is_write, > + struct pt_regs *regs); > + > #else /* CONFIG_KASAN_SW_TAGS || CONFIG_KASAN_HW_TAGS */ > > static inline void *kasan_reset_tag(const void *addr) > diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h > index abbcc1b0eec5..39772c21a8ae 100644 > --- a/mm/kasan/kasan.h > +++ b/mm/kasan/kasan.h > @@ -175,6 +175,7 @@ struct kasan_report_info { > size_t access_size; > bool is_write; > unsigned long ip; > + struct pt_regs *regs; > > /* Filled in by the common reporting code. */ > void *first_bad_addr; > diff --git a/mm/kasan/report.c b/mm/kasan/report.c > index df3602062bfd..be8dd97940c7 100644 > --- a/mm/kasan/report.c > +++ b/mm/kasan/report.c > @@ -24,6 +24,7 @@ > #include <linux/types.h> > #include <linux/kasan.h> > #include <linux/module.h> > +#include <linux/sched/debug.h> > #include <linux/sched/task_stack.h> > #include <linux/uaccess.h> > #include <trace/events/error_report.h> > @@ -281,9 +282,6 @@ static void print_address_description(void *addr, u8 tag, > { > struct page *page = addr_to_page(addr); > > - dump_stack_lvl(KERN_ERR); > - pr_err("\n"); > - > if (info->cache && info->object) { > describe_object(addr, info); > pr_err("\n"); > @@ -391,11 +389,15 @@ static void print_report(struct kasan_report_info *info) > kasan_print_tags(tag, info->first_bad_addr); > pr_err("\n"); > > + if (info->regs) > + show_regs(info->regs); > + else > + dump_stack_lvl(KERN_ERR); > + > if (addr_has_metadata(addr)) { > + pr_err("\n"); > print_address_description(addr, tag, info); > print_memory_metadata(info->first_bad_addr); > - } else { > - dump_stack_lvl(KERN_ERR); > } > } > > @@ -467,8 +469,8 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_ty > * user_access_save/restore(): kasan_report_invalid_free() cannot be called > * from a UACCESS region, and kasan_report_async() is not used on x86. > */ > -bool kasan_report(unsigned long addr, size_t size, bool is_write, > - unsigned long ip) > +static bool __kasan_report(unsigned long addr, size_t size, bool is_write, > + unsigned long ip, struct pt_regs *regs) > { > bool ret = true; > void *ptr = (void *)addr; > @@ -489,6 +491,7 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write, > info.access_size = size; > info.is_write = is_write; > info.ip = ip; > + info.regs = regs; > > complete_report_info(&info); > > @@ -502,6 +505,19 @@ bool kasan_report(unsigned long addr, size_t size, bool is_write, > return ret; > } > > +bool kasan_report(unsigned long addr, size_t size, bool is_write, > + unsigned long ip) > +{ > + return __kasan_report(addr, size, is_write, ip, NULL); > +} > + > +bool kasan_report_regs(unsigned long addr, size_t size, bool is_write, > + struct pt_regs *regs) > +{ > + return __kasan_report(addr, size, is_write, instruction_pointer(regs), > + regs); > +} > + > #ifdef CONFIG_KASAN_HW_TAGS > void kasan_report_async(void) > { > -- > 2.37.3.998.g577e59143f-goog > Reviewed-by: Andrey Konovalov <andreyknvl@xxxxxxxxx> Thanks!