diff mbox series

[v2,1/2] kunit: add a KUnit test for SLUB debugging functionality

Message ID 20210330120436.4591-1-glittao@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v2,1/2] kunit: add a KUnit test for SLUB debugging functionality | expand

Commit Message

Oliver Glitta March 30, 2021, 12:04 p.m. UTC
From: Oliver Glitta <glittao@gmail.com>

SLUB has resiliency_test() function which is hidden behind #ifdef
SLUB_RESILIENCY_TEST that is not part of Kconfig, so nobody
runs it. KUnit should be a proper replacement for it.

Try changing byte in redzone after allocation and changing
pointer to next free node, first byte, 50th byte and redzone
byte. Check if validation finds errors.

There are several differences from the original resiliency test:
Tests create own caches with known state instead of corrupting
shared kmalloc caches.

The corruption of freepointer uses correct offset, the original
resiliency test got broken with freepointer changes.

Scratch changing random byte test, because it does not have
meaning in this form where we need deterministic results.

Add new option CONFIG_SLUB_KUNIT_TEST in Kconfig.

Add a counter field "errors" to struct kmem_cache to count number
of errors detected in cache.

Silence bug report in SLUB test. Add SLAB_SILENT_ERRORS debug flag.
Add SLAB_SILENT_ERRORS flag to SLAB_NEVER_MERGE, SLAB_DEBUG_FLAGS,
SLAB_FLAGS_PERMITTED macros.

Signed-off-by: Oliver Glitta <glittao@gmail.com>
---
Changes since v1

Conversion from kselftest to KUnit test suggested by Marco Elver.
Error silencing.
Error counting improvements. 

 include/linux/slab.h     |   2 +
 include/linux/slub_def.h |   2 +
 lib/Kconfig.debug        |   5 ++
 lib/Makefile             |   1 +
 lib/test_slub.c          | 124 +++++++++++++++++++++++++++++++++++++++
 mm/slab.h                |   7 ++-
 mm/slab_common.c         |   2 +-
 mm/slub.c                |  64 +++++++++++++-------
 8 files changed, 184 insertions(+), 23 deletions(-)
 create mode 100644 lib/test_slub.c

Comments

kernel test robot March 30, 2021, 4:22 p.m. UTC | #1
Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.12-rc5]
[cannot apply to hnaz-linux-mm/master next-20210330]
[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]

