Message ID | 20221129063358.3012362-2-feng.tang@intel.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [v2,1/2] mm/slub, kunit: add SLAB_SKIP_KFENCE flag for cache creation | expand |
On Tue, 29 Nov 2022 at 07:37, Feng Tang <feng.tang@intel.com> wrote: > > kmalloc redzone check for slub has been merged, and it's better to add > a kunit case for it, which is inspired by a real-world case as described > in commit 120ee599b5bf ("staging: octeon-usb: prevent memory corruption"): > > " > octeon-hcd will crash the kernel when SLOB is used. This usually happens > after the 18-byte control transfer when a device descriptor is read. > The DMA engine is always transferring full 32-bit words and if the > transfer is shorter, some random garbage appears after the buffer. > The problem is not visible with SLUB since it rounds up the allocations > to word boundary, and the extra bytes will go undetected. > " > > To avoid interrupting the normal functioning of kmalloc caches, a > kmem_cache mimicing kmalloc cache is created with similar and all > necessary flags to have kmalloc-redzone enabled, and kmalloc_trace() > is used to really test the orig_size and redzone setup. > > Suggested-by: Vlastimil Babka <vbabka@suse.cz> > Signed-off-by: Feng Tang <feng.tang@intel.com> > --- > Changelog: > > since v1: > * create a new cache mimicing kmalloc cache, reduce dependency > over global slub_debug setting (Vlastimil Babka) > > lib/slub_kunit.c | 23 +++++++++++++++++++++++ > mm/slab.h | 3 ++- > 2 files changed, 25 insertions(+), 1 deletion(-) > > diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c > index a303adf8f11c..dbdd656624d0 100644 > --- a/lib/slub_kunit.c > +++ b/lib/slub_kunit.c > @@ -122,6 +122,28 @@ static void test_clobber_redzone_free(struct kunit *test) > kmem_cache_destroy(s); > } > > +static void test_kmalloc_redzone_access(struct kunit *test) > +{ > + struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_kmalloc", 32, 0, > + SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE|DEFAULT_FLAGS, > + NULL); > + u8 *p = kmalloc_trace(s, GFP_KERNEL, 18); > + > + kasan_disable_current(); > + > + /* Suppress the -Warray-bounds warning */ > + OPTIMIZER_HIDE_VAR(p); > + p[18] = 0xab; > + p[19] = 0xab; > + > + kmem_cache_free(s, p); > + validate_slab_cache(s); > + KUNIT_EXPECT_EQ(test, 2, slab_errors); > + > + kasan_enable_current(); > + kmem_cache_destroy(s); > +} > + > static int test_init(struct kunit *test) > { > slab_errors = 0; > @@ -141,6 +163,7 @@ static struct kunit_case test_cases[] = { > #endif > > KUNIT_CASE(test_clobber_redzone_free), > + KUNIT_CASE(test_kmalloc_redzone_access), > {} > }; > > diff --git a/mm/slab.h b/mm/slab.h > index c71590f3a22b..b6cd98b16ba7 100644 > --- a/mm/slab.h > +++ b/mm/slab.h > @@ -327,7 +327,8 @@ static inline slab_flags_t kmem_cache_flags(unsigned int object_size, > /* Legal flag mask for kmem_cache_create(), for various configurations */ > #define SLAB_CORE_FLAGS (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA | \ > SLAB_CACHE_DMA32 | SLAB_PANIC | \ > - SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS ) > + SLAB_KMALLOC | SLAB_SKIP_KFENCE | \ > + SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS) Shouldn't this hunk be in the previous patch, otherwise that patch alone will fail? This will also make SLAB_SKIP_KFENCE generally available to be used for cache creation. This is a significant change, and before it wasn't possible. Perhaps add a brief note to the commit message (or have a separate patch). We were trying to avoid making this possible, as it might be abused - however, given it's required for tests like these, I suppose there's no way around it. Thanks, -- Marco
On Tue, 29 Nov 2022 at 12:01, Vlastimil Babka <vbabka@suse.cz> wrote: > > On 11/29/22 10:31, Marco Elver wrote: > > On Tue, 29 Nov 2022 at 07:37, Feng Tang <feng.tang@intel.com> wrote: > >> diff --git a/mm/slab.h b/mm/slab.h > >> index c71590f3a22b..b6cd98b16ba7 100644 > >> --- a/mm/slab.h > >> +++ b/mm/slab.h > >> @@ -327,7 +327,8 @@ static inline slab_flags_t kmem_cache_flags(unsigned int object_size, > >> /* Legal flag mask for kmem_cache_create(), for various configurations */ > >> #define SLAB_CORE_FLAGS (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA | \ > >> SLAB_CACHE_DMA32 | SLAB_PANIC | \ > >> - SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS ) > >> + SLAB_KMALLOC | SLAB_SKIP_KFENCE | \ > >> + SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS) > > > > Shouldn't this hunk be in the previous patch, otherwise that patch > > alone will fail? > > Good point. > > > This will also make SLAB_SKIP_KFENCE generally available to be used > > for cache creation. This is a significant change, and before it wasn't > > possible. Perhaps add a brief note to the commit message (or have a > > separate patch). We were trying to avoid making this possible, as it > > might be abused - however, given it's required for tests like these, I > > suppose there's no way around it. > > For SLAB_SKIP_KFENCE, we could also add the flag after creation to avoid > this trouble? After all there is a sysfs file to control it at runtime > anyway (via skip_kfence_store()). > In that case patch 1 would have to wrap kmem_cache_create() and the flag > addition with a new function to avoid repeating. That function could also be > adding SLAB_NO_USER_FLAGS to kmem_cache_create(), instead of the #define > DEFAULT_FLAGS. I wouldn't overcomplicate it, all we need is a way to say "this flag should not be used directly" - and only have it available via an indirect step. Availability via sysfs is one such step. And for tests, there are 2 options: 1. we could provide a function "kmem_cache_set_test_flags(cache, gfp_flags)" and define SLAB_TEST_FLAGS (which would include SLAB_SKIP_KFENCE). This still allows to set it generally, but should make abuse less likely due to the "test" in the name of that function. 2. just set it directly, s->flags |= SLAB_SKIP_KFENCE. If you're fine with #2, that seems simplest and would be my preference. > For SLAB_KMALLOC there's probably no such way unless we abuse the internal > APIs even more and call e.g. create_boot_cache() instead of > kmem_cache_create(). But that one is __init, so probably not. If we do > instead allow the flag, I wouldn't add it to SLAB_CORE_FLAGS but rather > SLAB_CACHE_FLAGS and SLAB_FLAGS_PERMITTED. I'd probably go with the simplest solution here.
On 11/29/22 12:48, Marco Elver wrote: > On Tue, 29 Nov 2022 at 12:01, Vlastimil Babka <vbabka@suse.cz> wrote: >> >> On 11/29/22 10:31, Marco Elver wrote: >> > On Tue, 29 Nov 2022 at 07:37, Feng Tang <feng.tang@intel.com> wrote: > >> For SLAB_SKIP_KFENCE, we could also add the flag after creation to avoid >> this trouble? After all there is a sysfs file to control it at runtime >> anyway (via skip_kfence_store()). >> In that case patch 1 would have to wrap kmem_cache_create() and the flag >> addition with a new function to avoid repeating. That function could also be >> adding SLAB_NO_USER_FLAGS to kmem_cache_create(), instead of the #define >> DEFAULT_FLAGS. > > I wouldn't overcomplicate it, all we need is a way to say "this flag > should not be used directly" - and only have it available via an > indirect step. Availability via sysfs is one such step. > > And for tests, there are 2 options: > > 1. we could provide a function "kmem_cache_set_test_flags(cache, > gfp_flags)" and define SLAB_TEST_FLAGS (which would include > SLAB_SKIP_KFENCE). This still allows to set it generally, but should > make abuse less likely due to the "test" in the name of that function. > > 2. just set it directly, s->flags |= SLAB_SKIP_KFENCE. > > If you're fine with #2, that seems simplest and would be my preference. Yeah, that's what I meant. But slub_kunit.c could still have own internal cache creation function so the "|SLAB_NO_USER_FLAGS" and "s->flags |= SLAB_SKIP_KFENCE" is not repeated X times. > >> For SLAB_KMALLOC there's probably no such way unless we abuse the internal >> APIs even more and call e.g. create_boot_cache() instead of >> kmem_cache_create(). But that one is __init, so probably not. If we do >> instead allow the flag, I wouldn't add it to SLAB_CORE_FLAGS but rather >> SLAB_CACHE_FLAGS and SLAB_FLAGS_PERMITTED. > > I'd probably go with the simplest solution here. Agreed.
On 11/29/22 13:56, Marco Elver wrote: > On Tue, 29 Nov 2022 at 13:53, Feng Tang <feng.tang@intel.com> wrote: >> >> On Tue, Nov 29, 2022 at 08:02:51PM +0800, Vlastimil Babka wrote: >> > On 11/29/22 12:48, Marco Elver wrote: >> > > On Tue, 29 Nov 2022 at 12:01, Vlastimil Babka <vbabka@suse.cz> wrote: >> > >> >> > >> On 11/29/22 10:31, Marco Elver wrote: >> > >> > On Tue, 29 Nov 2022 at 07:37, Feng Tang <feng.tang@intel.com> wrote: >> > > >> > >> For SLAB_SKIP_KFENCE, we could also add the flag after creation to avoid >> > >> this trouble? After all there is a sysfs file to control it at runtime >> > >> anyway (via skip_kfence_store()). >> > >> In that case patch 1 would have to wrap kmem_cache_create() and the flag >> > >> addition with a new function to avoid repeating. That function could also be >> > >> adding SLAB_NO_USER_FLAGS to kmem_cache_create(), instead of the #define >> > >> DEFAULT_FLAGS. >> > > >> > > I wouldn't overcomplicate it, all we need is a way to say "this flag >> > > should not be used directly" - and only have it available via an >> > > indirect step. Availability via sysfs is one such step. >> > > >> > > And for tests, there are 2 options: >> > > >> > > 1. we could provide a function "kmem_cache_set_test_flags(cache, >> > > gfp_flags)" and define SLAB_TEST_FLAGS (which would include >> > > SLAB_SKIP_KFENCE). This still allows to set it generally, but should >> > > make abuse less likely due to the "test" in the name of that function. >> > > >> > > 2. just set it directly, s->flags |= SLAB_SKIP_KFENCE. >> > > >> > > If you're fine with #2, that seems simplest and would be my preference. >> > >> > Yeah, that's what I meant. But slub_kunit.c could still have own internal >> > cache creation function so the "|SLAB_NO_USER_FLAGS" and "s->flags |= >> > SLAB_SKIP_KFENCE" is not repeated X times. >> >> I just quickly tried adding a new wrapper, like >> >> struct kmem_cache *debug_kmem_cache_create(const char *name, unsigned int size, >> unsigned int align, slab_flags_t flags, >> void (*ctor)(void *), slab_flags_t debug_flags); >> >> and found that, IIUC, both SLAB_KMALLOC and SLAB_NO_USER are creation >> time flag, while SLAB_SKIP_KFENCE is an allocation runtime flag which >> could be set after creation. >> >> So how about use the initial suggestion from Vlastimil to set the >> SKIP_KFENCE flag through an internal wrapper in slub_kunit.c? >> >> /* Only for debug and test use, to skip kfence allocation */ >> static inline void kmem_cache_skip_kfence(struct kmem_cache *s) >> { >> s->flags |= SLAB_SKIP_KFENCE; >> } > > Yes, that's fine - as long as it's local to slub_kunit.c, this seems > very reasonable. Wrapping just |= SLAB_SKIP_KFENCE won't help that much as you'd need to add a call to kmem_cache_skip_kfence() after each kmem_cache_create() in slub_kunit.c. That's why I propose a wrapper, *also internally defined in slub_kunit.c*, that calls kmem_cache_create() with flags |SLAB_NO_USER_FLAGS, then does s->flags |= SLAB_SKIP_KFENCE; then returns s. At this point said wrapper wouldn't even need align and ctor parameters and could pass 0 and NULL to kmem_cache_create() by itself, as no test uses different values. > Thanks, > -- Marco
diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c index a303adf8f11c..dbdd656624d0 100644 --- a/lib/slub_kunit.c +++ b/lib/slub_kunit.c @@ -122,6 +122,28 @@ static void test_clobber_redzone_free(struct kunit *test) kmem_cache_destroy(s); } +static void test_kmalloc_redzone_access(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_kmalloc", 32, 0, + SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE|DEFAULT_FLAGS, + NULL); + u8 *p = kmalloc_trace(s, GFP_KERNEL, 18); + + kasan_disable_current(); + + /* Suppress the -Warray-bounds warning */ + OPTIMIZER_HIDE_VAR(p); + p[18] = 0xab; + p[19] = 0xab; + + kmem_cache_free(s, p); + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + kasan_enable_current(); + kmem_cache_destroy(s); +} + static int test_init(struct kunit *test) { slab_errors = 0; @@ -141,6 +163,7 @@ static struct kunit_case test_cases[] = { #endif KUNIT_CASE(test_clobber_redzone_free), + KUNIT_CASE(test_kmalloc_redzone_access), {} }; diff --git a/mm/slab.h b/mm/slab.h index c71590f3a22b..b6cd98b16ba7 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -327,7 +327,8 @@ static inline slab_flags_t kmem_cache_flags(unsigned int object_size, /* Legal flag mask for kmem_cache_create(), for various configurations */ #define SLAB_CORE_FLAGS (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA | \ SLAB_CACHE_DMA32 | SLAB_PANIC | \ - SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS ) + SLAB_KMALLOC | SLAB_SKIP_KFENCE | \ + SLAB_TYPESAFE_BY_RCU | SLAB_DEBUG_OBJECTS) #if defined(CONFIG_DEBUG_SLAB) #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
kmalloc redzone check for slub has been merged, and it's better to add a kunit case for it, which is inspired by a real-world case as described in commit 120ee599b5bf ("staging: octeon-usb: prevent memory corruption"): " octeon-hcd will crash the kernel when SLOB is used. This usually happens after the 18-byte control transfer when a device descriptor is read. The DMA engine is always transferring full 32-bit words and if the transfer is shorter, some random garbage appears after the buffer. The problem is not visible with SLUB since it rounds up the allocations to word boundary, and the extra bytes will go undetected. " To avoid interrupting the normal functioning of kmalloc caches, a kmem_cache mimicing kmalloc cache is created with similar and all necessary flags to have kmalloc-redzone enabled, and kmalloc_trace() is used to really test the orig_size and redzone setup. Suggested-by: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Feng Tang <feng.tang@intel.com> --- Changelog: since v1: * create a new cache mimicing kmalloc cache, reduce dependency over global slub_debug setting (Vlastimil Babka) lib/slub_kunit.c | 23 +++++++++++++++++++++++ mm/slab.h | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-)