Skip to content

Commit afcc90f

Browse files
committed
usercopy: WARN() on slab cache usercopy region violations
This patch adds checking of usercopy cache whitelisting, and is modified from Brad Spengler/PaX Team's PAX_USERCOPY whitelisting code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. The SLAB and SLUB allocators are modified to WARN() on all copy operations in which the kernel heap memory being modified falls outside of the cache's defined usercopy region. Based on an earlier patch from David Windsor. Cc: Christoph Lameter <[email protected]> Cc: Pekka Enberg <[email protected]> Cc: David Rientjes <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Laura Abbott <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Mark Rutland <[email protected]> Cc: [email protected] Cc: [email protected] Signed-off-by: Kees Cook <[email protected]>
1 parent 8eb8284 commit afcc90f

File tree

4 files changed

+58
-10
lines changed

4 files changed

+58
-10
lines changed

include/linux/uaccess.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
274274
#endif
275275

276276
#ifdef CONFIG_HARDENED_USERCOPY
277+
void usercopy_warn(const char *name, const char *detail, bool to_user,
278+
unsigned long offset, unsigned long len);
277279
void __noreturn usercopy_abort(const char *name, const char *detail,
278280
bool to_user, unsigned long offset,
279281
unsigned long len);

mm/slab.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,7 +4392,9 @@ module_init(slab_proc_init);
43924392

43934393
#ifdef CONFIG_HARDENED_USERCOPY
43944394
/*
4395-
* Rejects objects that are incorrectly sized.
4395+
* Rejects incorrectly sized objects and objects that are to be copied
4396+
* to/from userspace but do not fall entirely within the containing slab
4397+
* cache's usercopy region.
43964398
*
43974399
* Returns NULL if check passes, otherwise const char * to name of cache
43984400
* to indicate an error.
@@ -4412,10 +4414,24 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page,
44124414
/* Find offset within object. */
44134415
offset = ptr - index_to_obj(cachep, page, objnr) - obj_offset(cachep);
44144416

4415-
/* Allow address range falling entirely within object size. */
4416-
if (offset <= cachep->object_size && n <= cachep->object_size - offset)
4417+
/* Allow address range falling entirely within usercopy region. */
4418+
if (offset >= cachep->useroffset &&
4419+
offset - cachep->useroffset <= cachep->usersize &&
4420+
n <= cachep->useroffset - offset + cachep->usersize)
44174421
return;
44184422

4423+
/*
4424+
* If the copy is still within the allocated object, produce
4425+
* a warning instead of rejecting the copy. This is intended
4426+
* to be a temporary method to find any missing usercopy
4427+
* whitelists.
4428+
*/
4429+
if (offset <= cachep->object_size &&
4430+
n <= cachep->object_size - offset) {
4431+
usercopy_warn("SLAB object", cachep->name, to_user, offset, n);
4432+
return;
4433+
}
4434+
44194435
usercopy_abort("SLAB object", cachep->name, to_user, offset, n);
44204436
}
44214437
#endif /* CONFIG_HARDENED_USERCOPY */

mm/slub.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,7 +3813,9 @@ EXPORT_SYMBOL(__kmalloc_node);
38133813

38143814
#ifdef CONFIG_HARDENED_USERCOPY
38153815
/*
3816-
* Rejects objects that are incorrectly sized.
3816+
* Rejects incorrectly sized objects and objects that are to be copied
3817+
* to/from userspace but do not fall entirely within the containing slab
3818+
* cache's usercopy region.
38173819
*
38183820
* Returns NULL if check passes, otherwise const char * to name of cache
38193821
* to indicate an error.
@@ -3827,7 +3829,6 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page,
38273829

38283830
/* Find object and usable object size. */
38293831
s = page->slab_cache;
3830-
object_size = slab_ksize(s);
38313832

38323833
/* Reject impossible pointers. */
38333834
if (ptr < page_address(page))
@@ -3845,10 +3846,24 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page,
38453846
offset -= s->red_left_pad;
38463847
}
38473848

3848-
/* Allow address range falling entirely within object size. */
3849-
if (offset <= object_size && n <= object_size - offset)
3849+
/* Allow address range falling entirely within usercopy region. */
3850+
if (offset >= s->useroffset &&
3851+
offset - s->useroffset <= s->usersize &&
3852+
n <= s->useroffset - offset + s->usersize)
38503853
return;
38513854

3855+
/*
3856+
* If the copy is still within the allocated object, produce
3857+
* a warning instead of rejecting the copy. This is intended
3858+
* to be a temporary method to find any missing usercopy
3859+
* whitelists.
3860+
*/
3861+
object_size = slab_ksize(s);
3862+
if (offset <= object_size && n <= object_size - offset) {
3863+
usercopy_warn("SLUB object", s->name, to_user, offset, n);
3864+
return;
3865+
}
3866+
38523867
usercopy_abort("SLUB object", s->name, to_user, offset, n);
38533868
}
38543869
#endif /* CONFIG_HARDENED_USERCOPY */

mm/usercopy.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,28 @@ static noinline int check_stack_object(const void *obj, unsigned long len)
5959
}
6060

6161
/*
62-
* If this function is reached, then CONFIG_HARDENED_USERCOPY has found an
63-
* unexpected state during a copy_from_user() or copy_to_user() call.
62+
* If these functions are reached, then CONFIG_HARDENED_USERCOPY has found
63+
* an unexpected state during a copy_from_user() or copy_to_user() call.
6464
* There are several checks being performed on the buffer by the
6565
* __check_object_size() function. Normal stack buffer usage should never
6666
* trip the checks, and kernel text addressing will always trip the check.
67-
* For cache objects, copies must be within the object size.
67+
* For cache objects, it is checking that only the whitelisted range of
68+
* bytes for a given cache is being accessed (via the cache's usersize and
69+
* useroffset fields). To adjust a cache whitelist, use the usercopy-aware
70+
* kmem_cache_create_usercopy() function to create the cache (and
71+
* carefully audit the whitelist range).
6872
*/
73+
void usercopy_warn(const char *name, const char *detail, bool to_user,
74+
unsigned long offset, unsigned long len)
75+
{
76+
WARN_ONCE(1, "Bad or missing usercopy whitelist? Kernel memory %s attempt detected %s %s%s%s%s (offset %lu, size %lu)!\n",
77+
to_user ? "exposure" : "overwrite",
78+
to_user ? "from" : "to",
79+
name ? : "unknown?!",
80+
detail ? " '" : "", detail ? : "", detail ? "'" : "",
81+
offset, len);
82+
}
83+
6984
void __noreturn usercopy_abort(const char *name, const char *detail,
7085
bool to_user, unsigned long offset,
7186
unsigned long len)

0 commit comments

Comments
 (0)