url:    https://github.com/0day-ci/linux/commits/glittao-gmail-com/kunit-add-a-KUnit-test-for-SLUB-debugging-functionality/20210330-200635
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 1e43c377a79f9189fea8f2711b399d4e8b4e609b
config: x86_64-randconfig-r022-20210330 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 482283042f795ecc27838a3b2f76b5494991401c)
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
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/98f544695d3d3b4676d7ff7bd7c964c9d39172d2
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review glittao-gmail-com/kunit-add-a-KUnit-test-for-SLUB-debugging-functionality/20210330-200635
        git checkout 98f544695d3d3b4676d7ff7bd7c964c9d39172d2
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> mm/slub.c:744:17: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^  ~~~~~~~~~~~~~~~~~~
   mm/slub.c:744:17: note: use '&' for a bitwise operation
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^~
                          &
   mm/slub.c:744:17: note: remove constant to silence this warning
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                         ~^~~~~~~~~~~~~~~~~~~~~
   mm/slub.c:759:17: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^  ~~~~~~~~~~~~~~~~~~
   mm/slub.c:759:17: note: use '&' for a bitwise operation
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^~
                          &
   mm/slub.c:759:17: note: remove constant to silence this warning
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                         ~^~~~~~~~~~~~~~~~~~~~~
   mm/slub.c:807:17: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^  ~~~~~~~~~~~~~~~~~~
   mm/slub.c:807:17: note: use '&' for a bitwise operation
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                          ^~
                          &
   mm/slub.c:807:17: note: remove constant to silence this warning
           if (!(s->flags && SLAB_SILENT_ERRORS)) {
                         ~^~~~~~~~~~~~~~~~~~~~~
   mm/slub.c:1536:21: warning: unused function 'kmalloc_large_node_hook' [-Wunused-function]
   static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
                       ^
   4 warnings generated.


vim +744 mm/slub.c

   740	
   741	void object_err(struct kmem_cache *s, struct page *page,
   742				u8 *object, char *reason)
   743	{
 > 744		if (!(s->flags && SLAB_SILENT_ERRORS)) {
   745			slab_bug(s, "%s", reason);
   746			print_trailer(s, page, object);
   747		}
   748	}
   749	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Dan Carpenter March 31, 2021, 4:34 a.m. UTC | #2
Hi,

url:    https://github.com/0day-ci/linux/commits/glittao-gmail-com/kunit-add-a-KUnit-test-for-SLUB-debugging-functionality/20210330-200635
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 1e43c377a79f9189fea8f2711b399d4e8b4e609b
config: i386-randconfig-m021-20210330 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
mm/slub.c:744 object_err() warn: should this be a bitwise op?
mm/slub.c:759 slab_err() warn: should this be a bitwise op?
mm/slub.c:807 check_bytes_and_report() warn: should this be a bitwise op?

vim +744 mm/slub.c

75c66def8d8152 Andrey Ryabinin   2015-02-13  741  void object_err(struct kmem_cache *s, struct page *page,
81819f0fc8285a Christoph Lameter 2007-05-06  742  			u8 *object, char *reason)
81819f0fc8285a Christoph Lameter 2007-05-06  743  {
98f544695d3d3b Oliver Glitta     2021-03-30 @744  	if (!(s->flags && SLAB_SILENT_ERRORS)) {
                                                                       ^^
& was intended instead of &&

3dc5063786b273 Christoph Lameter 2008-04-23  745  		slab_bug(s, "%s", reason);
2492268472e7d3 Christoph Lameter 2007-07-17  746  		print_trailer(s, page, object);
81819f0fc8285a Christoph Lameter 2007-05-06  747  	}
98f544695d3d3b Oliver Glitta     2021-03-30  748  }
81819f0fc8285a Christoph Lameter 2007-05-06  749  
a38965bf941b7c Mathieu Malaterre 2018-06-07  750  static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page,
d0e0ac9772f8ec Chen Gang         2013-07-15  751  			const char *fmt, ...)
81819f0fc8285a Christoph Lameter 2007-05-06  752  {
81819f0fc8285a Christoph Lameter 2007-05-06  753  	va_list args;
81819f0fc8285a Christoph Lameter 2007-05-06  754  	char buf[100];
81819f0fc8285a Christoph Lameter 2007-05-06  755  
2492268472e7d3 Christoph Lameter 2007-07-17  756  	va_start(args, fmt);
2492268472e7d3 Christoph Lameter 2007-07-17  757  	vsnprintf(buf, sizeof(buf), fmt, args);
81819f0fc8285a Christoph Lameter 2007-05-06  758  	va_end(args);
98f544695d3d3b Oliver Glitta     2021-03-30 @759  	if (!(s->flags && SLAB_SILENT_ERRORS)) {
                                                                       ^^^^^^^^^^^^^^^^^^^^^

3dc5063786b273 Christoph Lameter 2008-04-23  760  		slab_bug(s, "%s", buf);
2492268472e7d3 Christoph Lameter 2007-07-17  761  		print_page_info(page);
81819f0fc8285a Christoph Lameter 2007-05-06  762  		dump_stack();
81819f0fc8285a Christoph Lameter 2007-05-06  763  	}
98f544695d3d3b Oliver Glitta     2021-03-30  764  }
81819f0fc8285a Christoph Lameter 2007-05-06  765  
f7cb1933621bce Christoph Lameter 2010-09-29  766  static void init_object(struct kmem_cache *s, void *object, u8 val)
81819f0fc8285a Christoph Lameter 2007-05-06  767  {
aa1ef4d7b3f67f Andrey Konovalov  2020-12-22  768  	u8 *p = kasan_reset_tag(object);
81819f0fc8285a Christoph Lameter 2007-05-06  769  
d86bd1bece6fc4 Joonsoo Kim       2016-03-15  770  	if (s->flags & SLAB_RED_ZONE)
d86bd1bece6fc4 Joonsoo Kim       2016-03-15  771  		memset(p - s->red_left_pad, val, s->red_left_pad);
d86bd1bece6fc4 Joonsoo Kim       2016-03-15  772  
81819f0fc8285a Christoph Lameter 2007-05-06  773  	if (s->flags & __OBJECT_POISON) {
3b0efdfa1e7193 Christoph Lameter 2012-06-13  774  		memset(p, POISON_FREE, s->object_size - 1);
3b0efdfa1e7193 Christoph Lameter 2012-06-13  775  		p[s->object_size - 1] = POISON_END;
81819f0fc8285a Christoph Lameter 2007-05-06  776  	}
81819f0fc8285a Christoph Lameter 2007-05-06  777  
81819f0fc8285a Christoph Lameter 2007-05-06  778  	if (s->flags & SLAB_RED_ZONE)
3b0efdfa1e7193 Christoph Lameter 2012-06-13  779  		memset(p + s->object_size, val, s->inuse - s->object_size);
81819f0fc8285a Christoph Lameter 2007-05-06  780  }
81819f0fc8285a Christoph Lameter 2007-05-06  781  
2492268472e7d3 Christoph Lameter 2007-07-17  782  static void restore_bytes(struct kmem_cache *s, char *message, u8 data,
2492268472e7d3 Christoph Lameter 2007-07-17  783  						void *from, void *to)
2492268472e7d3 Christoph Lameter 2007-07-17  784  {
2492268472e7d3 Christoph Lameter 2007-07-17  785  	slab_fix(s, "Restoring 0x%p-0x%p=0x%x\n", from, to - 1, data);
2492268472e7d3 Christoph Lameter 2007-07-17  786  	memset(from, data, to - from);
2492268472e7d3 Christoph Lameter 2007-07-17  787  }
2492268472e7d3 Christoph Lameter 2007-07-17  788  
2492268472e7d3 Christoph Lameter 2007-07-17  789  static int check_bytes_and_report(struct kmem_cache *s, struct page *page,
2492268472e7d3 Christoph Lameter 2007-07-17  790  			u8 *object, char *what,
2492268472e7d3 Christoph Lameter 2007-07-17  791  			u8 *start, unsigned int value, unsigned int bytes)
2492268472e7d3 Christoph Lameter 2007-07-17  792  {
2492268472e7d3 Christoph Lameter 2007-07-17  793  	u8 *fault;
2492268472e7d3 Christoph Lameter 2007-07-17  794  	u8 *end;
e1b70dd1e6429f Miles Chen        2019-11-30  795  	u8 *addr = page_address(page);
2492268472e7d3 Christoph Lameter 2007-07-17  796  
a79316c6178ca4 Andrey Ryabinin   2015-02-13  797  	metadata_access_enable();
aa1ef4d7b3f67f Andrey Konovalov  2020-12-22  798  	fault = memchr_inv(kasan_reset_tag(start), value, bytes);
a79316c6178ca4 Andrey Ryabinin   2015-02-13  799  	metadata_access_disable();
2492268472e7d3 Christoph Lameter 2007-07-17  800  	if (!fault)
81819f0fc8285a Christoph Lameter 2007-05-06  801  		return 1;
2492268472e7d3 Christoph Lameter 2007-07-17  802  
2492268472e7d3 Christoph Lameter 2007-07-17  803  	end = start + bytes;
2492268472e7d3 Christoph Lameter 2007-07-17  804  	while (end > fault && end[-1] == value)
2492268472e7d3 Christoph Lameter 2007-07-17  805  		end--;
2492268472e7d3 Christoph Lameter 2007-07-17  806  
98f544695d3d3b Oliver Glitta     2021-03-30 @807  	if (!(s->flags && SLAB_SILENT_ERRORS)) {
                                                                       ^^^^^^^^^^^^^^^^^^^^^

2492268472e7d3 Christoph Lameter 2007-07-17  808  		slab_bug(s, "%s overwritten", what);
e1b70dd1e6429f Miles Chen        2019-11-30  809  		pr_err("INFO: 0x%p-0x%p @offset=%tu. First byte 0x%x instead of 0x%x\n",
e1b70dd1e6429f Miles Chen        2019-11-30  810  					fault, end - 1, fault - addr,
e1b70dd1e6429f Miles Chen        2019-11-30  811  					fault[0], value);
2492268472e7d3 Christoph Lameter 2007-07-17  812  		print_trailer(s, page, object);
98f544695d3d3b Oliver Glitta     2021-03-30  813  	}
2492268472e7d3 Christoph Lameter 2007-07-17  814  
2492268472e7d3 Christoph Lameter 2007-07-17  815  	restore_bytes(s, what, value, fault, end);
2492268472e7d3 Christoph Lameter 2007-07-17  816  	return 0;
81819f0fc8285a Christoph Lameter 2007-05-06  817  }

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/include/linux/slab.h b/include/linux/slab.h
index 7ae604076767..ed1a5a64d028 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -25,6 +25,8 @@ 
  */
 /* DEBUG: Perform (expensive) checks on alloc/free */
 #define SLAB_CONSISTENCY_CHECKS	((slab_flags_t __force)0x00000100U)
