From: Kees Cook <keescook@chromium.org>
To: kernel-hardening@lists.openwall.com
Cc: John Wood <john.wood@gmx.com>,
Matthew Wilcox <willy@infradead.org>,
Jonathan Corbet <corbet@lwn.net>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Steven Rostedt <rostedt@goodmis.org>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Luis Chamberlain <mcgrof@kernel.org>,
Iurii Zaikin <yzaikin@google.com>,
James Morris <jmorris@namei.org>,
"Serge E. Hallyn" <serge@hallyn.com>,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
Subject: Re: [RFC PATCH 2/6] security/fbfam: Add the api to manage statistics
Date: Thu, 10 Sep 2020 16:23:36 -0700 [thread overview]
Message-ID: <202009101618.2D05F2588@keescook> (raw)
In-Reply-To: <20200910202107.3799376-3-keescook@chromium.org>
On Thu, Sep 10, 2020 at 01:21:03PM -0700, Kees Cook wrote:
> From: John Wood <john.wood@gmx.com>
>
> Create a statistical data structure to hold all the necessary
> information involve in a fork brute force attack. This info is a
> timestamp for the first fork or execve and the number of crashes since
> then. Moreover, due to this statitistical data will be shared between
> different tasks, a reference counter it is necessary.
>
> For a correct management of an attack it is also necessary that all the
> tasks hold statistical data. The same statistical data needs to be
> shared between all the tasks that hold the same memory contents or in
> other words, between all the tasks that have been forked without any
> execve call.
>
> When a forked task calls the execve system call, the memory contents are
> set with new values. So, in this scenario the parent's statistical data
> no need to be share. Instead, a new statistical data structure must be
> allocated to start a new cycle.
>
> The statistical data that every task holds needs to be clear when a task
> exits. Due to this data is shared across multiples tasks, the reference
> counter is useful to free the previous allocated data only when there
> are not other pointers to the same data. Or in other words, when the
> reference counter reaches zero.
>
> So, based in all the previous information add the api to manage all the
> commented cases.
>
> Also, add to the struct task_struct a new field to point to the
> statitistical data related to an attack. This way, all the tasks will
> have access to this information.
>
> Signed-off-by: John Wood <john.wood@gmx.com>
I think patch 1 should be merged into this one since the former doesn't
really *do* anything. ;)
> ---
> include/fbfam/fbfam.h | 18 +++++
> include/linux/sched.h | 4 +
> security/Makefile | 4 +
> security/fbfam/Makefile | 2 +
> security/fbfam/fbfam.c | 163 ++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 191 insertions(+)
> create mode 100644 include/fbfam/fbfam.h
> create mode 100644 security/fbfam/Makefile
> create mode 100644 security/fbfam/fbfam.c
>
> diff --git a/include/fbfam/fbfam.h b/include/fbfam/fbfam.h
> new file mode 100644
> index 000000000000..b5b7d1127a52
> --- /dev/null
> +++ b/include/fbfam/fbfam.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _FBFAM_H_
> +#define _FBFAM_H_
> +
> +#include <linux/sched.h>
> +
> +#ifdef CONFIG_FBFAM
> +int fbfam_fork(struct task_struct *child);
> +int fbfam_execve(void);
> +int fbfam_exit(void);
> +#else
> +static inline int fbfam_fork(struct task_struct *child) { return 0; }
This appears to map well to LSM hook "task_alloc".
> +static inline int fbfam_execve(void) { return 0; }
This appears to map well to LSM hook "bprm_committing_creds".
> +static inline int fbfam_exit(void) { return 0; }
This appears to map well to LSM hook "task_free".
> +#endif
> +
> +#endif /* _FBFAM_H_ */
> +
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index afe01e232935..00e1aa5e00cd 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1315,6 +1315,10 @@ struct task_struct {
> struct callback_head mce_kill_me;
> #endif
>
> +#ifdef CONFIG_FBFAM
> + struct fbfam_stats *fbfam_stats;
> +#endif
This could be part of the task_struct security blob used by LSMs.
> +
> /*
> * New fields for task_struct should be added above here, so that
> * they are included in the randomized portion of task_struct.
> diff --git a/security/Makefile b/security/Makefile
> index 3baf435de541..36dc4b536349 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -36,3 +36,7 @@ obj-$(CONFIG_BPF_LSM) += bpf/
> # Object integrity file lists
> subdir-$(CONFIG_INTEGRITY) += integrity
> obj-$(CONFIG_INTEGRITY) += integrity/
> +
> +# Object fbfam file lists
> +subdir-$(CONFIG_FBFAM) += fbfam
> +obj-$(CONFIG_FBFAM) += fbfam/
> diff --git a/security/fbfam/Makefile b/security/fbfam/Makefile
> new file mode 100644
> index 000000000000..f4b9f0b19c44
> --- /dev/null
> +++ b/security/fbfam/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_FBFAM) += fbfam.o
> diff --git a/security/fbfam/fbfam.c b/security/fbfam/fbfam.c
> new file mode 100644
> index 000000000000..0387f95f6408
> --- /dev/null
> +++ b/security/fbfam/fbfam.c
> @@ -0,0 +1,163 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <asm/current.h>
> +#include <fbfam/fbfam.h>
> +#include <linux/errno.h>
> +#include <linux/gfp.h>
> +#include <linux/jiffies.h>
> +#include <linux/refcount.h>
> +#include <linux/slab.h>
> +
> +/**
> + * struct fbfam_stats - Fork brute force attack mitigation statistics.
> + * @refc: Reference counter.
> + * @faults: Number of crashes since jiffies.
> + * @jiffies: First fork or execve timestamp.
> + *
> + * The purpose of this structure is to manage all the necessary information to
> + * compute the crashing rate of an application. So, it holds a first fork or
> + * execve timestamp and a number of crashes since then. This way the crashing
> + * rate in milliseconds per fault can be compute when necessary with the
> + * following formula:
> + *
> + * u64 delta_jiffies = get_jiffies_64() - fbfam_stats::jiffies;
> + * u64 delta_time = jiffies64_to_msecs(delta_jiffies);
> + * u64 crashing_rate = delta_time / (u64)fbfam_stats::faults;
> + *
> + * If the fbfam_stats::faults is zero, the above formula can't be used. In this
> + * case, the crashing rate is zero.
> + *
> + * Moreover, since the same allocated structure will be used in every fork
> + * since the first one or execve, it's also necessary a reference counter.
> + */
> +struct fbfam_stats {
> + refcount_t refc;
> + unsigned int faults;
> + u64 jiffies;
> +};
> +
> +/**
> + * fbfam_new_stats() - Allocation of new statistics structure.
> + *
> + * If the allocation is successful the reference counter is set to one to
> + * indicate that there will be one task that points to this structure. The
> + * faults field is initialize to zero and the timestamp for this moment is set.
> + *
> + * Return: NULL if the allocation fails. A pointer to the new allocated
> + * statistics structure if it success.
> + */
> +static struct fbfam_stats *fbfam_new_stats(void)
> +{
> + struct fbfam_stats *stats = kmalloc(sizeof(struct fbfam_stats),
> + GFP_KERNEL);
> +
> + if (stats) {
> + refcount_set(&stats->refc, 1);
> + stats->faults = 0;
> + stats->jiffies = get_jiffies_64();
> + }
> +
> + return stats;
> +}
> +
> +/*
> + * fbfam_fork() - Fork management.
> + * @child: Pointer to the child task that will be created with the fork system
> + * call.
> + *
> + * For a correct management of a fork brute force attack it is necessary that
> + * all the tasks hold statistical data. The same statistical data needs to be
> + * shared between all the tasks that hold the same memory contents or in other
> + * words, between all the tasks that have been forked without any execve call.
> + *
> + * To ensure this, if the current task doesn't have statistical data when forks
> + * (only possible in the first fork of the zero task), it is mandatory to
> + * allocate a new one. This way, the child task always will share the statistics
> + * with its parent.
> + *
> + * Return: -ENOMEN if the allocation of the new statistics structure fails.
> + * Zero otherwise.
> + */
> +int fbfam_fork(struct task_struct *child)
> +{
> + struct fbfam_stats **stats = ¤t->fbfam_stats;
> +
> + if (!*stats) {
> + *stats = fbfam_new_stats();
> + if (!*stats)
> + return -ENOMEM;
> + }
> +
> + refcount_inc(&(*stats)->refc);
> + child->fbfam_stats = *stats;
> + return 0;
> +}
> +
> +/**
> + * fbfam_execve() - Execve management.
> + *
> + * When a forked task calls the execve system call, the memory contents are set
> + * with new values. So, in this scenario the parent's statistical data no need
> + * to be share. Instead, a new statistical data structure must be allocated to
> + * start a new cycle. This condition is detected when the statistics reference
> + * counter holds a value greater than or equal to two (a fork always sets the
> + * statistics reference counter to two since the parent and the child task are
> + * sharing the same data).
> + *
> + * However, if the execve function is called immediately after another execve
> + * call, althought the memory contents are reset, there is no need to allocate
> + * a new statistical data structure. This is possible because at this moment
> + * only one task (the task that calls the execve function) points to the data.
> + * In this case, the previous allocation is used and only the faults and time
> + * fields are reset.
> + *
> + * Return: -ENOMEN if the allocation of the new statistics structure fails.
> + * -EFAULT if the current task doesn't have statistical data. Zero
> + * otherwise.
> + */
> +int fbfam_execve(void)
> +{
> + struct fbfam_stats **stats = ¤t->fbfam_stats;
> +
> + if (!*stats)
> + return -EFAULT;
> +
> + if (!refcount_dec_not_one(&(*stats)->refc)) {
> + /* execve call after an execve call */
> + (*stats)->faults = 0;
> + (*stats)->jiffies = get_jiffies_64();
> + return 0;
> + }
> +
> + /* execve call after a fork call */
> + *stats = fbfam_new_stats();
> + if (!*stats)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +/**
> + * fbfam_exit() - Exit management.
> + *
> + * The statistical data that every task holds needs to be clear when a task
> + * exits. Due to this data is shared across multiples tasks, the reference
> + * counter is useful to free the previous allocated data only when there are
> + * not other pointers to the same data. Or in other words, when the reference
> + * counter reaches zero.
> + *
> + * Return: -EFAULT if the current task doesn't have statistical data. Zero
> + * otherwise.
> + */
> +int fbfam_exit(void)
> +{
> + struct fbfam_stats *stats = current->fbfam_stats;
> +
> + if (!stats)
> + return -EFAULT;
> +
> + if (refcount_dec_and_test(&stats->refc))
> + kfree(stats);
> +
> + return 0;
> +}
> +
> --
> 2.25.1
>
Jann mentions some concerns about races, and I'd agree: this doesn't
feel right to me, but I've not had a chance to study it yet. I'm
concerned about the lifetime management of the stats vs the task
hierarchy.
--
Kees Cook
next prev parent reply other threads:[~2020-09-10 23:23 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-10 20:21 [RESEND][RFC PATCH 0/6] Fork brute force attack mitigation (fbfam) Kees Cook
2020-09-10 20:21 ` [RFC PATCH 1/6] security/fbfam: Add a Kconfig to enable the fbfam feature Kees Cook
2020-09-10 21:21 ` Jann Horn
2020-09-17 17:32 ` John Wood
2020-09-10 23:18 ` Kees Cook
2020-09-17 18:40 ` John Wood
2020-09-17 22:05 ` Kees Cook
2020-09-18 14:50 ` John Wood
2020-09-10 20:21 ` [RFC PATCH 2/6] security/fbfam: Add the api to manage statistics Kees Cook
2020-09-10 23:23 ` Kees Cook [this message]
2020-09-10 20:21 ` [RFC PATCH 3/6] security/fbfam: Use " Kees Cook
2020-09-10 20:27 ` Jann Horn
2020-09-10 23:33 ` Kees Cook
2020-09-29 23:47 ` Steven Rostedt
2020-09-29 23:49 ` Steven Rostedt
2020-10-03 9:52 ` John Wood
2020-09-10 20:21 ` [RFC PATCH 4/6] security/fbfam: Add a new sysctl to control the crashing rate threshold Kees Cook
2020-09-10 23:14 ` Kees Cook
2020-09-13 14:33 ` John Wood
2020-09-10 20:21 ` [RFC PATCH 5/6] security/fbfam: Detect a fork brute force attack Kees Cook
2020-09-10 21:10 ` Jann Horn
2020-09-13 17:54 ` John Wood
2020-09-14 19:42 ` Jann Horn
2020-09-15 18:44 ` John Wood
2020-09-10 23:49 ` Kees Cook
2020-09-11 0:01 ` Jann Horn
2020-09-13 16:56 ` John Wood
2020-09-14 19:39 ` Jann Horn
2020-09-15 17:36 ` John Wood
2020-09-10 20:21 ` [RFC PATCH 6/6] security/fbfam: Mitigate " Kees Cook
2020-09-10 20:55 ` Jann Horn
2020-09-10 23:56 ` Kees Cook
2020-09-11 0:20 ` Jann Horn
2020-09-18 16:02 ` John Wood
2020-09-18 21:35 ` Kees Cook
2020-09-19 8:01 ` John Wood
2020-09-10 20:39 ` [RESEND][RFC PATCH 0/6] Fork brute force attack mitigation (fbfam) Jann Horn
2020-09-10 23:58 ` Kees Cook
[not found] ` <20200911144806.GA4128@ubuntu>
[not found] ` <202009120053.9FB7F2A7@keescook>
2020-09-12 12:24 ` John Wood
2020-09-12 0:03 ` James Morris
2020-09-12 7:56 ` Kees Cook
2020-09-12 9:36 ` John Wood
2020-09-12 14:47 ` Mel Gorman
2020-09-12 20:48 ` Ondrej Mosnacek
2020-09-13 7:24 ` John Wood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202009101618.2D05F2588@keescook \
--to=keescook@chromium.org \
--cc=bsegall@google.com \
--cc=corbet@lwn.net \
--cc=dietmar.eggemann@arm.com \
--cc=jmorris@namei.org \
--cc=john.wood@gmx.com \
--cc=juri.lelli@redhat.com \
--cc=kernel-hardening@lists.openwall.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mcgrof@kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=serge@hallyn.com \
--cc=vincent.guittot@linaro.org \
--cc=viro@zeniv.linux.org.uk \
--cc=willy@infradead.org \
--cc=yzaikin@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).