[PATCH] kasan: infer the requested size by scanning shadow memory

2 views
Skip to first unread message

Kuan-Ying Lee

unread,
Jan 3, 2023, 2:56:24 AM1/3/23
to Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, chinwe...@mediatek.com, qun-w...@mediatek.com, Kuan-Ying Lee, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
We scan the shadow memory to infer the requested size instead of
printing cache->object_size directly.

This patch will fix the confusing generic kasan report like below. [1]
Report shows "cache kmalloc-192 of size 192", but user
actually kmalloc(184).

==================================================================
BUG: KASAN: slab-out-of-bounds in _find_next_bit+0x143/0x160 lib/find_bit.c:109
Read of size 8 at addr ffff8880175766b8 by task kworker/1:1/26
...
The buggy address belongs to the object at ffff888017576600
which belongs to the cache kmalloc-192 of size 192
The buggy address is located 184 bytes inside of
192-byte region [ffff888017576600, ffff8880175766c0)
...
Memory state around the buggy address:
ffff888017576580: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff888017576600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff888017576680: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc
^
ffff888017576700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888017576780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================

After this patch, report will show "cache kmalloc-192 of size 184".

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216457 [1]

Signed-off-by: Kuan-Ying Lee <Kuan-Y...@mediatek.com>
---
mm/kasan/kasan.h | 5 +++++
mm/kasan/report.c | 3 ++-
mm/kasan/report_generic.c | 18 ++++++++++++++++++
3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 32413f22aa82..7bb627d21580 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -340,8 +340,13 @@ static inline void kasan_print_address_stack_frame(const void *addr) { }

#ifdef CONFIG_KASAN_GENERIC
void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object);
+int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache);
#else
static inline void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object) { }
+static inline int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache)
+{
+ return cache->object_size;
+}
#endif

