diff --git a/include/linux/kref.h b/include/linux/kref.h
index e15828f..6c44a0d 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -20,10 +20,19 @@
 #include <linux/kernel.h>
 #include <linux/mutex.h>
 
+/*
+ * Strictly speaking, atomic_t is signed. Avoid undefined behavior by keeping
+ * the reference count below 0x80000000.
+ */
+#define KREF_MAX_DECREMENTABLE 0x70000000
+#define KREF_MAX_INCREMENTABLE 0x78000000
+
 struct kref {
 	atomic_t refcount;
 };
 
+void kref_get_slowpath(struct kref *kref, unsigned int new_refcount);
+
 /**
  * kref_init - initialize object.
  * @kref: object in question.
@@ -39,11 +48,11 @@ static inline void kref_init(struct kref *kref)
  */
 static inline void kref_get(struct kref *kref)
 {
-	/* If refcount was 0 before incrementing then we have a race
-	 * condition when this kref is freeing by some other thread right now.
-	 * In this case one should use kref_get_unless_zero()
-	 */
-	WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
+	unsigned int new_refcount = atomic_inc_return(&kref->refcount);
+
+	if (unlikely(new_refcount < 2 ||
+	    new_refcount > KREF_MAX_INCREMENTABLE))
+		kref_get_slowpath(kref, new_refcount);
 }
 
 /**
@@ -69,6 +78,12 @@ static inline int kref_sub(struct kref *kref, unsigned int count,
 {
 	WARN_ON(release == NULL);
 
+#ifdef CONFIG_HARDEN_KREF_BIGMEM
+	/* If the object has been pinned, leak the reference. */
+	if (unlikely(atomic_read(&kref->refcount) > KREF_MAX_DECREMENTABLE))
+		return 0;
+#endif
+
 	if (atomic_sub_and_test((int) count, &kref->refcount)) {
 		release(kref);
 		return 1;
@@ -103,6 +118,13 @@ static inline int kref_put_mutex(struct kref *kref,
 				 struct mutex *lock)
 {
 	WARN_ON(release == NULL);
+
+#ifdef CONFIG_HARDEN_KREF_BIGMEM
+	/* If the object has been pinned, leak the reference. */
+	if (unlikely(atomic_read(&kref->refcount) > KREF_MAX_DECREMENTABLE))
+		return 0;
+#endif
+
 	if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
 		mutex_lock(lock);
 		if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
@@ -133,6 +155,11 @@ static inline int kref_put_mutex(struct kref *kref,
  */
 static inline int __must_check kref_get_unless_zero(struct kref *kref)
 {
-	return atomic_add_unless(&kref->refcount, 1, 0);
+	int old_value = __atomic_add_unless(&kref->refcount, 1, 0);
+
+	if (unlikely(old_value > KREF_MAX_INCREMENTABLE))
+		atomic_dec(&kref->refcount);
+
+	return old_value != 0;
 }
 #endif /* _KREF_H_ */
diff --git a/init/Kconfig b/init/Kconfig
index f755a60..8618353 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1855,6 +1855,22 @@ config PROFILING
 config TRACEPOINTS
 	bool
 
+config HARDEN_KREF_BIGMEM
+	bool "Harden kref against too many references"
+	depends on 64BIT
+	default n
+	help
+	  Prevent memory corruption caused by temporarily having more
+	  references to a kref-managed object than the reference counter can
+	  represent.
+	  This increases security for systems with over 16GB of RAM (swap
+	  doesn't count); if your kernel will only run on systems with <=16GB
+	  of RAM, you can safely leave this disabled to reduce kernel code
+	  size and increase performance. Even on systems with 64GB or so of
+	  RAM, this only protects against worst-case kernel code that probably
+	  doesn't exist; but if your kernel might run on a system with a
+	  terabyte of RAM or so, you'll probably want to enable this.
+
 source "arch/Kconfig"
 
 endmenu		# General setup
diff --git a/kernel/Makefile b/kernel/Makefile
index e2ec54e..e2dcf8d 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y     = fork.o exec_domain.o panic.o \
 	    extable.o params.o \
 	    kthread.o sys_ni.o nsproxy.o \
 	    notifier.o ksysfs.o cred.o reboot.o \
-	    async.o range.o smpboot.o
+	    async.o range.o smpboot.o kref.o
 
 obj-$(CONFIG_MULTIUSER) += groups.o
 
diff --git a/kernel/kref.c b/kernel/kref.c
new file mode 100644
index 0000000..2f277cc
--- /dev/null
+++ b/kernel/kref.c
@@ -0,0 +1,17 @@
+#include <linux/bug.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+
+void kref_get_slowpath(struct kref *kref, unsigned int new_refcount)
+{
+	/* If refcount was 0 before incrementing then we have a race
+	 * condition when this kref is freeing by some other thread right now.
+	 * In this case one should use kref_get_unless_zero()
+	 */
+	WARN_ON_ONCE(new_refcount < 2);
+
+	if (new_refcount > KREF_MAX_INCREMENTABLE)
+		atomic_dec(&kref->refcount);
+}