+/* DEBUG: Silent bug reports */
+#define SLAB_SILENT_ERRORS	((slab_flags_t __force)0x00000200U)
 /* DEBUG: Red zone objs in a cache */
 #define SLAB_RED_ZONE		((slab_flags_t __force)0x00000400U)
 /* DEBUG: Poison objects */
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index dcde82a4434c..e4b51bb5bb83 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -133,6 +133,8 @@  struct kmem_cache {
 	unsigned int usersize;		/* Usercopy region size */
 
 	struct kmem_cache_node *node[MAX_NUMNODES];
+
+	int errors;			/* Number of errors in cache */
 };
 
 #ifdef CONFIG_SLUB_CPU_PARTIAL
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2779c29d9981..e0dec830c269 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2371,6 +2371,11 @@  config BITS_TEST
 
 	  If unsure, say N.
 
+config SLUB_KUNIT_TEST
+	tristate "KUnit Test for SLUB cache error detection" if !KUNIT_ALL_TESTS
+	depends on SLUB_DEBUG && KUNIT
+	default KUNIT_ALL_TESTS
+
 config TEST_UDELAY
 	tristate "udelay test driver"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index b5307d3eec1a..e1eb986c0e87 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -352,5 +352,6 @@  obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
 obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
 obj-$(CONFIG_BITS_TEST) += test_bits.o
 obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o
