Currently, hardened usercopy performs dynamic bounds checking on slab
cache objects. This is good, but still leaves a lot of kernel memory available to be copied to/from userspace in the face of bugs. To further restrict what memory is available for copying, this creates a way to whitelist specific areas of a given slab cache object for copying to/from userspace, allowing much finer granularity of access control. Slab caches that are never exposed to userspace can declare no whitelist for their objects, thereby keeping them unavailable to userspace via dynamic copy operations. (Note, an implicit form of whitelisting is the use of constant sizes in usercopy operations and get_user()/put_user(); these bypass all hardened usercopy checks since these sizes cannot change at runtime.) This new check is WARN-by-default, so any mistakes can be found over the next several releases without breaking anyone's system. The series has roughly the following sections: - remove %p and improve reporting with offset - prepare infrastructure and whitelist kmalloc - update VFS subsystem with whitelists - update SCSI subsystem with whitelists - update network subsystem with whitelists - update process memory with whitelists - update per-architecture thread_struct with whitelists - update KVM with whitelists and fix ioctl bug - mark all other allocations as not whitelisted - update lkdtm for more sensible test overage -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 Comment: Kees Cook <kees@outflux.net> iQIcBAABCgAGBQJabvleAAoJEIly9N/cbcAmO1kQAJnjVPutnLSbnUteZxtsv7W4 43Cggvokfxr6l08Yh3hUowNxZVKjhF9uwMVgRRg9Nl5WdYCN+vCQbHz+ZdzGJXKq cGqdKWgexMKX+aBdNDrK7BphUeD46sH7JWR+a/lDV/BgPxBCm9i5ZZCgXbPP89AZ NpLBji7gz49wMsnm/x135xtNlZ3dG0oKETzi7MiR+NtKtUGvoIszSKy5JdPZ4m8q 9fnXmHqmwM6uQFuzDJPt1o+D1fusTuYnjI7EgyrJRRhQ+BB3qEFZApXnKNDRS9Dm uB7jtcwefJCjlZVCf2+PWTOEifH2WFZXLPFlC8f44jK6iRW2Nc+wVRisJ3vSNBG1 gaRUe/FSge68eyfQj5OFiwM/2099MNkKdZ0fSOjEBeubQpiFChjgWgcOXa5Bhlrr C4CIhFV2qg/tOuHDAF+Q5S96oZkaTy5qcEEwhBSW15ySDUaRWFSrtboNt6ZVOhug d8JJvDCQWoNu1IQozcbv6xW/Rk7miy8c0INZ4q33YUvIZpH862+vgDWfTJ73Zy9H jR/8eG6t3kFHKS1vWdKZzOX1bEcnd02CGElFnFYUEewKoV7ZeeLsYX7zodyUAKyi Yp5CImsDbWWTsptBg6h9nt2TseXTxYCt2bbmpJcqzsqSCUwOQNQ4/YpuzLeG0ihc JgOmUnQNJWCTwUUw5AS1 =tzmJ -----END PGP SIGNATURE----- Merge tag 'usercopy-v4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull hardened usercopy whitelisting from Kees Cook: "Currently, hardened usercopy performs dynamic bounds checking on slab cache objects. This is good, but still leaves a lot of kernel memory available to be copied to/from userspace in the face of bugs. To further restrict what memory is available for copying, this creates a way to whitelist specific areas of a given slab cache object for copying to/from userspace, allowing much finer granularity of access control. Slab caches that are never exposed to userspace can declare no whitelist for their objects, thereby keeping them unavailable to userspace via dynamic copy operations. (Note, an implicit form of whitelisting is the use of constant sizes in usercopy operations and get_user()/put_user(); these bypass all hardened usercopy checks since these sizes cannot change at runtime.) This new check is WARN-by-default, so any mistakes can be found over the next several releases without breaking anyone's system. The series has roughly the following sections: - remove %p and improve reporting with offset - prepare infrastructure and whitelist kmalloc - update VFS subsystem with whitelists - update SCSI subsystem with whitelists - update network subsystem with whitelists - update process memory with whitelists - update per-architecture thread_struct with whitelists - update KVM with whitelists and fix ioctl bug - mark all other allocations as not whitelisted - update lkdtm for more sensible test overage" * tag 'usercopy-v4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (38 commits) lkdtm: Update usercopy tests for whitelisting usercopy: Restrict non-usercopy caches to size 0 kvm: x86: fix KVM_XEN_HVM_CONFIG ioctl kvm: whitelist struct kvm_vcpu_arch arm: Implement thread_struct whitelist for hardened usercopy arm64: Implement thread_struct whitelist for hardened usercopy x86: Implement thread_struct whitelist for hardened usercopy fork: Provide usercopy whitelisting for task_struct fork: Define usercopy region in thread_stack slab caches fork: Define usercopy region in mm_struct slab caches net: Restrict unwhitelisted proto caches to size 0 sctp: Copy struct sctp_sock.autoclose to userspace using put_user() sctp: Define usercopy region in SCTP proto slab cache caif: Define usercopy region in caif proto slab cache ip: Define usercopy region in IP proto slab cache net: Define usercopy region in struct proto slab cache scsi: Define usercopy region in scsi_sense_cache slab cache cifs: Define usercopy region in cifs_request slab cache vxfs: Define usercopy region in vxfs_inode slab cache ufs: Define usercopy region in ufs_inode_cache slab cache ...
This commit is contained in:
commit
617aebe6a9
45 changed files with 515 additions and 215 deletions
|
|
@ -104,6 +104,20 @@ extern int arch_task_struct_size __read_mostly;
|
|||
# define arch_task_struct_size (sizeof(struct task_struct))
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST
|
||||
/*
|
||||
* If an architecture has not declared a thread_struct whitelist we
|
||||
* must assume something there may need to be copied to userspace.
|
||||
*/
|
||||
static inline void arch_thread_struct_whitelist(unsigned long *offset,
|
||||
unsigned long *size)
|
||||
{
|
||||
*offset = 0;
|
||||
/* Handle dynamically sized thread_struct. */
|
||||
*size = arch_task_struct_size - offsetof(struct task_struct, thread);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VMAP_STACK
|
||||
static inline struct vm_struct *task_stack_vm_area(const struct task_struct *t)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -135,9 +135,15 @@ struct mem_cgroup;
|
|||
void __init kmem_cache_init(void);
|
||||
bool slab_is_available(void);
|
||||
|
||||
struct kmem_cache *kmem_cache_create(const char *, size_t, size_t,
|
||||
slab_flags_t,
|
||||
void (*)(void *));
|
||||
extern bool usercopy_fallback;
|
||||
|
||||
struct kmem_cache *kmem_cache_create(const char *name, size_t size,
|
||||
size_t align, slab_flags_t flags,
|
||||
void (*ctor)(void *));
|
||||
struct kmem_cache *kmem_cache_create_usercopy(const char *name,
|
||||
size_t size, size_t align, slab_flags_t flags,
|
||||
size_t useroffset, size_t usersize,
|
||||
void (*ctor)(void *));
|
||||
void kmem_cache_destroy(struct kmem_cache *);
|
||||
int kmem_cache_shrink(struct kmem_cache *);
|
||||
|
||||
|
|
@ -153,9 +159,20 @@ void memcg_destroy_kmem_caches(struct mem_cgroup *);
|
|||
* f.e. add ____cacheline_aligned_in_smp to the struct declaration
|
||||
* then the objects will be properly aligned in SMP configurations.
|
||||
*/
|
||||
#define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\
|
||||
sizeof(struct __struct), __alignof__(struct __struct),\
|
||||
(__flags), NULL)
|
||||
#define KMEM_CACHE(__struct, __flags) \
|
||||
kmem_cache_create(#__struct, sizeof(struct __struct), \
|
||||
__alignof__(struct __struct), (__flags), NULL)
|
||||
|
||||
/*
|
||||
* To whitelist a single field for copying to/from usercopy, use this
|
||||
* macro instead for KMEM_CACHE() above.
|
||||
*/
|
||||
#define KMEM_CACHE_USERCOPY(__struct, __flags, __field) \
|
||||
kmem_cache_create_usercopy(#__struct, \
|
||||
sizeof(struct __struct), \
|
||||
__alignof__(struct __struct), (__flags), \
|
||||
offsetof(struct __struct, __field), \
|
||||
sizeof_field(struct __struct, __field), NULL)
|
||||
|
||||
/*
|
||||
* Common kmalloc functions provided by all allocators
|
||||
|
|
@ -167,15 +184,11 @@ void kzfree(const void *);
|
|||
size_t ksize(const void *);
|
||||
|
||||
#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
|
||||
const char *__check_heap_object(const void *ptr, unsigned long n,
|
||||
struct page *page);
|
||||
void __check_heap_object(const void *ptr, unsigned long n, struct page *page,
|
||||
bool to_user);
|
||||
#else
|
||||
static inline const char *__check_heap_object(const void *ptr,
|
||||
unsigned long n,
|
||||
struct page *page)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void __check_heap_object(const void *ptr, unsigned long n,
|
||||
struct page *page, bool to_user) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ struct kmem_cache {
|
|||
unsigned int *random_seq;
|
||||
#endif
|
||||
|
||||
size_t useroffset; /* Usercopy region offset */
|
||||
size_t usersize; /* Usercopy region size */
|
||||
|
||||
struct kmem_cache_node *node[MAX_NUMNODES];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@ struct kmem_cache {
|
|||
struct kasan_cache kasan_info;
|
||||
#endif
|
||||
|
||||
size_t useroffset; /* Usercopy region offset */
|
||||
size_t usersize; /* Usercopy region size */
|
||||
|
||||
struct kmem_cache_node *node[MAX_NUMNODES];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ enum {
|
|||
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sizeof_field(TYPE, MEMBER)
|
||||
*
|
||||
* @TYPE: The structure containing the field of interest
|
||||
* @MEMBER: The field to return the size of
|
||||
*/
|
||||
#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
|
||||
|
||||
/**
|
||||
* offsetofend(TYPE, MEMBER)
|
||||
*
|
||||
|
|
@ -26,6 +34,6 @@ enum {
|
|||
* @MEMBER: The member within the structure to get the end offset of
|
||||
*/
|
||||
#define offsetofend(TYPE, MEMBER) \
|
||||
(offsetof(TYPE, MEMBER) + sizeof(((TYPE *)0)->MEMBER))
|
||||
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -273,4 +273,12 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
|
|||
#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HARDENED_USERCOPY
|
||||
void usercopy_warn(const char *name, const char *detail, bool to_user,
|
||||
unsigned long offset, unsigned long len);
|
||||
void __noreturn usercopy_abort(const char *name, const char *detail,
|
||||
bool to_user, unsigned long offset,
|
||||
unsigned long len);
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_UACCESS_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue