Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1658095 > unrolled thread
| Started by | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| First post | 2017-06-05 21:30 +0200 |
| Last post | 2017-06-06 14:30 +0200 |
| Articles | 20 on this page of 23 — 7 participants |
Back to article view | Back to linux.kernel
(none) Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-05 21:30 +0200
[PATCH 3/5] Protectable Memory Allocator - Debug interface Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-05 21:30 +0200
Re: [kernel-hardening] [PATCH 3/5] Protectable Memory Allocator - Debug interface Jann Horn <jannh@google.com> - 2017-06-05 22:30 +0200
Re: [kernel-hardening] [PATCH 3/5] Protectable Memory Allocator - Debug interface Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 11:10 +0200
[PATCH 4/5] Make LSM Writable Hooks a command line option Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-05 21:30 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Casey Schaufler <casey@schaufler-ca.com> - 2017-06-05 22:00 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2017-06-05 23:00 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 11:10 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2017-06-06 13:00 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 13:20 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2017-06-06 13:50 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 14:20 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2017-06-06 16:40 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 17:00 +0200
Re: [PATCH 4/5] Make LSM Writable Hooks a command line option Casey Schaufler <casey@schaufler-ca.com> - 2017-06-06 17:20 +0200
[PATCH 2/5] Protectable Memory Allocator Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-05 21:30 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> - 2017-06-06 06:50 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Christoph Hellwig <hch@infradead.org> - 2017-06-06 08:30 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 13:40 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Laura Abbott <labbott@redhat.com> - 2017-06-06 18:30 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 13:50 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2017-06-06 14:10 +0200
Re: [PATCH 2/5] Protectable Memory Allocator Igor Stoppa <igor.stoppa@huawei.com> - 2017-06-06 14:30 +0200
Page 1 of 2 [1] 2 Next page →
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-05 21:30 +0200 |
| Subject | (none) |
| Message-ID | <tP5Rn-6ZT-3@gated-at.bofh.it> |
Subject: [RFC v4 PATCH 0/5] NOT FOR MERGE - ro protection for dynamic data
This patchset introduces the possibility of protecting memory that has
been allocated dynamically.
The memory is managed in pools: when a pool is made R/O, all the memory
that is part of it, will become R/O.
A R/O pool can be destroyed to recover its memory, but it cannot be
turned back into R/W mode.
This is intentional and this feature is meant for data that doesn't need
further modifications, after initialization.
An example is provided, showing how to turn into a boot-time option the
writable state of the security hooks.
Prior to this patch, it was a compile-time option.
This is made possible, thanks to Tetsuo Handa's rewor of the hooks
structure (included in the patchset).
Notes:
* I have performed some preliminary test on qemu x86_64 and the changes
seem to hold, but more extensive testing is required.
* I'll be AFK for about a week, so I preferred to share this version, even
if not thoroughly tested, in the hope to get preliminary comments, but
it is rough around the edges.
Igor Stoppa (4):
Protectable Memory Allocator
Protectable Memory Allocator - Debug interface
Make LSM Writable Hooks a command line option
NOT FOR MERGE - Protectable Memory Allocator test
Tetsuo Handa (1):
LSM: Convert security_hook_heads into explicit array of struct
list_head
include/linux/lsm_hooks.h | 412 ++++++++++++++++++++---------------------
include/linux/page-flags.h | 2 +
include/linux/pmalloc.h | 20 ++
include/trace/events/mmflags.h | 1 +
init/main.c | 2 +
mm/Kconfig | 11 ++
mm/Makefile | 4 +-
mm/pmalloc.c | 340 ++++++++++++++++++++++++++++++++++
mm/pmalloc_test.c | 172 +++++++++++++++++
mm/usercopy.c | 24 ++-
security/security.c | 58 ++++--
11 files changed, 814 insertions(+), 232 deletions(-)
create mode 100644 include/linux/pmalloc.h
create mode 100644 mm/pmalloc.c
create mode 100644 mm/pmalloc_test.c
--
2.9.3
[toc] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-05 21:30 +0200 |
| Subject | [PATCH 3/5] Protectable Memory Allocator - Debug interface |
| Message-ID | <tP5Ro-6ZT-13@gated-at.bofh.it> |
| In reply to | #1658095 |
Debugfs interface: it creates a file
/sys/kernel/debug/pmalloc/pools
which exposes statistics about all the pools and memory nodes in use.
Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
---
mm/Kconfig | 11 ++++++
mm/pmalloc.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 124 insertions(+)
diff --git a/mm/Kconfig b/mm/Kconfig
index beb7a45..dfbdc07 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -539,6 +539,17 @@ config CMA_AREAS
If unsure, leave the default value "7".
+config PMALLOC_DEBUG
+ bool "Protectable Memory Allocator debugging"
+ depends on DEBUG_KERNEL
+ default y
+ help
+ Debugfs support for dumping information about memory pools.
+ It shows internal stats: free/used/total space, protection
+ status, data overhead, etc.
+
+ If unsure, say "y".
+
config MEM_SOFT_DIRTY
bool "Track memory changes"
depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
diff --git a/mm/pmalloc.c b/mm/pmalloc.c
index c73d60c..6dd6bbe 100644
--- a/mm/pmalloc.c
+++ b/mm/pmalloc.c
@@ -225,3 +225,116 @@ int __init pmalloc_init(void)
return 0;
}
EXPORT_SYMBOL(pmalloc_init);
+
+#ifdef CONFIG_PMALLOC_DEBUG
+#include <linux/debugfs.h>
+static struct dentry *pmalloc_root;
+
+static void *__pmalloc_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos)
+ return NULL;
+ return pos;
+}
+
+static void *__pmalloc_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return NULL;
+}
+
+static void __pmalloc_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static __always_inline
+void __seq_printf_node(struct seq_file *s, struct pmalloc_node *node)
+{
+ unsigned long total_space, node_pages, end_of_node,
+ used_space, available_space;
+ int total_words, used_words, available_words;
+
+ used_words = atomic_read(&node->used_words);
+ total_words = node->total_words;
+ available_words = total_words - used_words;
+ used_space = used_words * WORD_SIZE;
+ total_space = total_words * WORD_SIZE;
+ available_space = total_space - used_space;
+ node_pages = (total_space + HEADER_SIZE) / PAGE_SIZE;
+ end_of_node = total_space + HEADER_SIZE + (unsigned long) node;
+ seq_printf(s, " - node:\t\t%p\n", node);
+ seq_printf(s, " - start of data ptr:\t%p\n", node->data);
+ seq_printf(s, " - end of node ptr:\t%p\n", (void *)end_of_node);
+ seq_printf(s, " - total words:\t%d\n", total_words);
+ seq_printf(s, " - used words:\t%d\n", used_words);
+ seq_printf(s, " - available words:\t%d\n", available_words);
+ seq_printf(s, " - pages:\t\t%lu\n", node_pages);
+ seq_printf(s, " - total space:\t%lu\n", total_space);
+ seq_printf(s, " - used space:\t%lu\n", used_space);
+ seq_printf(s, " - available space:\t%lu\n", available_space);
+}
+
+static __always_inline
+void __seq_printf_pool(struct seq_file *s, struct pmalloc_pool *pool)
+{
+ struct pmalloc_node *node;
+
+ seq_printf(s, "pool:\t\t\t%p\n", pool);
+ seq_printf(s, " - name:\t\t%s\n", pool->name);
+ seq_printf(s, " - protected:\t\t%u\n", pool->protected);
+ seq_printf(s, " - nodes count:\t\t%u\n",
+ atomic_read(&pool->nodes_count));
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(node, &pool->nodes_list_head, nodes_list)
+ __seq_printf_node(s, node);
+ rcu_read_unlock();
+}
+
+static int __pmalloc_seq_show(struct seq_file *s, void *v)
+{
+ struct pmalloc_pool *pool;
+
+ seq_printf(s, "pools count:\t\t%u\n",
+ atomic_read(&pmalloc_data->pools_count));
+ seq_printf(s, "page size:\t\t%lu\n", PAGE_SIZE);
+ seq_printf(s, "word size:\t\t%lu\n", WORD_SIZE);
+ seq_printf(s, "node header size:\t%lu\n", HEADER_SIZE);
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(pool, &pmalloc_data->pools_list_head,
+ pools_list)
+ __seq_printf_pool(s, pool);
+ rcu_read_unlock();
+ return 0;
+}
+
+static const struct seq_operations pmalloc_seq_ops = {
+ .start = __pmalloc_seq_start,
+ .next = __pmalloc_seq_next,
+ .stop = __pmalloc_seq_stop,
+ .show = __pmalloc_seq_show,
+};
+
+static int __pmalloc_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &pmalloc_seq_ops);
+}
+
+static const struct file_operations pmalloc_file_ops = {
+ .owner = THIS_MODULE,
+ .open = __pmalloc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+
+static int __init __pmalloc_init_track_pool(void)
+{
+ struct dentry *de = NULL;
+
+ pmalloc_root = debugfs_create_dir("pmalloc", NULL);
+ debugfs_create_file("pools", 0644, pmalloc_root, NULL,
+ &pmalloc_file_ops);
+ return 0;
+}
+late_initcall(__pmalloc_init_track_pool);
+#endif
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Jann Horn <jannh@google.com> |
|---|---|
| Date | 2017-06-05 22:30 +0200 |
| Subject | Re: [kernel-hardening] [PATCH 3/5] Protectable Memory Allocator - Debug interface |
| Message-ID | <tP6Nr-7zW-3@gated-at.bofh.it> |
| In reply to | #1658098 |
On Mon, Jun 5, 2017 at 9:22 PM, Igor Stoppa <igor.stoppa@huawei.com> wrote:
> Debugfs interface: it creates a file
>
> /sys/kernel/debug/pmalloc/pools
>
> which exposes statistics about all the pools and memory nodes in use.
>
> Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
[...]
> + seq_printf(s, " - node:\t\t%p\n", node);
> + seq_printf(s, " - start of data ptr:\t%p\n", node->data);
> + seq_printf(s, " - end of node ptr:\t%p\n", (void *)end_of_node);
[...]
> + seq_printf(s, "pool:\t\t\t%p\n", pool);
[...]
> + debugfs_create_file("pools", 0644, pmalloc_root, NULL,
> + &pmalloc_file_ops);
You should probably be using %pK to hide the kernel pointers.
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 11:10 +0200 |
| Subject | Re: [kernel-hardening] [PATCH 3/5] Protectable Memory Allocator - Debug interface |
| Message-ID | <tPiEV-6IR-5@gated-at.bofh.it> |
| In reply to | #1658131 |
On 05/06/17 23:24, Jann Horn wrote: > On Mon, Jun 5, 2017 at 9:22 PM, Igor Stoppa <igor.stoppa@huawei.com> wrote: >> Debugfs interface: it creates a file [...] > You should probably be using %pK to hide the kernel pointers. ok, will do --- igor
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-05 21:30 +0200 |
| Subject | [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tP5Ro-6ZT-11@gated-at.bofh.it> |
| In reply to | #1658095 |
This patch shows how it is possible to take advantage of pmalloc:
instead of using the build-time option __lsm_ro_after_init, to decide if
it is possible to keep the hooks modifiable, now this becomes a
boot-time decision, based on the kernel command line.
This patch relies on:
"Convert security_hook_heads into explicit array of struct list_head"
Author: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
to break free from the static constraint imposed by the previous
hardening model, based on __ro_after_init.
Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
CC: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
init/main.c | 2 ++
security/security.c | 29 ++++++++++++++++++++++++++---
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/init/main.c b/init/main.c
index f866510..7850887 100644
--- a/init/main.c
+++ b/init/main.c
@@ -485,6 +485,7 @@ static void __init mm_init(void)
ioremap_huge_init();
}
+extern int __init pmalloc_init(void);
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
@@ -653,6 +654,7 @@ asmlinkage __visible void __init start_kernel(void)
proc_caches_init();
buffer_init();
key_init();
+ pmalloc_init();
security_init();
dbg_late_init();
vfs_caches_init();
diff --git a/security/security.c b/security/security.c
index c492f68..4285545 100644
--- a/security/security.c
+++ b/security/security.c
@@ -26,6 +26,7 @@
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
+#include <linux/pmalloc.h>
#include <net/flow.h>
#define MAX_LSM_EVM_XATTR 2
@@ -33,8 +34,17 @@
/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10
-static struct list_head hook_heads[LSM_MAX_HOOK_INDEX]
- __lsm_ro_after_init;
+static int security_debug;
+
+static __init int set_security_debug(char *str)
+{
+ get_option(&str, &security_debug);
+ return 0;
+}
+early_param("security_debug", set_security_debug);
+
+static struct list_head *hook_heads;
+static struct pmalloc_pool *sec_pool;
char *lsm_names;
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
@@ -59,6 +69,13 @@ int __init security_init(void)
{
enum security_hook_index i;
+ sec_pool = pmalloc_create_pool("security");
+ if (!sec_pool)
+ goto error_pool;
+ hook_heads = pmalloc(sizeof(struct list_head) * LSM_MAX_HOOK_INDEX,
+ sec_pool);
+ if (!hook_heads)
+ goto error_heads;
for (i = 0; i < LSM_MAX_HOOK_INDEX; i++)
INIT_LIST_HEAD(&hook_heads[i]);
pr_info("Security Framework initialized\n");
@@ -74,8 +91,14 @@ int __init security_init(void)
* Load all the remaining security modules.
*/
do_security_initcalls();
-
+ if (!security_debug)
+ pmalloc_protect_pool(sec_pool);
return 0;
+
+error_heads:
+ pmalloc_destroy_pool(sec_pool);
+error_pool:
+ return -ENOMEM;
}
/* Save user chosen LSM */
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Casey Schaufler <casey@schaufler-ca.com> |
|---|---|
| Date | 2017-06-05 22:00 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tP6kp-7bv-7@gated-at.bofh.it> |
| In reply to | #1658099 |
On 6/5/2017 12:22 PM, Igor Stoppa wrote:
> This patch shows how it is possible to take advantage of pmalloc:
> instead of using the build-time option __lsm_ro_after_init, to decide if
> it is possible to keep the hooks modifiable, now this becomes a
> boot-time decision, based on the kernel command line.
>
> This patch relies on:
>
> "Convert security_hook_heads into explicit array of struct list_head"
> Author: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
>
> to break free from the static constraint imposed by the previous
> hardening model, based on __ro_after_init.
>
> Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
> CC: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> init/main.c | 2 ++
> security/security.c | 29 ++++++++++++++++++++++++++---
> 2 files changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/init/main.c b/init/main.c
> index f866510..7850887 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -485,6 +485,7 @@ static void __init mm_init(void)
> ioremap_huge_init();
> }
>
> +extern int __init pmalloc_init(void);
> asmlinkage __visible void __init start_kernel(void)
> {
> char *command_line;
> @@ -653,6 +654,7 @@ asmlinkage __visible void __init start_kernel(void)
> proc_caches_init();
> buffer_init();
> key_init();
> + pmalloc_init();
> security_init();
> dbg_late_init();
> vfs_caches_init();
> diff --git a/security/security.c b/security/security.c
> index c492f68..4285545 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -26,6 +26,7 @@
> #include <linux/personality.h>
> #include <linux/backing-dev.h>
> #include <linux/string.h>
> +#include <linux/pmalloc.h>
> #include <net/flow.h>
>
> #define MAX_LSM_EVM_XATTR 2
> @@ -33,8 +34,17 @@
> /* Maximum number of letters for an LSM name string */
> #define SECURITY_NAME_MAX 10
>
> -static struct list_head hook_heads[LSM_MAX_HOOK_INDEX]
> - __lsm_ro_after_init;
> +static int security_debug;
> +
> +static __init int set_security_debug(char *str)
> +{
> + get_option(&str, &security_debug);
> + return 0;
> +}
> +early_param("security_debug", set_security_debug);
I don't care for calling this "security debug". Making
the lists writable after init isn't about development,
it's about (Tetsuo's desire for) dynamic module loading.
I would prefer "dynamic_module_lists" our something else
more descriptive.
> +
> +static struct list_head *hook_heads;
> +static struct pmalloc_pool *sec_pool;
> char *lsm_names;
> /* Boot-time LSM user choice */
> static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
> @@ -59,6 +69,13 @@ int __init security_init(void)
> {
> enum security_hook_index i;
>
> + sec_pool = pmalloc_create_pool("security");
> + if (!sec_pool)
> + goto error_pool;
Excessive gotoing - return -ENOMEM instead.
> + hook_heads = pmalloc(sizeof(struct list_head) * LSM_MAX_HOOK_INDEX,
> + sec_pool);
> + if (!hook_heads)
> + goto error_heads;
This is the only case where you'd destroy the pool, so
the goto is unnecessary. Put the
pmalloc_destroy_pool(sec_pool);
return -ENOMEM;
under the if here.
> for (i = 0; i < LSM_MAX_HOOK_INDEX; i++)
> INIT_LIST_HEAD(&hook_heads[i]);
> pr_info("Security Framework initialized\n");
> @@ -74,8 +91,14 @@ int __init security_init(void)
> * Load all the remaining security modules.
> */
> do_security_initcalls();
> -
> + if (!security_debug)
> + pmalloc_protect_pool(sec_pool);
> return 0;
> +
> +error_heads:
> + pmalloc_destroy_pool(sec_pool);
> +error_pool:
> + return -ENOMEM;
> }
>
> /* Save user chosen LSM */
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2017-06-05 23:00 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tP7gt-7Lh-7@gated-at.bofh.it> |
| In reply to | #1658117 |
Casey Schaufler wrote:
> > @@ -33,8 +34,17 @@
> > /* Maximum number of letters for an LSM name string */
> > #define SECURITY_NAME_MAX 10
> >
> > -static struct list_head hook_heads[LSM_MAX_HOOK_INDEX]
> > - __lsm_ro_after_init;
> > +static int security_debug;
> > +
> > +static __init int set_security_debug(char *str)
> > +{
> > + get_option(&str, &security_debug);
> > + return 0;
> > +}
> > +early_param("security_debug", set_security_debug);
>
> I don't care for calling this "security debug". Making
> the lists writable after init isn't about development,
> it's about (Tetsuo's desire for) dynamic module loading.
> I would prefer "dynamic_module_lists" our something else
> more descriptive.
Maybe dynamic_lsm ?
>
> > +
> > +static struct list_head *hook_heads;
> > +static struct pmalloc_pool *sec_pool;
> > char *lsm_names;
> > /* Boot-time LSM user choice */
> > static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
> > @@ -59,6 +69,13 @@ int __init security_init(void)
> > {
> > enum security_hook_index i;
> >
> > + sec_pool = pmalloc_create_pool("security");
> > + if (!sec_pool)
> > + goto error_pool;
>
> Excessive gotoing - return -ENOMEM instead.
But does it make sense to continue?
hook_heads == NULL and we will oops as soon as
call_void_hook() or call_int_hook() is called for the first time.
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 11:10 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPiEW-6IR-29@gated-at.bofh.it> |
| In reply to | #1658146 |
On 05/06/17 23:50, Tetsuo Handa wrote: > Casey Schaufler wrote: [...] >> I don't care for calling this "security debug". Making >> the lists writable after init isn't about development, >> it's about (Tetsuo's desire for) dynamic module loading. >> I would prefer "dynamic_module_lists" our something else >> more descriptive. > > Maybe dynamic_lsm ? ok, apologies for misunderstanding, I'll fix it. I am not sure I understood what exactly the use case is: -1) loading off-tree modules -2) loading and unloading modules -3) something else ? I'm asking this because I now wonder if I should provide means for protecting the heads later on (which still can make sense for case 1). Or if it's expected that things will stay fluid and this dynamic loading is matched by unloading, therefore the heads must stay writable (case 2) [...] >>> + if (!sec_pool) >>> + goto error_pool; >> >> Excessive gotoing - return -ENOMEM instead. > > But does it make sense to continue? > hook_heads == NULL and we will oops as soon as > call_void_hook() or call_int_hook() is called for the first time. Shouldn't the caller check for result? -ENOMEM gives it a chance to do so. I can replace the goto. --- igor
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2017-06-06 13:00 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPkno-7Bq-13@gated-at.bofh.it> |
| In reply to | #1658509 |
Igor Stoppa wrote: > On 05/06/17 23:50, Tetsuo Handa wrote: > > Casey Schaufler wrote: > > [...] > > >> I don't care for calling this "security debug". Making > >> the lists writable after init isn't about development, > >> it's about (Tetsuo's desire for) dynamic module loading. > >> I would prefer "dynamic_module_lists" our something else > >> more descriptive. > > > > Maybe dynamic_lsm ? > > ok, apologies for misunderstanding, I'll fix it. > > I am not sure I understood what exactly the use case is: > -1) loading off-tree modules Does off-tree mean out-of-tree? If yes, this case is not correct. "Loading modules which are not compiled as built-in" is correct. My use case is to allow users to use LSM modules as loadable kernel modules which distributors do not compile as built-in. > -2) loading and unloading modules Unloading LSM modules is dangerous. Only SELinux allows unloading at the risk of triggering an oops. If we insert delay while removing list elements, we can easily observe oops due to free function being called without corresponding allocation function. > -3) something else ? Nothing else, as far as I know. > > I'm asking this because I now wonder if I should provide means for > protecting the heads later on (which still can make sense for case 1). > > Or if it's expected that things will stay fluid and this dynamic loading > is matched by unloading, therefore the heads must stay writable (case 2) > > [...] > > >>> + if (!sec_pool) > >>> + goto error_pool; > >> > >> Excessive gotoing - return -ENOMEM instead. > > > > But does it make sense to continue? > > hook_heads == NULL and we will oops as soon as > > call_void_hook() or call_int_hook() is called for the first time. > > Shouldn't the caller check for result? -ENOMEM gives it a chance to do > so. I can replace the goto. security_init() is called from start_kernel() in init/main.c , and errors are silently ignored. Thus, I don't think returning error to the caller makes sense.
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 13:20 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPkGK-82Q-11@gated-at.bofh.it> |
| In reply to | #1658608 |
On 06/06/17 13:54, Tetsuo Handa wrote: [...] > "Loading modules which are not compiled as built-in" is correct. > My use case is to allow users to use LSM modules as loadable kernel > modules which distributors do not compile as built-in. Ok, so I suppose someone should eventually lock down the header, after the additional modules are loaded. Who decides when enough is enough, meaning that all the needed modules are loaded? Should I provide an interface to user-space? A sysfs entry? [...] > Unloading LSM modules is dangerous. Only SELinux allows unloading > at the risk of triggering an oops. If we insert delay while removing > list elements, we can easily observe oops due to free function being > called without corresponding allocation function. Ok. But even in this case, the sys proposal would still work. It would just stay unused. -- igor
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2017-06-06 13:50 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPl9M-8gs-15@gated-at.bofh.it> |
| In reply to | #1658647 |
Igor Stoppa wrote: > Who decides when enough is enough, meaning that all the needed modules > are loaded? > Should I provide an interface to user-space? A sysfs entry? No such interface is needed. Just an API for applying set_memory_rw() and set_memory_ro() on LSM hooks is enough. security_add_hooks() can call set_memory_rw() before adding hooks and call set_memory_ro() after adding hooks. Ditto for security_delete_hooks() for SELinux's unregistration.
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 14:20 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPlCO-el-5@gated-at.bofh.it> |
| In reply to | #1658675 |
On 06/06/17 14:42, Tetsuo Handa wrote: > Igor Stoppa wrote: >> Who decides when enough is enough, meaning that all the needed modules >> are loaded? >> Should I provide an interface to user-space? A sysfs entry? > > No such interface is needed. Just an API for applying set_memory_rw() > and set_memory_ro() on LSM hooks is enough. > > security_add_hooks() can call set_memory_rw() before adding hooks and > call set_memory_ro() after adding hooks. Ditto for security_delete_hooks() > for SELinux's unregistration. I think this should be considered part of the 2nd phase "write seldom", as we agreed with Kees Cook. Right now the goal was to provide the basic API for: - create pool - get memory from pool - lock the pool - destroy the pool And, behind the scene, verify that a memory range falls into Pmalloc pages. Then would come the "write seldom" part. The reason for this is that a proper implementation of write seldom should, imho, make writable only those pages that really need to be modified. Possibly also add some verification on the call stack about who is requesting the unlocking. Therefore I would feel more comfortable in splitting the work into 2 part. For the case at hand, would it work if there was a non-API call that you could use until the API is properly expanded? -- igor
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2017-06-06 16:40 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPnOi-1Aw-27@gated-at.bofh.it> |
| In reply to | #1658697 |
Igor Stoppa wrote: > For the case at hand, would it work if there was a non-API call that you > could use until the API is properly expanded? Kernel command line switching (i.e. this patch) is fine for my use cases. SELinux folks might want -static int security_debug; +static int security_debug = IS_ENABLED(CONFIG_SECURITY_SELINUX_DISABLE); so that those who are using SELINUX=disabled in /etc/selinux/config won't get oops upon boot by default. If "unlock the pool" were available, SELINUX=enforcing users would be happy. Maybe two modes for rw/ro transition helps. oneway rw -> ro transition mode: can't be made rw again by calling "unlock the pool" API twoway rw <-> ro transition mode: can be made rw again by calling "unlock the pool" API
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 17:00 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPo7D-1Hh-7@gated-at.bofh.it> |
| In reply to | #1658820 |
On 06/06/17 17:36, Tetsuo Handa wrote: > Igor Stoppa wrote: >> For the case at hand, would it work if there was a non-API call that you >> could use until the API is properly expanded? > > Kernel command line switching (i.e. this patch) is fine for my use cases. > > SELinux folks might want > > -static int security_debug; > +static int security_debug = IS_ENABLED(CONFIG_SECURITY_SELINUX_DISABLE); ok, thanks, I will add this > so that those who are using SELINUX=disabled in /etc/selinux/config won't > get oops upon boot by default. If "unlock the pool" were available, > SELINUX=enforcing users would be happy. Maybe two modes for rw/ro transition helps. > > oneway rw -> ro transition mode: can't be made rw again by calling "unlock the pool" API > twoway rw <-> ro transition mode: can be made rw again by calling "unlock the pool" API This was in the first cut of the API, but I was told that it would require further rework, to make it ok for upstream, so we agreed to do first the lockdown/destroy only part and the the rewrite. Is there really a valid use case for unloading SE Linux? Or any other security module. -- igor
[toc] | [prev] | [next] | [standalone]
| From | Casey Schaufler <casey@schaufler-ca.com> |
|---|---|
| Date | 2017-06-06 17:20 +0200 |
| Subject | Re: [PATCH 4/5] Make LSM Writable Hooks a command line option |
| Message-ID | <tPor0-24q-13@gated-at.bofh.it> |
| In reply to | #1658844 |
On 6/6/2017 7:51 AM, Igor Stoppa wrote: > On 06/06/17 17:36, Tetsuo Handa wrote: >> Igor Stoppa wrote: >>> For the case at hand, would it work if there was a non-API call that you >>> could use until the API is properly expanded? >> Kernel command line switching (i.e. this patch) is fine for my use cases. >> >> SELinux folks might want >> >> -static int security_debug; >> +static int security_debug = IS_ENABLED(CONFIG_SECURITY_SELINUX_DISABLE); > ok, thanks, I will add this > >> so that those who are using SELINUX=disabled in /etc/selinux/config won't >> get oops upon boot by default. If "unlock the pool" were available, >> SELINUX=enforcing users would be happy. Maybe two modes for rw/ro transition helps. >> >> oneway rw -> ro transition mode: can't be made rw again by calling "unlock the pool" API >> twoway rw <-> ro transition mode: can be made rw again by calling "unlock the pool" API > This was in the first cut of the API, but I was told that it would > require further rework, to make it ok for upstream, so we agreed to do > first the lockdown/destroy only part and the the rewrite. > > Is there really a valid use case for unloading SE Linux? It's used today in the Redhat distros. There is talk of removing it. You can only unload SELinux before policy is loaded, which is sort of saying that you have your system misconfigured but can't figure out how to fix it. You might be able to convince Paul Moore to accelerate the removal of this feature for this worthy cause. > Or any other security module. I suppose that you could argue that if a security module had been in place for 2 years on a system and had never once denied anyone access it should be removed. That's a reasonable use case description, but I doubt you'd encounter it in the real world. Another possibility is a security module that is used during container setup and once the system goes into full operation is no longer needed. Personally, I don't see either of these cases as compelling. "systemctl restart xyzzyd".
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-05 21:30 +0200 |
| Subject | [PATCH 2/5] Protectable Memory Allocator |
| Message-ID | <tP5Ro-6ZT-25@gated-at.bofh.it> |
| In reply to | #1658095 |
The MMU available in many systems runnign Linux can often provide R/O
protection to the memory pages it handles.
However, this works efficiently only when said pages contain only data
that does not need to be modified.
This can work well for statically allocated variables, however it doe
not fit too well the case of dynamically allocated ones.
Dynamic allocation does not provide, currently, means for grouping
variables in memory pages that would contain exclusively data that can
be made read only.
The allocator here provided (pmalloc - protectable memory allocator)
introduces the concept of pools of protectable memory.
A module can request a pool and then refer any allocation request to the
pool handler it has received.
Once all the memory requested (over various iterations) is initialized,
the pool can be protected.
After this point, the pool can only be destroyed (it is up to the module
to avoid any no further references to the memory from the pool, after
the destruction is invoked).
The latter case is mainly meant for releasing memory when a module is
unloaded.
A module can have as many pools as needed, for example to support the
protection of data that is initialized in sufficiently distinct phases.
Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
---
include/linux/page-flags.h | 2 +
include/linux/pmalloc.h | 20 ++++
include/trace/events/mmflags.h | 1 +
mm/Makefile | 2 +-
mm/pmalloc.c | 227 +++++++++++++++++++++++++++++++++++++++++
mm/usercopy.c | 24 +++--
6 files changed, 266 insertions(+), 10 deletions(-)
create mode 100644 include/linux/pmalloc.h
create mode 100644 mm/pmalloc.c
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 6b5818d..acc0723 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -81,6 +81,7 @@ enum pageflags {
PG_active,
PG_waiters, /* Page has waiters, check its waitqueue. Must be bit #7 and in the same byte as "PG_locked" */
PG_slab,
+ PG_pmalloc,
PG_owner_priv_1, /* Owner use. If pagecache, fs may use*/
PG_arch_1,
PG_reserved,
@@ -274,6 +275,7 @@ PAGEFLAG(Active, active, PF_HEAD) __CLEARPAGEFLAG(Active, active, PF_HEAD)
TESTCLEARFLAG(Active, active, PF_HEAD)
__PAGEFLAG(Slab, slab, PF_NO_TAIL)
__PAGEFLAG(SlobFree, slob_free, PF_NO_TAIL)
+__PAGEFLAG(Pmalloc, pmalloc, PF_NO_TAIL)
PAGEFLAG(Checked, checked, PF_NO_COMPOUND) /* Used by some filesystems */
/* Xen */
diff --git a/include/linux/pmalloc.h b/include/linux/pmalloc.h
new file mode 100644
index 0000000..83d3557
--- /dev/null
+++ b/include/linux/pmalloc.h
@@ -0,0 +1,20 @@
+/*
+ * pmalloc.h: Header for Protectable Memory Allocator
+ *
+ * (C) Copyright 2017 Huawei Technologies Co. Ltd.
+ * Author: Igor Stoppa <igor.stoppa@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef _PMALLOC_H
+#define _PMALLOC_H
+
+struct pmalloc_pool *pmalloc_create_pool(const char *name);
+void *pmalloc(unsigned long size, struct pmalloc_pool *pool);
+int pmalloc_protect_pool(struct pmalloc_pool *pool);
+int pmalloc_destroy_pool(struct pmalloc_pool *pool);
+#endif
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index 304ff94..41d1587 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -91,6 +91,7 @@
{1UL << PG_lru, "lru" }, \
{1UL << PG_active, "active" }, \
{1UL << PG_slab, "slab" }, \
+ {1UL << PG_pmalloc, "pmalloc" }, \
{1UL << PG_owner_priv_1, "owner_priv_1" }, \
{1UL << PG_arch_1, "arch_1" }, \
{1UL << PG_reserved, "reserved" }, \
diff --git a/mm/Makefile b/mm/Makefile
index 026f6a8..79dd99c 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -25,7 +25,7 @@ mmu-y := nommu.o
mmu-$(CONFIG_MMU) := gup.o highmem.o memory.o mincore.o \
mlock.o mmap.o mprotect.o mremap.o msync.o \
page_vma_mapped.o pagewalk.o pgtable-generic.o \
- rmap.o vmalloc.o
+ rmap.o vmalloc.o pmalloc.o
ifdef CONFIG_CROSS_MEMORY_ATTACH
diff --git a/mm/pmalloc.c b/mm/pmalloc.c
new file mode 100644
index 0000000..c73d60c
--- /dev/null
+++ b/mm/pmalloc.c
@@ -0,0 +1,227 @@
+/*
+ * pmalloc.c: Protectable Memory Allocator
+ *
+ * (C) Copyright 2017 Huawei Technologies Co. Ltd.
+ * Author: Igor Stoppa <igor.stoppa@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/printk.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <asm/set_memory.h>
+#include <asm/page.h>
+
+typedef uint64_t align_t;
+#define WORD_SIZE sizeof(align_t)
+
+#define __PMALLOC_ALIGNED __aligned(WORD_SIZE)
+
+#define MAX_POOL_NAME_LEN 40
+
+#define PMALLOC_HASH_SIZE (PAGE_SIZE / 2)
+
+#define PMALLOC_HASH_ENTRIES ilog2(PMALLOC_HASH_SIZE)
+
+
+struct pmalloc_data {
+ struct hlist_head pools_list_head;
+ struct mutex pools_list_mutex;
+ atomic_t pools_count;
+};
+
+struct pmalloc_pool {
+ struct hlist_node pools_list;
+ struct hlist_head nodes_list_head;
+ struct mutex nodes_list_mutex;
+ atomic_t nodes_count;
+ bool protected;
+ char name[MAX_POOL_NAME_LEN];
+};
+
+struct pmalloc_node {
+ struct hlist_node nodes_list;
+ atomic_t used_words;
+ unsigned int total_words;
+ __PMALLOC_ALIGNED align_t data[];
+};
+
+#define HEADER_SIZE sizeof(struct pmalloc_node)
+
+static struct pmalloc_data *pmalloc_data;
+
+struct pmalloc_node *__pmalloc_create_node(int words)
+{
+ struct pmalloc_node *node;
+ unsigned long size, i, pages;
+ struct page *p;
+
+ size = ((HEADER_SIZE - 1 + PAGE_SIZE) +
+ WORD_SIZE * (unsigned long) words) & PAGE_MASK;
+ node = vmalloc(size);
+ if (!node)
+ return NULL;
+ atomic_set(&node->used_words, 0);
+ node->total_words = (size - HEADER_SIZE) / WORD_SIZE;
+ pages = size / PAGE_SIZE;
+ for (i = 0; i < pages; i++) {
+ p = vmalloc_to_page((void *)(i * PAGE_SIZE +
+ (unsigned long)node));
+ __SetPagePmalloc(p);
+ }
+ return node;
+}
+
+void *pmalloc(unsigned long size, struct pmalloc_pool *pool)
+{
+ struct pmalloc_node *node;
+ int req_words;
+ int starting_word;
+
+ if (size > INT_MAX || size == 0)
+ return NULL;
+ req_words = (((int)size) + WORD_SIZE - 1) / WORD_SIZE;
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(node, &pool->nodes_list_head, nodes_list) {
+ starting_word = atomic_fetch_add(req_words, &node->used_words);
+ if (starting_word + req_words > node->total_words)
+ atomic_sub(req_words, &node->used_words);
+ else
+ goto found_node;
+ }
+ rcu_read_unlock();
+ node = __pmalloc_create_node(req_words);
+ starting_word = atomic_fetch_add(req_words, &node->used_words);
+ mutex_lock(&pool->nodes_list_mutex);
+ hlist_add_head_rcu(&node->nodes_list, &pool->nodes_list_head);
+ mutex_unlock(&pool->nodes_list_mutex);
+ atomic_inc(&pool->nodes_count);
+found_node:
+ return node->data + starting_word;
+}
+
+const char msg[] = "Not a valid Pmalloc object.";
+const char *__pmalloc_check_object(const void *ptr, unsigned long n)
+{
+ unsigned long p;
+
+ p = (unsigned long)ptr;
+ n += (unsigned long)ptr;
+ for (; (PAGE_MASK & p) <= (PAGE_MASK & n); p += PAGE_SIZE) {
+ if (is_vmalloc_addr((void *)p)) {
+ struct page *page;
+
+ page = vmalloc_to_page((void *)p);
+ if (!(page && PagePmalloc(page)))
+ return msg;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(__pmalloc_check_object);
+
+
+struct pmalloc_pool *pmalloc_create_pool(const char *name)
+{
+ struct pmalloc_pool *pool;
+ unsigned int name_len;
+
+ name_len = strnlen(name, MAX_POOL_NAME_LEN);
+ if (unlikely(name_len == MAX_POOL_NAME_LEN))
+ return NULL;
+ pool = vmalloc(sizeof(struct pmalloc_pool));
+ if (unlikely(!pool))
+ return NULL;
+ INIT_HLIST_NODE(&pool->pools_list);
+ INIT_HLIST_HEAD(&pool->nodes_list_head);
+ mutex_init(&pool->nodes_list_mutex);
+ atomic_set(&pool->nodes_count, 0);
+ pool->protected = false;
+ strcpy(pool->name, name);
+ mutex_lock(&pmalloc_data->pools_list_mutex);
+ hlist_add_head_rcu(&pool->pools_list, &pmalloc_data->pools_list_head);
+ mutex_unlock(&pmalloc_data->pools_list_mutex);
+ atomic_inc(&pmalloc_data->pools_count);
+ return pool;
+}
+
+int pmalloc_protect_pool(struct pmalloc_pool *pool)
+{
+ struct pmalloc_node *node;
+
+ if (!pool)
+ return -EINVAL;
+ mutex_lock(&pool->nodes_list_mutex);
+ hlist_for_each_entry(node, &pool->nodes_list_head, nodes_list) {
+ unsigned long size, pages;
+
+ size = WORD_SIZE * node->total_words + HEADER_SIZE;
+ pages = size / PAGE_SIZE;
+ set_memory_ro((unsigned long)node, pages);
+ }
+ pool->protected = true;
+ mutex_unlock(&pool->nodes_list_mutex);
+ return 0;
+}
+
+static __always_inline
+void __pmalloc_destroy_node(struct pmalloc_node *node)
+{
+ int pages, i;
+
+ pages = (node->total_words * WORD_SIZE + HEADER_SIZE) / PAGE_SIZE;
+ for (i = 0; i < pages; i++)
+ __ClearPagePmalloc(vmalloc_to_page(node + i * PAGE_SIZE));
+ vfree(node);
+}
+
+int pmalloc_destroy_pool(struct pmalloc_pool *pool)
+{
+ struct pmalloc_node *node;
+
+ if (!pool)
+ return -EINVAL;
+ mutex_lock(&pool->nodes_list_mutex);
+ mutex_lock(&pmalloc_data->pools_list_mutex);
+ hlist_del_rcu(&pool->pools_list);
+ mutex_unlock(&pmalloc_data->pools_list_mutex);
+ hlist_for_each_entry_rcu(node, &pool->nodes_list_head, nodes_list) {
+ int pages;
+
+ pages = (node->total_words * WORD_SIZE + HEADER_SIZE) /
+ PAGE_SIZE;
+ set_memory_rw((unsigned long)node, pages);
+ }
+
+ while (likely(!hlist_empty(&pool->nodes_list_head))) {
+ node = hlist_entry(pool->nodes_list_head.first,
+ struct pmalloc_node, nodes_list);
+ hlist_del(&node->nodes_list);
+ __pmalloc_destroy_node(node);
+ }
+ mutex_unlock(&pool->nodes_list_mutex);
+ atomic_dec(&pmalloc_data->pools_count);
+ vfree(pool);
+ return 0;
+}
+
+int __init pmalloc_init(void)
+{
+ pmalloc_data = vmalloc(sizeof(struct pmalloc_data));
+ if (!pmalloc_data)
+ return -ENOMEM;
+ INIT_HLIST_HEAD(&pmalloc_data->pools_list_head);
+ mutex_init(&pmalloc_data->pools_list_mutex);
+ atomic_set(&pmalloc_data->pools_count, 0);
+ return 0;
+}
+EXPORT_SYMBOL(pmalloc_init);
diff --git a/mm/usercopy.c b/mm/usercopy.c
index a9852b2..29bb691 100644
--- a/mm/usercopy.c
+++ b/mm/usercopy.c
@@ -195,22 +195,28 @@ static inline const char *check_page_span(const void *ptr, unsigned long n,
return NULL;
}
+extern const char *__pmalloc_check_object(const void *ptr, unsigned long n);
+
static inline const char *check_heap_object(const void *ptr, unsigned long n,
bool to_user)
{
struct page *page;
- if (!virt_addr_valid(ptr))
- return NULL;
-
- page = virt_to_head_page(ptr);
-
- /* Check slab allocator for flags and size. */
- if (PageSlab(page))
- return __check_heap_object(ptr, n, page);
+ if (virt_addr_valid(ptr)) {
+ page = virt_to_head_page(ptr);
+ /* Check slab allocator for flags and size. */
+ if (PageSlab(page))
+ return __check_heap_object(ptr, n, page);
/* Verify object does not incorrectly span multiple pages. */
- return check_page_span(ptr, n, page, to_user);
+ return check_page_span(ptr, n, page, to_user);
+ }
+ if (likely(is_vmalloc_addr(ptr))) {
+ page = vmalloc_to_page(ptr);
+ if (unlikely(page && PagePmalloc(page)))
+ return __pmalloc_check_object(ptr, n);
+ }
+ return NULL;
}
/*
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> |
|---|---|
| Date | 2017-06-06 06:50 +0200 |
| Subject | Re: [PATCH 2/5] Protectable Memory Allocator |
| Message-ID | <tPeBj-42I-1@gated-at.bofh.it> |
| In reply to | #1658104 |
Igor Stoppa wrote:
> +int pmalloc_protect_pool(struct pmalloc_pool *pool)
> +{
> + struct pmalloc_node *node;
> +
> + if (!pool)
> + return -EINVAL;
> + mutex_lock(&pool->nodes_list_mutex);
> + hlist_for_each_entry(node, &pool->nodes_list_head, nodes_list) {
> + unsigned long size, pages;
> +
> + size = WORD_SIZE * node->total_words + HEADER_SIZE;
> + pages = size / PAGE_SIZE;
> + set_memory_ro((unsigned long)node, pages);
> + }
> + pool->protected = true;
> + mutex_unlock(&pool->nodes_list_mutex);
> + return 0;
> +}
As far as I know, not all CONFIG_MMU=y architectures provide
set_memory_ro()/set_memory_rw(). You need to provide fallback for
architectures which do not provide set_memory_ro()/set_memory_rw()
or kernels built with CONFIG_MMU=n.
> mmu-$(CONFIG_MMU) := gup.o highmem.o memory.o mincore.o \
> mlock.o mmap.o mprotect.o mremap.o msync.o \
> page_vma_mapped.o pagewalk.o pgtable-generic.o \
> - rmap.o vmalloc.o
> + rmap.o vmalloc.o pmalloc.o
Is this __PMALLOC_ALIGNED needed? Why not use "long" and "BITS_PER_LONG" ?
> +struct pmalloc_node {
> + struct hlist_node nodes_list;
> + atomic_t used_words;
> + unsigned int total_words;
> + __PMALLOC_ALIGNED align_t data[];
> +};
Please use macros for round up/down.
> + size = ((HEADER_SIZE - 1 + PAGE_SIZE) +
> + WORD_SIZE * (unsigned long) words) & PAGE_MASK;
> + req_words = (((int)size) + WORD_SIZE - 1) / WORD_SIZE;
You need to check for node != NULL before dereference it.
Also, why rcu_read_lock()/rcu_read_unlock() ?
I can't find corresponding synchronize_rcu() etc. in this patch.
pmalloc() won't be hotpath. Enclosing whole using a mutex might be OK.
If any reason to use rcu, rcu_read_unlock() is missing if came from "goto".
+void *pmalloc(unsigned long size, struct pmalloc_pool *pool)
+{
+ struct pmalloc_node *node;
+ int req_words;
+ int starting_word;
+
+ if (size > INT_MAX || size == 0)
+ return NULL;
+ req_words = (((int)size) + WORD_SIZE - 1) / WORD_SIZE;
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(node, &pool->nodes_list_head, nodes_list) {
+ starting_word = atomic_fetch_add(req_words, &node->used_words);
+ if (starting_word + req_words > node->total_words)
+ atomic_sub(req_words, &node->used_words);
+ else
+ goto found_node;
+ }
+ rcu_read_unlock();
+ node = __pmalloc_create_node(req_words);
+ starting_word = atomic_fetch_add(req_words, &node->used_words);
+ mutex_lock(&pool->nodes_list_mutex);
+ hlist_add_head_rcu(&node->nodes_list, &pool->nodes_list_head);
+ mutex_unlock(&pool->nodes_list_mutex);
+ atomic_inc(&pool->nodes_count);
+found_node:
+ return node->data + starting_word;
+}
I feel that n is off-by-one if (ptr + n) % PAGE_SIZE == 0
according to check_page_span().
> +const char *__pmalloc_check_object(const void *ptr, unsigned long n)
> +{
> + unsigned long p;
> +
> + p = (unsigned long)ptr;
> + n += (unsigned long)ptr;
> + for (; (PAGE_MASK & p) <= (PAGE_MASK & n); p += PAGE_SIZE) {
> + if (is_vmalloc_addr((void *)p)) {
> + struct page *page;
> +
> + page = vmalloc_to_page((void *)p);
> + if (!(page && PagePmalloc(page)))
> + return msg;
> + }
> + }
> + return NULL;
> +}
Why need to call pmalloc_init() from loadable kernel module?
It has to be called very early stage of boot for only once.
> +int __init pmalloc_init(void)
> +{
> + pmalloc_data = vmalloc(sizeof(struct pmalloc_data));
> + if (!pmalloc_data)
> + return -ENOMEM;
> + INIT_HLIST_HEAD(&pmalloc_data->pools_list_head);
> + mutex_init(&pmalloc_data->pools_list_mutex);
> + atomic_set(&pmalloc_data->pools_count, 0);
> + return 0;
> +}
> +EXPORT_SYMBOL(pmalloc_init);
Since pmalloc_data is a globally shared variable, why need to
allocate it dynamically? If it is for randomizing the address
of pmalloc_data, it does not make sense to continue because
vmalloc() failure causes subsequent oops.
[toc] | [prev] | [next] | [standalone]
| From | Christoph Hellwig <hch@infradead.org> |
|---|---|
| Date | 2017-06-06 08:30 +0200 |
| Subject | Re: [PATCH 2/5] Protectable Memory Allocator |
| Message-ID | <tPga6-55F-13@gated-at.bofh.it> |
| In reply to | #1658340 |
On Tue, Jun 06, 2017 at 01:44:32PM +0900, Tetsuo Handa wrote:
> Igor Stoppa wrote:
> > +int pmalloc_protect_pool(struct pmalloc_pool *pool)
> > +{
> > + struct pmalloc_node *node;
> > +
> > + if (!pool)
> > + return -EINVAL;
> > + mutex_lock(&pool->nodes_list_mutex);
> > + hlist_for_each_entry(node, &pool->nodes_list_head, nodes_list) {
> > + unsigned long size, pages;
> > +
> > + size = WORD_SIZE * node->total_words + HEADER_SIZE;
> > + pages = size / PAGE_SIZE;
> > + set_memory_ro((unsigned long)node, pages);
> > + }
> > + pool->protected = true;
> > + mutex_unlock(&pool->nodes_list_mutex);
> > + return 0;
> > +}
>
> As far as I know, not all CONFIG_MMU=y architectures provide
> set_memory_ro()/set_memory_rw(). You need to provide fallback for
> architectures which do not provide set_memory_ro()/set_memory_rw()
> or kernels built with CONFIG_MMU=n.
I think we'll just need to generalize CONFIG_STRICT_MODULE_RWX and/or
ARCH_HAS_STRICT_MODULE_RWX so there is a symbol to key this off.
[toc] | [prev] | [next] | [standalone]
| From | Igor Stoppa <igor.stoppa@huawei.com> |
|---|---|
| Date | 2017-06-06 13:40 +0200 |
| Subject | Re: [PATCH 2/5] Protectable Memory Allocator |
| Message-ID | <tPl06-8cX-19@gated-at.bofh.it> |
| In reply to | #1658386 |
On 06/06/17 09:25, Christoph Hellwig wrote: > On Tue, Jun 06, 2017 at 01:44:32PM +0900, Tetsuo Handa wrote: [..] >> As far as I know, not all CONFIG_MMU=y architectures provide >> set_memory_ro()/set_memory_rw(). You need to provide fallback for >> architectures which do not provide set_memory_ro()/set_memory_rw() >> or kernels built with CONFIG_MMU=n. > > I think we'll just need to generalize CONFIG_STRICT_MODULE_RWX and/or > ARCH_HAS_STRICT_MODULE_RWX so there is a symbol to key this off. Would STRICT_KERNEL_RWX work? It's already present. If both kernel text and rodata can be protected, so can pmalloc data. --- igor
[toc] | [prev] | [next] | [standalone]
| From | Laura Abbott <labbott@redhat.com> |
|---|---|
| Date | 2017-06-06 18:30 +0200 |
| Subject | Re: [PATCH 2/5] Protectable Memory Allocator |
| Message-ID | <tPpwK-2JZ-21@gated-at.bofh.it> |
| In reply to | #1658660 |
On 06/06/2017 04:34 AM, Igor Stoppa wrote: > On 06/06/17 09:25, Christoph Hellwig wrote: >> On Tue, Jun 06, 2017 at 01:44:32PM +0900, Tetsuo Handa wrote: > > [..] > >>> As far as I know, not all CONFIG_MMU=y architectures provide >>> set_memory_ro()/set_memory_rw(). You need to provide fallback for >>> architectures which do not provide set_memory_ro()/set_memory_rw() >>> or kernels built with CONFIG_MMU=n. >> >> I think we'll just need to generalize CONFIG_STRICT_MODULE_RWX and/or >> ARCH_HAS_STRICT_MODULE_RWX so there is a symbol to key this off. > > Would STRICT_KERNEL_RWX work? It's already present. > If both kernel text and rodata can be protected, so can pmalloc data. > > --- > igor > > -- > To unsubscribe, send a message with 'unsubscribe linux-mm' in > the body to majordomo@kvack.org. For more info on Linux MM, > see: http://www.linux-mm.org/ . > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> > There's already ARCH_HAS_SET_MEMORY for this purpose. Thanks, Laura
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web