+obj-$(CONFIG_SLUB_KUNIT_TEST) += test_slub.o
 
 obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
diff --git a/lib/test_slub.c b/lib/test_slub.c
new file mode 100644
index 000000000000..4f8ea3c7d867
--- /dev/null
+++ b/lib/test_slub.c
@@ -0,0 +1,124 @@ 
+// SPDX-License-Identifier: GPL-2.0
+#include <kunit/test.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "../mm/slab.h"
+
+
+static void test_clobber_zone(struct kunit *test)
+{
+	struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_alloc", 64, 0,
+				SLAB_RED_ZONE | SLAB_SILENT_ERRORS, NULL);
+	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
+
+	p[64] = 0x12;
+
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 1, s->errors);
+
+	kmem_cache_free(s, p);
+	kmem_cache_destroy(s);
+}
+
+static void test_next_pointer(struct kunit *test)
+{
+	struct kmem_cache *s = kmem_cache_create("TestSlub_next_ptr_free", 64, 0,
+				SLAB_POISON | SLAB_SILENT_ERRORS, NULL);
+	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
+	unsigned long tmp;
+	unsigned long *ptr_addr;
+
+	kmem_cache_free(s, p);
+
+	ptr_addr = (unsigned long *)(p + s->offset);
+	tmp = *ptr_addr;
+	p[s->offset] = 0x12;
+
+	/*
+	 * Expecting two errors.
+	 * One for the corrupted freechain and the other one for the wrong
+	 * count of objects in use.
+	 */
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 2, s->errors);
+
+	/*
+	 * Try to repair corrupted freepointer.
+	 * Still expecting one error for the wrong count of objects in use.
+	 */
+	*ptr_addr = tmp;
+
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 1, s->errors);
+
+	/*
+	 * Previous validation repaired the count of objects in use.
+	 * Now expecting no error.
+	 */
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 0, s->errors);
+
+	kmem_cache_destroy(s);
+}
+
+static void test_first_word(struct kunit *test)
+{
+	struct kmem_cache *s = kmem_cache_create("TestSlub_1th_word_free", 64, 0,
+				SLAB_POISON | SLAB_SILENT_ERRORS, NULL);
+	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
+
+	kmem_cache_free(s, p);
+	*p = 0x78;
+
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 1, s->errors);
+
+	kmem_cache_destroy(s);
+}
+
+static void test_clobber_50th_byte(struct kunit *test)
+{
+	struct kmem_cache *s = kmem_cache_create("TestSlub_50th_word_free", 64, 0,
+				SLAB_POISON | SLAB_SILENT_ERRORS, NULL);
+	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
+
+	kmem_cache_free(s, p);
+	p[50] = 0x9a;
+
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 1, s->errors);
+	kmem_cache_destroy(s);
+}
+
+static void test_clobber_redzone_free(struct kunit *test)
+{
+	struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_free", 64, 0,
+				SLAB_RED_ZONE | SLAB_SILENT_ERRORS, NULL);
+	u8 *p = kmem_cache_alloc(s, GFP_KERNEL);
+
+	kmem_cache_free(s, p);
+	p[64] = 0xab;
+
+	validate_slab_cache(s);
+	KUNIT_EXPECT_EQ(test, 1, s->errors);
+	kmem_cache_destroy(s);
+}
+
+static struct kunit_case test_cases[] = {
+	KUNIT_CASE(test_clobber_zone),
+	KUNIT_CASE(test_next_pointer),
+	KUNIT_CASE(test_first_word),
+	KUNIT_CASE(test_clobber_50th_byte),
+	KUNIT_CASE(test_clobber_redzone_free),
+	{}
+};
+
+static struct kunit_suite test_suite = {
+	.name = "slub_test",
+	.test_cases = test_cases,
+};
+kunit_test_suite(test_suite);
+
+MODULE_LICENSE("GPL");
diff --git a/mm/slab.h b/mm/slab.h
index 076582f58f68..382507b6cab9 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -134,7 +134,8 @@  static inline slab_flags_t kmem_cache_flags(unsigned int object_size,
 #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
 #elif defined(CONFIG_SLUB_DEBUG)
 #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
-			  SLAB_TRACE | SLAB_CONSISTENCY_CHECKS)
+			  SLAB_TRACE | SLAB_CONSISTENCY_CHECKS | \
+			  SLAB_SILENT_ERRORS)
 #else
 #define SLAB_DEBUG_FLAGS (0)
 #endif