bool kasan_report(unsigned long addr, size_t size,
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 1d02757e90a3..6de454bb2cad 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -236,12 +236,13 @@ static void describe_object_addr(const void *addr, struct kmem_cache *cache,
{
unsigned long access_addr = (unsigned long)addr;
unsigned long object_addr = (unsigned long)object;
+ int real_size = kasan_get_alloc_size((void *)object_addr, cache);
const char *rel_type;
int rel_bytes;

pr_err("The buggy address belongs to the object at %px\n"
" which belongs to the cache %s of size %d\n",
- object, cache->name, cache->object_size);
+ object, cache->name, real_size);

if (access_addr < object_addr) {
rel_type = "to the left";
diff --git a/mm/kasan/report_generic.c b/mm/kasan/report_generic.c
index 043c94b04605..01b38e459352 100644
--- a/mm/kasan/report_generic.c
+++ b/mm/kasan/report_generic.c
@@ -43,6 +43,24 @@ void *kasan_find_first_bad_addr(void *addr, size_t size)
return p;
}

+int kasan_get_alloc_size(void *addr, struct kmem_cache *cache)
+{
+ int size = 0;
+ u8 *shadow = (u8 *)kasan_mem_to_shadow(addr);
+
+ while (size < cache->object_size) {
+ if (*shadow == 0)
+ size += KASAN_GRANULE_SIZE;
+ else if (*shadow >= 1 && *shadow <= KASAN_GRANULE_SIZE - 1)
+ size += *shadow;
+ else
+ return size;
+ shadow++;
+ }
+
+ return cache->object_size;
+}
+
static const char *get_shadow_bug_type(struct kasan_report_info *info)
{
const char *bug_type = "unknown-crash";
--
2.18.0

Andrey Konovalov

unread,
Jan 3, 2023, 9:01:01 PM1/3/23
to Kuan-Ying Lee, Andrey Ryabinin, Alexander Potapenko, Dmitry Vyukov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, chinwe...@mediatek.com, qun-w...@mediatek.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
On Tue, Jan 3, 2023 at 8:56 AM Kuan-Ying Lee <Kuan-Y...@mediatek.com> wrote:
>
> We scan the shadow memory to infer the requested size instead of
> printing cache->object_size directly.
>
> This patch will fix the confusing generic kasan report like below. [1]
> Report shows "cache kmalloc-192 of size 192", but user
> actually kmalloc(184).
>
> ==================================================================
> BUG: KASAN: slab-out-of-bounds in _find_next_bit+0x143/0x160 lib/find_bit.c:109
> Read of size 8 at addr ffff8880175766b8 by task kworker/1:1/26
> ...
> The buggy address belongs to the object at ffff888017576600
> which belongs to the cache kmalloc-192 of size 192
> The buggy address is located 184 bytes inside of
> 192-byte region [ffff888017576600, ffff8880175766c0)
> ...
> Memory state around the buggy address:
> ffff888017576580: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
> ffff888017576600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >ffff888017576680: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc
> ^
> ffff888017576700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> ffff888017576780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> ==================================================================
>
> After this patch, report will show "cache kmalloc-192 of size 184".

I think this introduces more confusion. kmalloc-192 cache doesn't have
the size of 184.

Let's leave the first two lines as is, and instead change the second
two lines to:

The buggy address is located 0 bytes to the right of
requested 184-byte region [ffff888017576600, ffff8880175766c0)

This specifically points out an out-of-bounds access.

Note the added "requested". Alternatively, we could say "allocated".

> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -340,8 +340,13 @@ static inline void kasan_print_address_stack_frame(const void *addr) { }
>
> #ifdef CONFIG_KASAN_GENERIC
> void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object);
> +int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache);
> #else
> static inline void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object) { }
> +static inline int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache)
> +{
> + return cache->object_size;

Please implement similar shadow/tag walking for the tag-based modes.
Even though we can only deduce the requested size with the granularity
of 16 bytes, it still makes sense.

It makes sense to also use the word "allocated" instead of "requested"
for these modes, as the size is not deduced precisely.

> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -236,12 +236,13 @@ static void describe_object_addr(const void *addr, struct kmem_cache *cache,
> {
> unsigned long access_addr = (unsigned long)addr;
> unsigned long object_addr = (unsigned long)object;
> + int real_size = kasan_get_alloc_size((void *)object_addr, cache);

Please add another field to the mode-specific section of the
kasan_report_info structure, fill it in complete_report_info, and use
it here. See kasan_find_first_bad_addr as a reference.

Thanks for working on this!

kernel test robot

unread,
Jan 5, 2023, 3:10:58 PM1/5/23
to Kuan-Ying Lee, Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, oe-kbu...@lists.linux.dev, Linux Memory Management List, chinwe...@mediatek.com, qun-w...@mediatek.com, Kuan-Ying Lee, kasa...@googlegroups.com, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
Hi Kuan-Ying,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on akpm-mm/mm-everything]
[also build test ERROR on linus/master v6.2-rc2 next-20230105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Kuan-Ying-Lee/kasan-infer-the-requested-size-by-scanning-shadow-memory/20230103-155641
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link: https://lore.kernel.org/r/20230103075603.12294-1-Kuan-Ying.Lee%40mediatek.com
patch subject: [PATCH] kasan: infer the requested size by scanning shadow memory
config: arm64-randconfig-c004-20230105
compiler: aarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/2e7537415684a55e473e98515beeef6d03e09c8f
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Kuan-Ying-Lee/kasan-infer-the-requested-size-by-scanning-shadow-memory/20230103-155641
git checkout 2e7537415684a55e473e98515beeef6d03e09c8f
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash mm/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <l...@intel.com>

All errors (new ones prefixed by >>):

In file included from mm/kasan/common.c:30:
mm/kasan/kasan.h: In function 'kasan_get_alloc_size':
>> mm/kasan/kasan.h:348:21: error: invalid use of undefined type 'struct kmem_cache'
348 | return cache->object_size;
| ^~
--
In file included from mm/kasan/report.c:34:
mm/kasan/kasan.h: In function 'kasan_get_alloc_size':
>> mm/kasan/kasan.h:348:21: error: invalid use of undefined type 'struct kmem_cache'
348 | return cache->object_size;
| ^~
mm/kasan/kasan.h:349:1: error: control reaches end of non-void function [-Werror=return-type]
349 | }
| ^
cc1: some warnings being treated as errors
--
In file included from mm/kasan/sw_tags.c:33:
mm/kasan/kasan.h: In function 'kasan_get_alloc_size':
>> mm/kasan/kasan.h:348:21: error: invalid use of undefined type 'struct kmem_cache'
348 | return cache->object_size;
| ^~
mm/kasan/sw_tags.c: At top level:
mm/kasan/sw_tags.c:173:6: warning: no previous prototype for 'kasan_tag_mismatch' [-Wmissing-prototypes]
173 | void kasan_tag_mismatch(unsigned long addr, unsigned long access_info,
| ^~~~~~~~~~~~~~~~~~


vim +348 mm/kasan/kasan.h

340
341 #ifdef CONFIG_KASAN_GENERIC
342 void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object);
343 int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache);
344 #else
345 static inline void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object) { }
346 static inline int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache)
347 {
> 348 return cache->object_size;
349 }
350 #endif
351

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
config

Kuan-Ying Lee (李冠穎)

unread,
Jan 9, 2023, 12:02:48 AM1/9/23
to andre...@gmail.com, linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, Chinwen Chang (張錦文), dvy...@google.com, kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, vincenzo...@arm.com, gli...@google.com, matthi...@gmail.com
Did you mean region [ffff888017576600, ffff8880175766b8)?

>
> This specifically points out an out-of-bounds access.
>
> Note the added "requested". Alternatively, we could say "allocated".
>
> > --- a/mm/kasan/kasan.h
> > +++ b/mm/kasan/kasan.h
> > @@ -340,8 +340,13 @@ static inline void
> > kasan_print_address_stack_frame(const void *addr) { }
> >
> > #ifdef CONFIG_KASAN_GENERIC
> > void kasan_print_aux_stacks(struct kmem_cache *cache, const void
> > *object);
> > +int kasan_get_alloc_size(void *object_addr, struct kmem_cache
> > *cache);
> > #else
> > static inline void kasan_print_aux_stacks(struct kmem_cache
> > *cache, const void *object) { }
> > +static inline int kasan_get_alloc_size(void *object_addr, struct
> > kmem_cache *cache)
> > +{
> > + return cache->object_size;
>
> Please implement similar shadow/tag walking for the tag-based modes.
> Even though we can only deduce the requested size with the
> granularity
> of 16 bytes, it still makes sense.

Will do in v2.

>
> It makes sense to also use the word "allocated" instead of
> "requested"
> for these modes, as the size is not deduced precisely.
>
> > --- a/mm/kasan/report.c
> > +++ b/mm/kasan/report.c
> > @@ -236,12 +236,13 @@ static void describe_object_addr(const void
> > *addr, struct kmem_cache *cache,
> > {
> > unsigned long access_addr = (unsigned long)addr;
> > unsigned long object_addr = (unsigned long)object;
> > + int real_size = kasan_get_alloc_size((void *)object_addr,
> > cache);
>
> Please add another field to the mode-specific section of the
> kasan_report_info structure, fill it in complete_report_info, and use
> it here. See kasan_find_first_bad_addr as a reference.

Got it. Will do in v2.

Dmitry Vyukov

unread,
Jan 9, 2023, 1:51:18 AM1/9/23
to Kuan-Ying Lee, Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, chinwe...@mediatek.com, qun-w...@mediatek.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
This only works for out-of-bounds reports, but I don't see any checks
for report type. Won't this break reporting for all other report
types?

I would also print the cache name anyway. Sometimes reports are
perplexing and/or this logic may return a wrong result for some
reason. The total object size may be useful to understand harder
cases.

Andrey Konovalov

unread,
Jan 9, 2023, 3:55:37 PM1/9/23
to Kuan-Ying Lee (李冠穎), linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, Chinwen Chang (張錦文), dvy...@google.com, kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, vincenzo...@arm.com, gli...@google.com, matthi...@gmail.com
On Mon, Jan 9, 2023 at 6:02 AM Kuan-Ying Lee (李冠穎)
<Kuan-Y...@mediatek.com> wrote:
>
> > Let's leave the first two lines as is, and instead change the second
> > two lines to:
> >
> > The buggy address is located 0 bytes to the right of
> > requested 184-byte region [ffff888017576600, ffff8880175766c0)
>
> Did you mean region [ffff888017576600, ffff8880175766b8)?

Yes! Forgot to change the range. The idea is to refer to the requested
size in these two lines of the report.

Kuan-Ying Lee (李冠穎)

unread,
Jan 13, 2023, 2:59:21 AM1/13/23
to dvy...@google.com, linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, andre...@gmail.com, Chinwen Chang (張錦文), kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, gli...@google.com, vincenzo...@arm.com, matthi...@gmail.com
> > https://urldefense.com/v3/__https://bugzilla.kernel.org/show_bug.cgi?id=216457__;!!CTRNKA9wMg0ARbw!mLNcuZ83c39d0Xkut-WMY3CcvZcAYDuLCmv4mu7IAldw4_n4i6XvX8GORBfjOadWxOa6d-ODQdx6ZCSvB2g13Q$
> > $ [1]
I think it won't break reporting for other report types.
This function is only called by slab OOB and UAF.

> I would also print the cache name anyway. Sometimes reports are
> perplexing and/or this logic may return a wrong result for some
> reason. The total object size may be useful to understand harder
> cases.
>

Ok. I will keep the cache name and the total object_size.

Dmitry Vyukov

unread,
Jan 13, 2023, 3:02:13 AM1/13/23
to Kuan-Ying Lee (李冠穎), linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, andre...@gmail.com, Chinwen Chang (張錦文), kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, gli...@google.com, vincenzo...@arm.com, matthi...@gmail.com
I meant specifically UAF reports.
During UAF there are no 0s in the object shadow.

> > I would also print the cache name anyway. Sometimes reports are
> > perplexing and/or this logic may return a wrong result for some
> > reason. The total object size may be useful to understand harder
> > cases.
> >
>
> Ok. I will keep the cache name and the total object_size.
>
> > > + }
> > > +
> > > + return cache->object_size;
> > > +}
> > > +
> > > static const char *get_shadow_bug_type(struct kasan_report_info
> > > *info)
> > > {
> > > const char *bug_type = "unknown-crash";
>
> --
> You received this message because you are subscribed to the Google Groups "kasan-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/kasan-dev/edbcce8a1e9e772e3a3fd032cd4600bd5677c877.camel%40mediatek.com.

Kuan-Ying Lee (李冠穎)

unread,
Jan 13, 2023, 4:28:12 AM1/13/23
to dvy...@google.com, linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, andre...@gmail.com, Chinwen Chang (張錦文), kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, gli...@google.com, vincenzo...@arm.com, matthi...@gmail.com
Ok.
I will check the report type in v2.

> > > I would also print the cache name anyway. Sometimes reports are
> > > perplexing and/or this logic may return a wrong result for some
> > > reason. The total object size may be useful to understand harder
> > > cases.
> > >
> >
> > Ok. I will keep the cache name and the total object_size.
> >
> > > > + }
> > > > +
> > > > + return cache->object_size;
> > > > +}
> > > > +
> > > > static const char *get_shadow_bug_type(struct
> > > > kasan_report_info
> > > > *info)
> > > > {
> > > > const char *bug_type = "unknown-crash";
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "kasan-dev" group.
> > To unsubscribe from this group and stop receiving emails from it,
> > send an email to kasan-dev+...@googlegroups.com.
> > To view this discussion on the web visit
> > https://urldefense.com/v3/__https://groups.google.com/d/msgid/kasan-dev/edbcce8a1e9e772e3a3fd032cd4600bd5677c877.camel*40mediatek.com__;JQ!!CTRNKA9wMg0ARbw!nLk2eBIc9qAXEy50sxxXRS2IRZKY8WSfVt_T3VtaMDrIrRHx31xOy5cTmqZa1py5ifu9UiHoqrKmxtnVKcWfJQ$
> > Q$ .

Kuan-Ying Lee

unread,
Jan 18, 2023, 4:39:05 AM1/18/23
to Andrey Ryabinin, Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, chinwe...@mediatek.com, qun-w...@mediatek.com, Kuan-Ying Lee, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
We scan the shadow memory to infer the requested size instead of
printing cache->object_size directly.

This patch will fix the confusing kasan slab-out-of-bounds
report like below. [1]
Report shows "cache kmalloc-192 of size 192", but user
actually kmalloc(184).

==================================================================
BUG: KASAN: slab-out-of-bounds in _find_next_bit+0x143/0x160 lib/find_bit.c:109
Read of size 8 at addr ffff8880175766b8 by task kworker/1:1/26
...
The buggy address belongs to the object at ffff888017576600
which belongs to the cache kmalloc-192 of size 192
The buggy address is located 184 bytes inside of
192-byte region [ffff888017576600, ffff8880175766c0)
...
Memory state around the buggy address:
ffff888017576580: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff888017576600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff888017576680: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc
^
ffff888017576700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888017576780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================

After this patch, slab-out-of-bounds report will show as below.
==================================================================
...
The buggy address belongs to the object at ffff888017576600
which belongs to the cache kmalloc-192 of size 192
The buggy address is located 0 bytes right of
allocated 184-byte region [ffff888017576600, ffff8880175766b8)
...
==================================================================

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216457 [1]

Signed-off-by: Kuan-Ying Lee <Kuan-Y...@mediatek.com>
---
V1 -> V2:
- Implement getting allocated size of object for tag-based kasan.
- Refine the kasan report.
- Check if it is slab-out-of-bounds report type.
- Thanks for Andrey and Dmitry suggestion.

mm/kasan/kasan.h | 2 ++
mm/kasan/report.c | 20 +++++++++++++-------
mm/kasan/report_generic.c | 24 ++++++++++++++++++++++++
mm/kasan/report_hw_tags.c | 18 ++++++++++++++++++
mm/kasan/report_sw_tags.c | 17 +++++++++++++++++
mm/kasan/report_tags.c | 8 ++++++++
6 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index abbcc1b0eec5..15ffd46fec6a 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -185,6 +185,7 @@ struct kasan_report_info {
const char *bug_type;
struct kasan_track alloc_track;
struct kasan_track free_track;
+ int obj_size;
};

/* Do not change the struct layout: compiler ABI. */
@@ -306,6 +307,7 @@ static inline bool addr_has_metadata(const void *addr)
void *kasan_find_first_bad_addr(void *addr, size_t size);
void kasan_complete_mode_report_info(struct kasan_report_info *info);
void kasan_metadata_fetch_row(char *buffer, void *row);
+int kasan_get_alloc_size(void *object_addr, struct kmem_cache *cache);

#if defined(CONFIG_KASAN_SW_TAGS) || defined(CONFIG_KASAN_HW_TAGS)
void kasan_print_tags(u8 addr_tag, const void *addr);
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index df3602062bfd..dae0d4ae8fe9 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -210,12 +210,13 @@ static inline struct page *addr_to_page(const void *addr)
}

