Revisited memory sanitization mechanism for 4.6.x

1. Got rid of statistics to simplify maintenance. Changing global
kernel structures is not a thing to take lightly.

2. Removed sanitization of pages. Starting from kernel 4.6, page
poisoning should be used instead. To enable it, set page_poison=on in
the boot options for the kernel.

3. The sanitization patch is now applied only if enhanced security is
enabled (disabled by default for ROSA Fresh, enabled for cert. builds).

You can also use "rpmbuild -ba --with enhanced_security <...>" to force
enable the feature.
This commit is contained in:
Evgenii Shatokhin 2016-07-29 13:39:16 +03:00
parent 16da528365
commit 9d9b9b948c
4 changed files with 357 additions and 0 deletions

View file

@ -8379,3 +8379,9 @@ CONFIG_FONT_SUPPORT=y
CONFIG_FONT_8x8=y CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y CONFIG_FONT_8x16=y
# CONFIG_SG_SPLIT is not set # CONFIG_SG_SPLIT is not set
#
# Hardening features
#
# CONFIG_PAX_MEMORY_SANITIZE is not set
# CONFIG_PAGE_POISONING_ZERO is not set

View file

@ -8198,3 +8198,9 @@ CONFIG_FONT_SUPPORT=y
CONFIG_FONT_8x8=y CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y CONFIG_FONT_8x16=y
# CONFIG_SG_SPLIT is not set # CONFIG_SG_SPLIT is not set
#
# Hardening features
#
# CONFIG_PAX_MEMORY_SANITIZE is not set
# CONFIG_PAGE_POISONING_ZERO is not set

View file

@ -40,6 +40,19 @@
%define build_dir ${RPM_BUILD_DIR}/%{top_dir_name} %define build_dir ${RPM_BUILD_DIR}/%{top_dir_name}
%define src_dir %{build_dir}/linux-%{tar_ver} %define src_dir %{build_dir}/linux-%{tar_ver}
############################################################################
# SELinux is now built in by default but some other hardening features
# are not.
%{?build_selinux}%{?!build_selinux:%bcond_with selinux}
%if %{with selinux}
%global enhanced_security 1
%else
%global enhanced_security 0
%endif
# Allow "rpmbuild --with enhanced_security <...>"
%{?_with_enhanced_security:%global enhanced_security 1}
############################################################################
# Build defines # Build defines
%define build_doc 1 %define build_doc 1
@ -201,6 +214,11 @@ Patch110: hp-wmi-rfkill-fix.patch
Patch111: 0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r11-4.5.0.patch Patch111: 0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r11-4.5.0.patch
Patch112: 0002-block-introduce-the-BFQ-v7r11-I-O-sched-for-4.5.0.patch Patch112: 0002-block-introduce-the-BFQ-v7r11-I-O-sched-for-4.5.0.patch
Patch113: 0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r11-for.patch Patch113: 0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r11-for.patch
# Sanitizing kernel memory
# We do not use "Patch:" here because apply_patched would always apply it
# then, it seems, even if we place "Patch: <..>" under a conditional.
Source701: sanitize-memory.patch
#################################################################### ####################################################################
# Defines for the things that are needed for all the kernels # Defines for the things that are needed for all the kernels
@ -563,6 +581,10 @@ cd %src_dir
%apply_patches %apply_patches
%if %{enhanced_security}
patch -p1 --fuzz=0 < %{SOURCE701}
%endif
# #
# Setup Begin # Setup Begin
# #
@ -574,6 +596,14 @@ sed -i 's/# CONFIG_DEBUG_INFO is not set/CONFIG_DEBUG_INFO=y\nCONFIG_DEBUG_INFO_
./kernel-%{arch_suffix}.config ./kernel-%{arch_suffix}.config
%endif %endif
# Hardening features
%if %{enhanced_security}
sed -i 's/# CONFIG_PAX_MEMORY_SANITIZE is not set/CONFIG_PAX_MEMORY_SANITIZE=y/' \
./kernel-%{arch_suffix}.config
sed -i 's/# CONFIG_PAGE_POISONING is not set/CONFIG_PAGE_POISONING=y\nCONFIG_PAGE_POISONING_NO_SANITY=y/' \
./kernel-%{arch_suffix}.config
%endif
# We may not want to build nrj-desktop flavour in some cases but its config # We may not want to build nrj-desktop flavour in some cases but its config
# is nice to have. It is used when preparing the RPM with the sources, # is nice to have. It is used when preparing the RPM with the sources,
# for example. # for example.

315
sanitize-memory.patch Normal file
View file

@ -0,0 +1,315 @@
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 0b3de80..2125a49 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2818,6 +2818,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
the specified number of seconds. This is to be used if
your oopses keep scrolling off the screen.
+ pax_sanitize_slab=
+ 0/1 to disable/enable slab object sanitization (disabled by
+ default).
+
pcbit= [HW,ISDN]
pcd. [PARIDE]
diff --git a/fs/buffer.c b/fs/buffer.c
index af0d9a8..2437a67 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -3406,7 +3406,7 @@ void __init buffer_init(void)
bh_cachep = kmem_cache_create("buffer_head",
sizeof(struct buffer_head), 0,
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_NO_SANITIZE),
NULL);
/*
diff --git a/fs/dcache.c b/fs/dcache.c
index 44008e3..e5c7f9d 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3444,7 +3444,8 @@ void __init vfs_caches_init_early(void)
void __init vfs_caches_init(void)
{
names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|
+ SLAB_NO_SANITIZE, NULL);
dcache_init();
inode_init();
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 508bd82..35f172f 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -23,6 +23,13 @@
#define SLAB_CONSISTENCY_CHECKS 0x00000100UL /* DEBUG: Perform (expensive) checks on alloc/free */
#define SLAB_RED_ZONE 0x00000400UL /* DEBUG: Red zone objs in a cache */
#define SLAB_POISON 0x00000800UL /* DEBUG: Poison objects */
+
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+#define SLAB_NO_SANITIZE 0x00001000UL /* PaX: Do not sanitize objs on free */
+#else
+#define SLAB_NO_SANITIZE 0x00000000UL
+#endif
+
#define SLAB_HWCACHE_ALIGN 0x00002000UL /* Align objs on cache lines */
#define SLAB_CACHE_DMA 0x00004000UL /* Use GFP_DMA memory */
#define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting */
diff --git a/kernel/fork.c b/kernel/fork.c
index d277e83..3ee91a2 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1882,7 +1882,7 @@ void __init proc_caches_init(void)
sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT,
NULL);
- vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC|SLAB_ACCOUNT);
+ vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC|SLAB_ACCOUNT|SLAB_NO_SANITIZE);
mmap_init();
nsproxy_cache_init();
}
diff --git a/mm/rmap.c b/mm/rmap.c
index 3ebf9c4..4e0d554 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -429,10 +429,10 @@ static void anon_vma_ctor(void *data)
void __init anon_vma_init(void)
{
anon_vma_cachep = kmem_cache_create("anon_vma", sizeof(struct anon_vma),
- 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC|SLAB_ACCOUNT,
+ 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC|SLAB_ACCOUNT|SLAB_NO_SANITIZE,
anon_vma_ctor);
anon_vma_chain_cachep = KMEM_CACHE(anon_vma_chain,
- SLAB_PANIC|SLAB_ACCOUNT);
+ SLAB_PANIC|SLAB_ACCOUNT|SLAB_NO_SANITIZE);
}
/*
diff --git a/mm/slab.c b/mm/slab.c
index 17e2848..25c241f 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -3332,6 +3332,17 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp,
kasan_slab_free(cachep, objp);
check_irq_off();
+
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ if (pax_sanitize_slab) {
+ if (!(cachep->flags & (SLAB_POISON | SLAB_NO_SANITIZE))) {
+ memset(objp, PAX_MEMORY_SANITIZE_VALUE, cachep->object_size);
+ if (cachep->ctor)
+ cachep->ctor(objp);
+ }
+ }
+#endif
+
kmemleak_free_recursive(objp, cachep->flags);
objp = cache_free_debugcheck(cachep, objp, caller);
diff --git a/mm/slab.h b/mm/slab.h
index 5969769..2f0bbc6 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -70,6 +70,15 @@ extern struct list_head slab_caches;
/* The slab cache that manages slab cache information */
extern struct kmem_cache *kmem_cache;
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+#ifdef CONFIG_X86_64
+#define PAX_MEMORY_SANITIZE_VALUE '\xfe'
+#else
+#define PAX_MEMORY_SANITIZE_VALUE '\xff'
+#endif
+extern bool pax_sanitize_slab;
+#endif
+
unsigned long calculate_alignment(unsigned long flags,
unsigned long align, unsigned long size);
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 3239bfd..8a974f5 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -44,7 +44,11 @@ struct kmem_cache *kmem_cache;
* Merge control. If this is set then no merging of slab caches will occur.
* (Could be removed. This was introduced to pacify the merge skeptics.)
*/
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+static int slab_nomerge = 1;
+#else
static int slab_nomerge;
+#endif
static int __init setup_slab_nomerge(char *str)
{
@@ -67,6 +71,20 @@ unsigned int kmem_cache_size(struct kmem_cache *s)
}
EXPORT_SYMBOL(kmem_cache_size);
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+bool pax_sanitize_slab = false;
+static int __init pax_sanitize_slab_setup(char *str)
+{
+ if (!str)
+ return -EINVAL;
+
+ pax_sanitize_slab = !!simple_strtol(str, NULL, 0);
+ pr_info("%sabled PaX slab sanitization\n", pax_sanitize_slab ? "En" : "Dis");
+ return 0;
+}
+early_param("pax_sanitize_slab", pax_sanitize_slab_setup);
+#endif
+
#ifdef CONFIG_DEBUG_VM
static int kmem_cache_sanity_check(const char *name, size_t size)
{
@@ -232,7 +250,11 @@ static inline void destroy_memcg_params(struct kmem_cache *s)
*/
int slab_unmergeable(struct kmem_cache *s)
{
- if (slab_nomerge || (s->flags & SLAB_NEVER_MERGE))
+ if (slab_nomerge || (s->flags & SLAB_NEVER_MERGE)
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ || pax_sanitize_slab
+#endif
+ )
return 1;
if (!is_root_cache(s))
@@ -255,7 +277,11 @@ struct kmem_cache *find_mergeable(size_t size, size_t align,
{
struct kmem_cache *s;
- if (slab_nomerge || (flags & SLAB_NEVER_MERGE))
+ if (slab_nomerge || (flags & SLAB_NEVER_MERGE)
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ || pax_sanitize_slab
+#endif
+ )
return NULL;
if (ctor)
@@ -411,6 +437,11 @@ kmem_cache_create(const char *name, size_t size, size_t align,
* passed flags.
*/
flags &= CACHE_CREATE_MASK;
+
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ if (flags & SLAB_DESTROY_BY_RCU)
+ flags |= SLAB_NO_SANITIZE;
+#endif
s = __kmem_cache_alias(name, size, align, flags, ctor);
if (s)
diff --git a/mm/slob.c b/mm/slob.c
index 5ec1580..385cdbc 100644
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -365,6 +365,11 @@ static void slob_free(void *block, int size)
return;
}
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ if (pax_sanitize_slab && !(c && (c->flags & SLAB_NO_SANITIZE)))
+ memset(block, PAX_MEMORY_SANITIZE_VALUE, size);
+#endif
+
if (!slob_page_free(sp)) {
/* This slob page is about to become partially free. Easy! */
sp->units = units;
diff --git a/mm/slub.c b/mm/slub.c
index 4dbb109e..aba52e1 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2778,6 +2778,14 @@ static __always_inline void slab_free(struct kmem_cache *s, struct page *page,
slab_free_freelist_hook(s, head, tail);
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ if (pax_sanitize_slab && !(s->flags & SLAB_NO_SANITIZE)) {
+ memset(x, PAX_MEMORY_SANITIZE_VALUE, s->object_size);
+ if (s->ctor)
+ s->ctor(x);
+ }
+#endif
+
redo:
/*
* Determine the currently cpus per cpu slab.
@@ -3291,6 +3299,9 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
s->inuse = size;
if (((flags & (SLAB_DESTROY_BY_RCU | SLAB_POISON)) ||
+#ifdef CONFIG_PAX_MEMORY_SANITIZE
+ (pax_sanitize_slab && !(flags & SLAB_NO_SANITIZE)) ||
+#endif
s->ctor)) {
/*
* Relocate free pointer after the object if it is not
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 59bf4d7..69f08cd 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3396,12 +3396,14 @@ void __init skb_init(void)
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
sizeof(struct sk_buff),
0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|
+ SLAB_NO_SANITIZE,
NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
sizeof(struct sk_buff_fclones),
0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|
+ SLAB_NO_SANITIZE,
NULL);
}
diff --git a/security/Kconfig b/security/Kconfig
index e452378..0847880 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -6,6 +6,37 @@ menu "Security options"
source security/keys/Kconfig
+menu "Miscellaneous hardening features"
+
+config PAX_MEMORY_SANITIZE
+ bool "Sanitize the freed memory"
+ default y
+ help
+ By saying Y here the kernel will erase the contents of slab objects
+ as soon as they are freed. This in turn reduces the lifetime of data
+ stored in them, making it less likely that sensitive information such
+ as passwords, cryptographic secrets, etc stay in memory for too long.
+
+ This is especially useful for programs whose runtime is short, long
+ lived processes and the kernel itself benefit from this as long as
+ they ensure timely freeing of memory that may hold sensitive
+ information.
+
+ A nice side effect of the sanitization of slab objects is the
+ reduction of possible info leaks caused by padding bytes within the
+ leaky structures. Use-after-free bugs for structures containing
+ pointers can also be detected as dereferencing the sanitized pointer
+ will generate an access violation.
+
+ The tradeoff is performance impact, on a single CPU system kernel
+ compilation sees a 3% slowdown, other systems and workloads may vary
+ and you are advised to test this feature on your expected workload
+ before deploying it.
+
+ Slab sanitization can be disabled with the kernel commandline
+ parameter "pax_sanitize_slab=0".
+endmenu
+
config SECURITY_DMESG_RESTRICT
bool "Restrict unprivileged access to the kernel syslog"
default n