@@ -164,7 +165,8 @@  static inline slab_flags_t kmem_cache_flags(unsigned int object_size,
 			      SLAB_NOLEAKTRACE | \
 			      SLAB_RECLAIM_ACCOUNT | \
 			      SLAB_TEMPORARY | \
-			      SLAB_ACCOUNT)
+			      SLAB_ACCOUNT | \
+			      SLAB_SILENT_ERRORS)
 
 bool __kmem_cache_empty(struct kmem_cache *);
 int __kmem_cache_shutdown(struct kmem_cache *);
@@ -215,6 +217,7 @@  DECLARE_STATIC_KEY_TRUE(slub_debug_enabled);
 DECLARE_STATIC_KEY_FALSE(slub_debug_enabled);
 #endif
 extern void print_tracking(struct kmem_cache *s, void *object);
+long validate_slab_cache(struct kmem_cache *s);
 #else
 static inline void print_tracking(struct kmem_cache *s, void *object)
 {
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 88e833986332..239c9095e7ea 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -55,7 +55,7 @@  static DECLARE_WORK(slab_caches_to_rcu_destroy_work,
  */
 #define SLAB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
 		SLAB_TRACE | SLAB_TYPESAFE_BY_RCU | SLAB_NOLEAKTRACE | \
-		SLAB_FAILSLAB | kasan_never_merge())
+		SLAB_FAILSLAB | SLAB_SILENT_ERRORS | kasan_never_merge())
 
 #define SLAB_MERGE_SAME (SLAB_RECLAIM_ACCOUNT | SLAB_CACHE_DMA | \
 			 SLAB_CACHE_DMA32 | SLAB_ACCOUNT)