static void describe_object_addr(const void *addr, struct kmem_cache *cache,
- void *object)
+ void *object, int obj_size, const char *bug_type)
{
unsigned long access_addr = (unsigned long)addr;
unsigned long object_addr = (unsigned long)object;
const char *rel_type;
int rel_bytes;
+ bool slab_oob = false;

pr_err("The buggy address belongs to the object at %px\n"
" which belongs to the cache %s of size %d\n",
@@ -224,18 +225,22 @@ static void describe_object_addr(const void *addr, struct kmem_cache *cache,
if (access_addr < object_addr) {
rel_type = "to the left";
rel_bytes = object_addr - access_addr;
- } else if (access_addr >= object_addr + cache->object_size) {
+ } else if (access_addr >= object_addr + obj_size) {
rel_type = "to the right";
- rel_bytes = access_addr - (object_addr + cache->object_size);
+ rel_bytes = access_addr - (object_addr + obj_size);
} else {
rel_type = "inside";
rel_bytes = access_addr - object_addr;
}

+ if (strcmp(bug_type, "slab-out-of-bounds") == 0)
+ slab_oob = true;
+
pr_err("The buggy address is located %d bytes %s of\n"
- " %d-byte region [%px, %px)\n",
- rel_bytes, rel_type, cache->object_size, (void *)object_addr,
- (void *)(object_addr + cache->object_size));
+ " %s%d-byte region [%px, %px)\n",
+ rel_bytes, rel_type, slab_oob ? "allocated " : "",
+ obj_size, (void *)object_addr,
+ (void *)(object_addr + obj_size));
}

static void describe_object_stacks(struct kasan_report_info *info)
@@ -257,7 +262,8 @@ static void describe_object(const void *addr, struct kasan_report_info *info)
{
if (kasan_stack_collection_enabled())
describe_object_stacks(info);
- describe_object_addr(addr, info->cache, info->object);
+ describe_object_addr(addr, info->cache, info->object, info->obj_size,
+ info->bug_type);
}

static inline bool kernel_or_module_addr(const void *addr)
diff --git a/mm/kasan/report_generic.c b/mm/kasan/report_generic.c
index 043c94b04605..7b4bec9e6d1a 100644
--- a/mm/kasan/report_generic.c
+++ b/mm/kasan/report_generic.c
@@ -43,6 +43,25 @@ void *kasan_find_first_bad_addr(void *addr, size_t size)
return p;
}