diff --git a/mm/slub.c b/mm/slub.c
index 3021ce9bf1b3..06916e11a2bf 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -673,14 +673,16 @@  static void slab_bug(struct kmem_cache *s, char *fmt, ...)
 
 static void slab_fix(struct kmem_cache *s, char *fmt, ...)
 {
-	struct va_format vaf;
-	va_list args;
-
-	va_start(args, fmt);
-	vaf.fmt = fmt;
-	vaf.va = &args;
-	pr_err("FIX %s: %pV\n", s->name, &vaf);
-	va_end(args);
+	if (!(s->flags & SLAB_SILENT_ERRORS)) {
+		struct va_format vaf;
+		va_list args;
+
+		va_start(args, fmt);
+		vaf.fmt = fmt;
+		vaf.va = &args;
+		pr_err("FIX %s: %pV\n", s->name, &vaf);
+		va_end(args);
+	}
 }
 
 static bool freelist_corrupted(struct kmem_cache *s, struct page *page,
@@ -739,8 +741,10 @@  static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p)
 void object_err(struct kmem_cache *s, struct page *page,
 			u8 *object, char *reason)
 {
-	slab_bug(s, "%s", reason);
-	print_trailer(s, page, object);
+	if (!(s->flags && SLAB_SILENT_ERRORS)) {
+		slab_bug(s, "%s", reason);
+		print_trailer(s, page, object);
+	}
 }
 
 static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page,
@@ -752,9 +756,11 @@  static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page,
 	va_start(args, fmt);
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
-	slab_bug(s, "%s", buf);
-	print_page_info(page);
-	dump_stack();
+	if (!(s->flags && SLAB_SILENT_ERRORS)) {
+		slab_bug(s, "%s", buf);
+		print_page_info(page);
+		dump_stack();
+	}
 }
 
 static void init_object(struct kmem_cache *s, void *object, u8 val)