+int kasan_get_alloc_size(void *addr, struct kmem_cache *cache)
+{
+ int size = 0;
+ u8 *shadow;
+
+ shadow = (u8 *)kasan_mem_to_shadow(addr);
+ while (size < cache->object_size) {
+ if (*shadow == 0)
+ size += KASAN_GRANULE_SIZE;
+ else if (*shadow >= 1 && *shadow <= KASAN_GRANULE_SIZE - 1)
+ size += *shadow;
+ else
+ return size;
+ shadow++;
+ }
+
+ return cache->object_size;
+}
+
static const char *get_shadow_bug_type(struct kasan_report_info *info)
{
const char *bug_type = "unknown-crash";
@@ -149,6 +168,11 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info)
memcpy(&info->free_track, &free_meta->free_track,
sizeof(info->free_track));
}
+
+ if (strcmp(info->bug_type, "slab-out-of-bounds") == 0)
+ info->obj_size = kasan_get_alloc_size(info->object, info->cache);
+ else
+ info->obj_size = info->cache->object_size;
}

void kasan_metadata_fetch_row(char *buffer, void *row)
diff --git a/mm/kasan/report_hw_tags.c b/mm/kasan/report_hw_tags.c
index f3d3be614e4b..e462dd750fe2 100644
--- a/mm/kasan/report_hw_tags.c
+++ b/mm/kasan/report_hw_tags.c
@@ -21,6 +21,24 @@ void *kasan_find_first_bad_addr(void *addr, size_t size)
return kasan_reset_tag(addr);
}

+int kasan_get_alloc_size(void *addr, struct kmem_cache *cache)
+{
+ int size = 0, i = 0;
+ u8 memory_tag;
+
+ while (size < cache->object_size) {
+ memory_tag = hw_get_mem_tag(addr + i * KASAN_GRANULE_SIZE);
+
+ if (memory_tag != KASAN_TAG_INVALID)
+ size += KASAN_GRANULE_SIZE;
+ else
+ return size;
+ i++;
+ }
+
+ return cache->object_size;
+}
+
void kasan_metadata_fetch_row(char *buffer, void *row)
{
int i;
diff --git a/mm/kasan/report_sw_tags.c b/mm/kasan/report_sw_tags.c
index 7a26397297ed..d50caefd7fd5 100644
--- a/mm/kasan/report_sw_tags.c
+++ b/mm/kasan/report_sw_tags.c
@@ -45,6 +45,23 @@ void *kasan_find_first_bad_addr(void *addr, size_t size)
return p;
}

+int kasan_get_alloc_size(void *addr, struct kmem_cache *cache)
+{
+ int size = 0;
+ u8 *shadow;
+
+ shadow = (u8 *)kasan_mem_to_shadow(addr);
+ while (size < cache->object_size) {
+ if (*shadow != KASAN_TAG_INVALID)
+ size += KASAN_GRANULE_SIZE;
+ else
+ return size;
+ shadow++;
+ }
+
+ return cache->object_size;
+}
+
void kasan_metadata_fetch_row(char *buffer, void *row)
{
memcpy(buffer, kasan_mem_to_shadow(row), META_BYTES_PER_ROW);
diff --git a/mm/kasan/report_tags.c b/mm/kasan/report_tags.c
index ecede06ef374..b349a0ae1b83 100644
--- a/mm/kasan/report_tags.c
+++ b/mm/kasan/report_tags.c
@@ -7,6 +7,7 @@
#include <linux/atomic.h>

#include "kasan.h"
+#include "../slab.h"

extern struct kasan_stack_ring stack_ring;

@@ -113,4 +114,11 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info)
/* Assign the common bug type if no entries were found. */
if (!info->bug_type)
info->bug_type = get_common_bug_type(info);
+
+ if (info->object && info->cache) {
+ if (strcmp(info->bug_type, "slab-out-of-bounds") == 0)
+ info->obj_size = kasan_get_alloc_size(info->object, info->cache);
+ else
+ info->obj_size = info->cache->object_size;
+ }
}
--
2.18.0