@@ -798,11 +804,13 @@  static int check_bytes_and_report(struct kmem_cache *s, struct page *page,
 	while (end > fault && end[-1] == value)
 		end--;
 
-	slab_bug(s, "%s overwritten", what);
-	pr_err("INFO: 0x%p-0x%p @offset=%tu. First byte 0x%x instead of 0x%x\n",
+	if (!(s->flags && SLAB_SILENT_ERRORS)) {
+		slab_bug(s, "%s overwritten", what);
+		pr_err("INFO: 0x%p-0x%p @offset=%tu. First byte 0x%x instead of 0x%x\n",
 					fault, end - 1, fault - addr,
 					fault[0], value);
-	print_trailer(s, page, object);
+		print_trailer(s, page, object);
+	}
 
 	restore_bytes(s, what, value, fault, end);
 	return 0;
@@ -964,6 +972,7 @@  static int check_slab(struct kmem_cache *s, struct page *page)
 
 	if (!PageSlab(page)) {
 		slab_err(s, page, "Not a valid slab page");
+		s->errors += 1;
 		return 0;
 	}
 
@@ -971,11 +980,13 @@  static int check_slab(struct kmem_cache *s, struct page *page)
 	if (page->objects > maxobj) {
 		slab_err(s, page, "objects %u > max %u",
 			page->objects, maxobj);
+		s->errors += 1;
 		return 0;
 	}
 	if (page->inuse > page->objects) {
 		slab_err(s, page, "inuse %u > max %u",
 			page->inuse, page->objects);
+		s->errors += 1;
 		return 0;
 	}
 	/* Slab_pad_check fixes things up after itself */
@@ -1008,8 +1019,10 @@  static int on_freelist(struct kmem_cache *s, struct page *page, void *search)
 				page->freelist = NULL;
 				page->inuse = page->objects;
 				slab_fix(s, "Freelist cleared");
+				s->errors += 1;
 				return 0;
 			}
+			s->errors += 1;
 			break;
 		}
 		object = fp;
@@ -1026,12 +1039,14 @@  static int on_freelist(struct kmem_cache *s, struct page *page, void *search)
 			 page->objects, max_objects);
 		page->objects = max_objects;
 		slab_fix(s, "Number of objects adjusted.");
+		s->errors += 1;
 	}
 	if (page->inuse != page->objects - nr) {
 		slab_err(s, page, "Wrong object count. Counter is %d but counted were %d",
 			 page->inuse, page->objects - nr);
 		page->inuse = page->objects - nr;
 		slab_fix(s, "Object count adjusted.");
+		s->errors += 1;
 	}
 	return search == NULL;
 }
@@ -4629,8 +4644,10 @@  static void validate_slab(struct kmem_cache *s, struct page *page)
 		u8 val = test_bit(__obj_to_index(s, addr, p), map) ?
 			 SLUB_RED_INACTIVE : SLUB_RED_ACTIVE;
 
-		if (!check_object(s, page, p, val))
+		if (!check_object(s, page, p, val)) {
+			s->errors += 1;
 			break;
+		}
 	}
 	put_map(map);
 unlock:
@@ -4650,9 +4667,11 @@  static int validate_slab_node(struct kmem_cache *s,
 		validate_slab(s, page);
 		count++;
 	}
-	if (count != n->nr_partial)
+	if (count != n->nr_partial) {
 		pr_err("SLUB %s: %ld partial slabs counted but counter=%ld\n",
 		       s->name, count, n->nr_partial);
+		s->errors += 1;
+	}
 
 	if (!(s->flags & SLAB_STORE_USER))
 		goto out;
@@ -4661,20 +4680,23 @@  static int validate_slab_node(struct kmem_cache *s,
 		validate_slab(s, page);
 		count++;
 	}
-	if (count != atomic_long_read(&n->nr_slabs))
+	if (count != atomic_long_read(&n->nr_slabs)) {
 		pr_err("SLUB: %s %ld slabs counted but counter=%ld\n",
 		       s->name, count, atomic_long_read(&n->nr_slabs));
+		s->errors += 1;
+	}
 
 out:
 	spin_unlock_irqrestore(&n->list_lock, flags);
 	return count;
 }
 
-static long validate_slab_cache(struct kmem_cache *s)
+long validate_slab_cache(struct kmem_cache *s)
 {
 	int node;
 	unsigned long count = 0;
 	struct kmem_cache_node *n;
+	s->errors = 0;
 
 	flush_all(s);
 	for_each_kmem_cache_node(s, node, n)
@@ -4682,6 +4704,8 @@  static long validate_slab_cache(struct kmem_cache *s)
 
 	return count;
 }
+EXPORT_SYMBOL(validate_slab_cache);
+
 /*
  * Generate lists of code addresses where slabcache objects are allocated
  * and freed.