Andrey Konovalov

unread,
Jan 23, 2023, 4:47:08 PM1/23/23
to Kuan-Ying Lee, Andrey Ryabinin, Alexander Potapenko, Dmitry Vyukov, Vincenzo Frascino, Andrew Morton, Matthias Brugger, chinwe...@mediatek.com, qun-w...@mediatek.com, kasa...@googlegroups.com, linu...@kvack.org, linux-...@vger.kernel.org, linux-ar...@lists.infradead.org, linux-m...@lists.infradead.org
Hi Kuan-Ying,

I came up with a few more things to fix while testing your patch and
decided to address them myself. Please check the v3 here:

https://github.com/xairy/linux/commit/012a584a9f11ba08a6051b075f7fd0a0eb54c719

The significant changes are to print "freed" for a slab-use-after-free
and only print the region state for the Generic mode (printing it for
Tag-Based modes doesn't work properly atm, see the comment in the
code). The rest is clean-ups and a few added comments. See the full
list of changes in the commit message.

Please check whether this v3 looks good to you, and then feel free to submit it.

Thank you!

Kuan-Ying Lee (李冠穎)

unread,
Jan 28, 2023, 9:58:42 AM1/28/23
to andre...@gmail.com, linux-...@vger.kernel.org, linux-m...@lists.infradead.org, Qun-wei Lin (林群崴), linu...@kvack.org, Chinwen Chang (張錦文), dvy...@google.com, kasa...@googlegroups.com, ak...@linux-foundation.org, ryabin...@gmail.com, linux-ar...@lists.infradead.org, vincenzo...@arm.com, gli...@google.com, matthi...@gmail.com
> > https://urldefense.com/v3/__https://bugzilla.kernel.org/show_bug.cgi?id=216457__;!!CTRNKA9wMg0ARbw!iEOOICl7DzhvfYobmQ8MsNFAWmbqicXdjd0LYWw9uBOqwj8lai7oEODVdRJyWUEXr11A3-m7wbIX2cdpxLwiW6Tm$
> > $ [1]
> >
> > Signed-off-by: Kuan-Ying Lee <Kuan-Y...@mediatek.com>
> > ---
> > V1 -> V2:
> > - Implement getting allocated size of object for tag-based kasan.
> > - Refine the kasan report.
> > - Check if it is slab-out-of-bounds report type.
> > - Thanks for Andrey and Dmitry suggestion.
>
> Hi Kuan-Ying,
>
> I came up with a few more things to fix while testing your patch and
> decided to address them myself. Please check the v3 here:
>
>
https://urldefense.com/v3/__https://github.com/xairy/linux/commit/012a584a9f11ba08a6051b075f7fd0a0eb54c719__;!!CTRNKA9wMg0ARbw!iEOOICl7DzhvfYobmQ8MsNFAWmbqicXdjd0LYWw9uBOqwj8lai7oEODVdRJyWUEXr11A3-m7wbIX2cdpxNwCtfpJ$ 
>
>
> The significant changes are to print "freed" for a slab-use-after-
> free
> and only print the region state for the Generic mode (printing it for
> Tag-Based modes doesn't work properly atm, see the comment in the
> code). The rest is clean-ups and a few added comments. See the full
> list of changes in the commit message.
>
> Please check whether this v3 looks good to you, and then feel free to
> submit it.

It looks good to me.
I will send the v3.
Thank you.

> Thank you!
Reply all
Reply to author
Forward
0 new messages