* Re: [PATCH 9/9] integrity: check properly whether EFI GetVariable() is available
From: Serge E. Hallyn @ 2020-02-20 3:19 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: Serge E. Hallyn, linux-efi, Leif Lindholm, Peter Jones,
Alexander Graf, Heinrich Schuchardt, Jeff Brasen, Atish Patra,
the arch/x86 maintainers, James Morris, linux-security-module
In-Reply-To: <CAKv+Gu_c4mhMN5LBoH5jJWwMHaMxKY7zcp4hiqdRFiadPT8Nww@mail.gmail.com>
On Wed, Feb 19, 2020 at 10:00:11PM +0100, Ard Biesheuvel wrote:
> On Wed, 19 Feb 2020 at 21:46, Serge E. Hallyn <serge@hallyn.com> wrote:
> >
> > On Wed, Feb 19, 2020 at 06:19:07PM +0100, Ard Biesheuvel wrote:
> > > Testing the value of the efi.get_variable function pointer is not
> > > the right way to establish whether the platform supports EFI
> > > variables at runtime. Instead, use the newly added granular check
> > > that can test for the presence of each EFI runtime service
> > > individually.
> > >
> > > Cc: James Morris <jmorris@namei.org>
> > > Cc: "Serge E. Hallyn" <serge@hallyn.com>
> > > Cc: linux-security-module@vger.kernel.org
> > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > ---
> > > security/integrity/platform_certs/load_uefi.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
> > > index 111898aad56e..e2fe1bd3abb9 100644
> > > --- a/security/integrity/platform_certs/load_uefi.c
> > > +++ b/security/integrity/platform_certs/load_uefi.c
> > > @@ -76,7 +76,7 @@ static int __init load_uefi_certs(void)
> > > unsigned long dbsize = 0, dbxsize = 0, moksize = 0;
> > > int rc = 0;
> > >
> > > - if (!efi.get_variable)
> > > + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
> >
> > Sorry, where is this defined?
> >
>
> Apologies, I failed to cc everyone on the whole series.
>
> It is defined in the first patch.
>
> https://lore.kernel.org/linux-efi/20200219171907.11894-1-ardb@kernel.org/
Gotcha, thanks, I shoulda get-lore-mbox'ed it :)
Acked-by: Serge Hallyn <serge@hallyn.com>
thanks,
-serge
^ permalink raw reply
* Re: [PATCH v5 01/10] capabilities: introduce CAP_PERFMON to kernel and user space
From: Alexey Budankov @ 2020-02-20 13:05 UTC (permalink / raw)
To: Thomas Gleixner, Stephen Smalley, Serge Hallyn, James Morris
Cc: Alexei Starovoitov, Peter Zijlstra, Arnaldo Carvalho de Melo,
Ingo Molnar, jani.nikula@linux.intel.com,
joonas.lahtinen@linux.intel.com, rodrigo.vivi@intel.com,
benh@kernel.crashing.org, Paul Mackerras, Michael Ellerman,
Will Deacon, Mark Rutland, Robert Richter, Alexei Starovoitov,
Jiri Olsa, Andi Kleen, Stephane Eranian, Igor Lubashev,
Alexander Shishkin, Namhyung Kim, Song Liu, Lionel Landwerlin,
linux-kernel, linux-security-module@vger.kernel.org,
selinux@vger.kernel.org, intel-gfx@lists.freedesktop.org,
linux-parisc@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel, linux-perf-users@vger.kernel.org, oprofile-list,
Andy Lutomirski
In-Reply-To: <7d6f4210-423f-e454-3910-9f8e17dff1aa@linux.intel.com>
On 07.02.2020 16:39, Alexey Budankov wrote:
>
> On 07.02.2020 14:38, Thomas Gleixner wrote:
>> Alexey Budankov <alexey.budankov@linux.intel.com> writes:
>>> On 22.01.2020 17:25, Alexey Budankov wrote:
>>>> On 22.01.2020 17:07, Stephen Smalley wrote:
>>>>>> It keeps the implementation simple and readable. The implementation is more
>>>>>> performant in the sense of calling the API - one capable() call for CAP_PERFMON
>>>>>> privileged process.
>>>>>>
>>>>>> Yes, it bloats audit log for CAP_SYS_ADMIN privileged and unprivileged processes,
>>>>>> but this bloating also advertises and leverages using more secure CAP_PERFMON
>>>>>> based approach to use perf_event_open system call.
>>>>>
>>>>> I can live with that. We just need to document that when you see
>>>>> both a CAP_PERFMON and a CAP_SYS_ADMIN audit message for a process,
>>>>> try only allowing CAP_PERFMON first and see if that resolves the
>>>>> issue. We have a similar issue with CAP_DAC_READ_SEARCH versus
>>>>> CAP_DAC_OVERRIDE.
>>>>
>>>> perf security [1] document can be updated, at least, to align and document
>>>> this audit logging specifics.
>>>
>>> And I plan to update the document right after this patch set is accepted.
>>> Feel free to let me know of the places in the kernel docs that also
>>> require update w.r.t CAP_PERFMON extension.
>>
>> The documentation update wants be part of the patch set and not planned
>> to be done _after_ the patch set is merged.
>
> Well, accepted. It is going to make patches #11 and beyond.
Patches #11 and #12 of v7 [1] contain information on CAP_PERFMON intention and usage.
Patch for man-pages [2] extends perf_event_open.2 documentation.
Thanks,
Alexey
---
[1] https://lore.kernel.org/lkml/c8de937a-0b3a-7147-f5ef-69f467e87a13@linux.intel.com/
[2] https://lore.kernel.org/lkml/18d1083d-efe5-f5f8-c531-d142c0e5c1a8@linux.intel.com/
^ permalink raw reply
* Re: [PATCH v3 09/25] fs: add is_userns_visible() helper
From: Serge E. Hallyn @ 2020-02-20 14:26 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Christian Brauner, Serge E. Hallyn, Stéphane Graber,
Eric W. Biederman, Aleksa Sarai, Jann Horn, smbarber,
Seth Forshee, Alexander Viro, Alexey Dobriyan, James Morris,
Kees Cook, Jonathan Corbet, Phil Estes, LKML, Linux FS Devel,
Linux Containers, LSM List, Linux API
In-Reply-To: <CALCETrW-XPkBMs30vk+Aiv+jA5i7TjHOYCgz0Ud6d0geaYte=g@mail.gmail.com>
On Wed, Feb 19, 2020 at 09:18:51AM -0800, Andy Lutomirski wrote:
> On Wed, Feb 19, 2020 at 4:06 AM Christian Brauner
> <christian.brauner@ubuntu.com> wrote:
> >
> > On Tue, Feb 18, 2020 at 08:42:33PM -0600, Serge Hallyn wrote:
> > > On Tue, Feb 18, 2020 at 03:33:55PM +0100, Christian Brauner wrote:
> > > > Introduce a helper which makes it possible to detect fileystems whose
> > > > superblock is visible in multiple user namespace. This currently only
> > > > means proc and sys. Such filesystems usually have special semantics so their
> > > > behavior will not be changed with the introduction of fsid mappings.
> > >
> > > Hi,
> > >
> > > I'm afraid I've got a bit of a hangup about the terminology here. I
> > > *think* what you mean is that SB_I_USERNS_VISIBLE is an fs whose uids are
> > > always translated per the id mappings, not fsid mappings. But when I see
> >
> > Correct!
> >
> > > the name it seems to imply that !SB_I_USERNS_VISIBLE filesystems can't
> > > be seen by other namespaces at all.
> > >
> > > Am I right in my first interpretation? If so, can we talk about the
> > > naming?
> >
> > Yep, your first interpretation is right. What about: wants_idmaps()
>
> Maybe fsidmap_exempt()?
Yeah, and maybe SB_USERNS_FSID_EXEMPT ?
> I still haven't convinced myself that any of the above is actually
> correct behavior, especially when people do things like creating
> setuid binaries.
The only place that would be a problem is if the child userns has an
fsidmapping from X to 0 in the parent userns, right? Yeah I'm sure
many people would ignore all advice to the contrary and do this anyway,
but I would try hard to suggest that people use an intermediary userns
for storing filesystems for the "docker share" case. So the host fsid
range would start at say 200000. So a setuid binary would just be
setuid-200000.
^ permalink raw reply
* [PATCH] lockdown: Allow unprivileged users to see lockdown status
From: Jeremy Cline @ 2020-02-20 15:17 UTC (permalink / raw)
To: James Morris, Serge E . Hallyn
Cc: Matthew Garrett, David Howells, linux-security-module,
linux-kernel, Jeremy Cline, Frank Ch . Eigler
A number of userspace tools, such as systemtap, need a way to see the
current lockdown state so they can gracefully deal with the kernel being
locked down. The state is already exposed in
/sys/kernel/security/lockdown, but is only readable by root. Adjust the
permissions so unprivileged users can read the state.
Fixes: 000d388ed3bb ("security: Add a static lockdown policy LSM")
Cc: Frank Ch. Eigler <fche@redhat.com>
Signed-off-by: Jeremy Cline <jcline@redhat.com>
---
security/lockdown/lockdown.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index 5a952617a0eb..87cbdc64d272 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -150,7 +150,7 @@ static int __init lockdown_secfs_init(void)
{
struct dentry *dentry;
- dentry = securityfs_create_file("lockdown", 0600, NULL, NULL,
+ dentry = securityfs_create_file("lockdown", 0644, NULL, NULL,
&lockdown_ops);
return PTR_ERR_OR_ZERO(dentry);
}
--
2.24.1
^ permalink raw reply related
* [PATCH bpf-next v4 0/8] MAC and Audit policy using eBPF (KRSI)
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
From: KP Singh <kpsingh@google.com>
# v3 -> v4
https://lkml.org/lkml/2020/1/23/515
* Moved away from allocating a separate security_hook_heads and adding a
new special case for arch_prepare_bpf_trampoline to using BPF fexit
trampolines called from the right place in the LSM hook and toggled by
static keys based on the discussion in:
https://lore.kernel.org/bpf/CAG48ez25mW+_oCxgCtbiGMX07g_ph79UOJa07h=o_6B6+Q-u5g@mail.gmail.com/
* Since the code does not deal with security_hook_heads anymore, it goes
from "being a BPF LSM" to "BPF program attachment to LSM hooks".
* Added a new test case which ensures that the BPF programs' return value
is reflected by the LSM hook.
# v2 -> v3 does not change the overall design and has some minor fixes:
https://lkml.org/lkml/2020/1/15/843
* LSM_ORDER_LAST is introduced to represent the behaviour of the BPF LSM
* Fixed the inadvertent clobbering of the LSM Hook error codes
* Added GPL license requirement to the commit log
* The lsm_hook_idx is now the more conventional 0-based index
* Some changes were split into a separate patch ("Load btf_vmlinux only
once per object")
https://lore.kernel.org/bpf/20200117212825.11755-1-kpsingh@chromium.org/
* Addressed Andrii's feedback on the BTF implementation
* Documentation update for using generated vmlinux.h to simplify
programs
* Rebase
# Changes since v1:
https://lkml.org/lkml/2019/12/20/641
* Eliminate the requirement to maintain LSM hooks separately in
security/bpf/hooks.h Use BPF trampolines to dynamically allocate
security hooks
* Drop the use of securityfs as bpftool provides the required
introspection capabilities. Update the tests to use the bpf_skeleton
and global variables
* Use O_CLOEXEC anonymous fds to represent BPF attachment in line with
the other BPF programs with the possibility to use bpf program pinning
in the future to provide "permanent attachment".
* Drop the logic based on prog names for handling re-attachment.
* Drop bpf_lsm_event_output from this series and send it as a separate
patch.
# Motivation
Google does analysis of rich runtime security data to detect and thwart
threats in real-time. Currently, this is done in custom kernel modules
but we would like to replace this with something that's upstream and
useful to others.
The current kernel infrastructure for providing telemetry (Audit, Perf
etc.) is disjoint from access enforcement (i.e. LSMs). Augmenting the
information provided by audit requires kernel changes to audit, its
policy language and user-space components. Furthermore, building a MAC
policy based on the newly added telemetry data requires changes to
various LSMs and their respective policy languages.
This patchset allows BPF programs to be attached to LSM hooks This
facilitates a unified and dynamic (not requiring re-compilation of the
kernel) audit and MAC policy.
# Why an LSM?
Linux Security Modules target security behaviours rather than the
kernel's API. For example, it's easy to miss out a newly added system
call for executing processes (eg. execve, execveat etc.) but the LSM
framework ensures that all process executions trigger the relevant hooks
irrespective of how the process was executed.
Allowing users to implement LSM hooks at runtime also benefits the LSM
eco-system by enabling a quick feedback loop from the security community
about the kind of behaviours that the LSM Framework should be targeting.
# How does it work?
The patchset introduces a new eBPF (https://docs.cilium.io/en/v1.6/bpf/)
program type BPF_PROG_TYPE_LSM which can only be attached to LSM hooks.
Attachment requires CAP_SYS_ADMIN for loading eBPF programs and
CAP_MAC_ADMIN for modifying MAC policies.
The eBPF programs are attached to nop functions (bpf_lsm_<name>) added
in the LSM hooks (when CONFIG_BPF_LSM) and executed after all the
statically defined hooks (i.e. the ones declared by static LSMs (eg,
SELinux, AppArmor, Smack etc) allow the action. This also ensures that
statically defined LSM hooks retain the behaviour of "being read-only
after init", i.e. __lsm_ro_after_init and do not increase the attack
surface.
The branch into this nop function is guarded with a static key (jump
label) and is only taken when a BPF program is attached to the LSM hook.
eg. for bprm_check_security:
int bpf_lsm_bprm_check_security(struct linux_binprm *bprm)
{
return 0;
}
DEFINE_STATIC_KEY_FALSE(bpf_lsm_key_bprm_check_security)
// Run all static hooks for bprm_check_security and set RC
if (static_key_unlikely(&bpf_lsm_key_bprm_check_security) {
if (RC == 0)
bpf_lsm_bprm_check_security(bprm);
}
Upon attachment, a BPF fexit trampoline is attached to the nop function
and the static key for the LSM hook is enabled. The trampoline has code
to handle the conversion from the signature of the hook to the BPF
context and allows the JIT'ed BPF program to be called as a C function
with the same arguments as the LSM hooks. If the attached eBPF programs
returns an error (like ENOPERM), the behaviour represented by the hook
is denied.
Audit logs can be written using a format chosen by the eBPF program to
the perf events buffer or to global eBPF variables or maps and can be
further processed in user-space.
# BTF Based Design
The current design uses BTF
(https://facebookmicrosites.github.io/bpf/blog/2018/11/14/btf-enhancement.html,
https://lwn.net/Articles/803258/) which allows verifiable read-only
structure accesses by field names rather than fixed offsets. This allows
accessing the hook parameters using a dynamically created context which
provides a certain degree of ABI stability:
// Only declare the structure and fields intended to be used
// in the program
struct vm_area_struct {
unsigned long vm_start;
} __attribute__((preserve_access_index));
// Declare the eBPF program mprotect_audit which attaches to
// to the file_mprotect LSM hook and accepts three arguments.
SEC("lsm/file_mprotect")
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
{
unsigned long vm_start = vma->vm_start;
return 0;
}
By relocating field offsets, BTF makes a large portion of kernel data
structures readily accessible across kernel versions without requiring a
large corpus of BPF helper functions and requiring recompilation with
every kernel version. The BTF type information is also used by the BPF
verifier to validate memory accesses within the BPF program and also
prevents arbitrary writes to the kernel memory.
The limitations of BTF compatibility are described in BPF Co-Re
(http://vger.kernel.org/bpfconf2019_talks/bpf-core.pdf, i.e. field
renames, #defines and changes to the signature of LSM hooks).
This design imposes that the MAC policy (eBPF programs) be updated when
the inspected kernel structures change outside of BTF compatibility
guarantees. In practice, this is only required when a structure field
used by a current policy is removed (or renamed) or when the used LSM
hooks change. We expect the maintenance cost of these changes to be
acceptable as compared to the previous design
(https://lore.kernel.org/bpf/20190910115527.5235-1-kpsingh@chromium.org/).
# Usage Examples
A simple example and some documentation is included in the patchset.
In order to better illustrate the capabilities of the framework some
more advanced prototype (not-ready for review) code has also been
published separately:
* Logging execution events (including environment variables and
arguments)
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c
* Detecting deletion of running executables:
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_detect_exec_unlink.c
* Detection of writes to /proc/<pid>/mem:
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c
We have updated Google's internal telemetry infrastructure and have
started deploying this LSM on our Linux Workstations. This gives us more
confidence in the real-world applications of such a system.
KP Singh (8):
bpf: Introduce BPF_PROG_TYPE_LSM
security: Refactor declaration of LSM hooks
bpf: lsm: provide attachment points for BPF LSM programs
bpf: lsm: Add support for enabling/disabling BPF hooks
bpf: lsm: Implement attach, detach and execution
tools/libbpf: Add support for BPF_PROG_TYPE_LSM
bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
bpf: lsm: Add Documentation
Documentation/bpf/bpf_lsm.rst | 147 +++++
Documentation/bpf/index.rst | 1 +
MAINTAINERS | 1 +
arch/x86/net/bpf_jit_comp.c | 21 +-
include/linux/bpf.h | 7 +
include/linux/bpf_lsm.h | 66 ++
include/linux/bpf_types.h | 4 +
include/linux/lsm_hook_names.h | 353 ++++++++++
include/linux/lsm_hooks.h | 622 +-----------------
include/uapi/linux/bpf.h | 2 +
init/Kconfig | 11 +
kernel/bpf/Makefile | 1 +
kernel/bpf/bpf_lsm.c | 88 +++
kernel/bpf/btf.c | 3 +-
kernel/bpf/syscall.c | 47 +-
kernel/bpf/trampoline.c | 24 +-
kernel/bpf/verifier.c | 19 +-
kernel/trace/bpf_trace.c | 12 +-
security/security.c | 35 +
tools/include/uapi/linux/bpf.h | 2 +
tools/lib/bpf/bpf.c | 3 +-
tools/lib/bpf/libbpf.c | 46 +-
tools/lib/bpf/libbpf.h | 4 +
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_probes.c | 1 +
tools/testing/selftests/bpf/lsm_helpers.h | 19 +
.../selftests/bpf/prog_tests/lsm_mprotect.c | 96 +++
.../selftests/bpf/progs/lsm_mprotect_audit.c | 48 ++
.../selftests/bpf/progs/lsm_mprotect_mac.c | 53 ++
29 files changed, 1085 insertions(+), 654 deletions(-)
create mode 100644 Documentation/bpf/bpf_lsm.rst
create mode 100644 include/linux/bpf_lsm.h
create mode 100644 include/linux/lsm_hook_names.h
create mode 100644 kernel/bpf/bpf_lsm.c
create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c
--
2.20.1
^ permalink raw reply
* [PATCH bpf-next v4 8/8] bpf: lsm: Add Documentation
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Brendan Jackman, Florent Revest, Thomas Garnier,
Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
Document how eBPF programs (BPF_PROG_TYPE_LSM) can be loaded and
attached (BPF_LSM_MAC) to the LSM hooks.
Signed-off-by: KP Singh <kpsingh@google.com>
Reviewed-by: Brendan Jackman <jackmanb@google.com>
Reviewed-by: Florent Revest <revest@google.com>
Reviewed-by: Thomas Garnier <thgarnie@google.com>
---
Documentation/bpf/bpf_lsm.rst | 147 ++++++++++++++++++++++++++++++++++
Documentation/bpf/index.rst | 1 +
2 files changed, 148 insertions(+)
create mode 100644 Documentation/bpf/bpf_lsm.rst
diff --git a/Documentation/bpf/bpf_lsm.rst b/Documentation/bpf/bpf_lsm.rst
new file mode 100644
index 000000000000..9d7ec8cb431d
--- /dev/null
+++ b/Documentation/bpf/bpf_lsm.rst
@@ -0,0 +1,147 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2019 Google LLC.
+
+================
+LSM BPF Programs
+================
+
+These BPF programs allow runtime instrumentation of the LSM hooks by privileged
+users to implement system-wide MAC (Mandatory Access Control) and Audit
+policies using eBPF. Since these program end up modifying the MAC policies of
+the system, they require both ``CAP_MAC_ADMIN`` and also require
+``CAP_SYS_ADMIN`` for the loading of BPF programs.
+
+Structure
+---------
+
+The example shows an eBPF program that can be attached to the ``file_mprotect``
+LSM hook:
+
+.. c:function:: int file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot);
+
+eBPF programs that use :doc:`/bpf/btf` do not need to include kernel headers
+for accessing information from the attached eBPF program's context. They can
+simply declare the structures in the eBPF program and only specify the fields
+that need to be accessed.
+
+.. code-block:: c
+
+ struct mm_struct {
+ unsigned long start_brk, brk, start_stack;
+ } __attribute__((preserve_access_index));
+
+ struct vm_area_struct {
+ unsigned long start_brk, brk, start_stack;
+ unsigned long vm_start, vm_end;
+ struct mm_struct *vm_mm;
+ } __attribute__((preserve_access_index));
+
+
+.. note:: Only the size and the names of the fields must match the type in the
+ kernel and the order of the fields is irrelevant.
+
+This can be further simplified (if one has access to the BTF information at
+build time) by generating the ``vmlinux.h`` with:
+
+.. code-block:: console
+
+ # bpftool dump file <path-to-btf-vmlinux> format c > vmlinux.h
+
+.. note:: ``path-to-btf-vmlinux`` can be ``/sys/kernel/btf/vmlinux`` if the
+ build environment matches the environment the BPF programs are
+ deployed in.
+
+The ``vmlinux.h`` can then simply be included in the BPF programs without
+requiring the definition of the types.
+
+The eBPF programs can be declared using the``BPF_PROG``
+macros defined in `tools/testing/selftests/bpf/bpf_trace_helpers.h`_. In this
+example:
+
+ * ``"lsm/file_mprotect"`` indicates the LSM hook that the program must
+ be attached to
+ * ``mprotect_audit`` is the name of the eBPF program
+
+.. code-block:: c
+
+ SEC("lsm/file_mprotect")
+ int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot, int ret)
+ {
+ /* Ret is the return value from the previous BPF program
+ * or 0 if it's the first hook.
+ */
+ if (ret != 0)
+ return ret;
+
+ int is_heap;
+
+ is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk);
+
+ /* Return an -EPERM or write information to the perf events buffer
+ * for auditing
+ */
+ }
+
+The ``__attribute__((preserve_access_index))`` is a clang feature that allows
+the BPF verifier to update the offsets for the access at runtime using the
+:doc:`/bpf/btf` information. Since the BPF verifier is aware of the types, it
+also validates all the accesses made to the various types in the eBPF program.
+
+Loading
+-------
+
+eBPP programs can be loaded with the :manpage:`bpf(2)` syscall's
+``BPF_PROG_LOAD`` operation or more simply by using the the libbpf helper
+``bpf_prog_load_xattr``:
+
+
+.. code-block:: c
+
+ struct bpf_prog_load_attr attr = {
+ .file = "./prog.o",
+ };
+ struct bpf_object *prog_obj;
+ struct bpf_program *prog;
+ int prog_fd;
+
+ bpf_prog_load_xattr(&attr, &prog_obj, &prog_fd);
+
+Attachment to LSM Hooks
+-----------------------
+
+The LSM allows attachment of eBPF programs as LSM hooks using :manpage:`bpf(2)`
+syscall's ``BPF_PROG_ATTACH`` operation or more simply by
+using the libbpf helper ``bpf_program__attach_lsm``. In the code shown below
+``prog`` is the eBPF program loaded using ``BPF_PROG_LOAD``:
+
+.. code-block:: c
+
+ struct bpf_link *link;
+
+ link = bpf_program__attach_lsm(prog);
+
+The program can be detached from the LSM hook by *destroying* the ``link``
+link returned by ``bpf_program__attach_lsm``:
+
+.. code-block:: c
+
+ link->destroy();
+
+Examples
+--------
+
+Example eBPF programs can be found in
+`tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c`_ and `tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c`_ and the corresponding
+userspace code in `tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c`_
+
+.. Links
+.. _tools/testing/selftests/bpf/bpf_trace_helpers.h:
+ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/selftests/bpf/bpf_trace_helpers.h
+.. _tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c:
+ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c
+.. _tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c:
+ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c
+.. _tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c:
+ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c
diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst
index 4f5410b61441..2c3d3c0cb7bb 100644
--- a/Documentation/bpf/index.rst
+++ b/Documentation/bpf/index.rst
@@ -45,6 +45,7 @@ Program types
prog_cgroup_sockopt
prog_cgroup_sysctl
prog_flow_dissector
+ bpf_lsm
Testing BPF
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 7/8] bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Brendan Jackman, Florent Revest, Thomas Garnier,
Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
* Load a BPF program that hooks to the mprotect calls.
* Attach the program to the "file_mprotect" LSM hook.
* Do an mprotect on some memory allocated on the heap
* Verify if the return value is overridden.
* Verify if the audit event was received using the shared global
result variable.
Signed-off-by: KP Singh <kpsingh@google.com>
Reviewed-by: Brendan Jackman <jackmanb@google.com>
Reviewed-by: Florent Revest <revest@google.com>
Reviewed-by: Thomas Garnier <thgarnie@google.com>
---
tools/testing/selftests/bpf/lsm_helpers.h | 19 ++++
.../selftests/bpf/prog_tests/lsm_mprotect.c | 96 +++++++++++++++++++
.../selftests/bpf/progs/lsm_mprotect_audit.c | 48 ++++++++++
.../selftests/bpf/progs/lsm_mprotect_mac.c | 53 ++++++++++
4 files changed, 216 insertions(+)
create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c
create mode 100644 tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c
diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h
new file mode 100644
index 000000000000..b973ec1c4a0b
--- /dev/null
+++ b/tools/testing/selftests/bpf/lsm_helpers.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+#ifndef _LSM_HELPERS_H
+#define _LSM_HELPERS_H
+
+struct lsm_mprotect_result {
+ /* This ensures that the LSM Hook only monitors the PID requested
+ * by the loader
+ */
+ __u32 monitored_pid;
+ /* The number of mprotect calls for the monitored PID.
+ */
+ __u32 mprotect_count;
+};
+
+#endif /* _LSM_HELPERS_H */
diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c b/tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c
new file mode 100644
index 000000000000..93c3b5fb2ef0
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/lsm_mprotect.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+
+#include <test_progs.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <malloc.h>
+#include "lsm_helpers.h"
+#include "lsm_mprotect_audit.skel.h"
+#include "lsm_mprotect_mac.skel.h"
+
+int heap_mprotect(void)
+{
+ void *buf;
+ long sz;
+
+ sz = sysconf(_SC_PAGESIZE);
+ if (sz < 0)
+ return sz;
+
+ buf = memalign(sz, 2 * sz);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ return mprotect(buf, sz, PROT_READ | PROT_EXEC);
+}
+
+void test_lsm_mprotect_audit(void)
+{
+ struct lsm_mprotect_result *result;
+ struct lsm_mprotect_audit *skel = NULL;
+ int err, duration = 0;
+
+ skel = lsm_mprotect_audit__open_and_load();
+ if (CHECK(!skel, "skel_load", "lsm_mprotect_audit skeleton failed\n"))
+ goto close_prog;
+
+ err = lsm_mprotect_audit__attach(skel);
+ if (CHECK(err, "attach", "lsm_mprotect_audit attach failed: %d\n", err))
+ goto close_prog;
+
+ result = &skel->bss->result;
+ result->monitored_pid = getpid();
+
+ err = heap_mprotect();
+ if (CHECK(err < 0, "heap_mprotect", "err %d errno %d\n", err, errno))
+ goto close_prog;
+
+ /* Make sure mprotect_audit program was triggered
+ * and detected an mprotect on the heap.
+ */
+ CHECK_FAIL(result->mprotect_count != 1);
+
+close_prog:
+ lsm_mprotect_audit__destroy(skel);
+}
+
+void test_lsm_mprotect_mac(void)
+{
+ struct lsm_mprotect_result *result;
+ struct lsm_mprotect_mac *skel = NULL;
+ int err, duration = 0;
+
+ skel = lsm_mprotect_mac__open_and_load();
+ if (CHECK(!skel, "skel_load", "lsm_mprotect_mac skeleton failed\n"))
+ goto close_prog;
+
+ err = lsm_mprotect_mac__attach(skel);
+ if (CHECK(err, "attach", "lsm_mprotect_mac attach failed: %d\n", err))
+ goto close_prog;
+
+ result = &skel->bss->result;
+ result->monitored_pid = getpid();
+
+ err = heap_mprotect();
+ if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
+ errno))
+ goto close_prog;
+
+ /* Make sure mprotect_mac program was triggered
+ * and detected an mprotect on the heap.
+ */
+ CHECK_FAIL(result->mprotect_count != 1);
+
+close_prog:
+ lsm_mprotect_mac__destroy(skel);
+}
+
+void test_lsm_mprotect(void)
+{
+ test_lsm_mprotect_audit();
+ test_lsm_mprotect_mac();
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c b/tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c
new file mode 100644
index 000000000000..c68fb02b57fa
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_mprotect_audit.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include "bpf_trace_helpers.h"
+#include <errno.h>
+#include "lsm_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct lsm_mprotect_result result = {
+ .mprotect_count = 0,
+ .monitored_pid = 0,
+};
+
+/*
+ * Define some of the structs used in the BPF program.
+ * Only the field names and their sizes need to be the
+ * same as the kernel type, the order is irrelevant.
+ */
+struct mm_struct {
+ unsigned long start_brk, brk;
+} __attribute__((preserve_access_index));
+
+struct vm_area_struct {
+ unsigned long vm_start, vm_end;
+ struct mm_struct *vm_mm;
+} __attribute__((preserve_access_index));
+
+SEC("lsm/file_mprotect")
+int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot)
+{
+ __u32 pid = bpf_get_current_pid_tgid();
+ int is_heap = 0;
+
+ is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk);
+
+ if (is_heap && result.monitored_pid == pid)
+ result.mprotect_count++;
+
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c b/tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c
new file mode 100644
index 000000000000..c0ae344593e8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lsm_mprotect_mac.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include "bpf_trace_helpers.h"
+#include <errno.h>
+#include "lsm_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct lsm_mprotect_result result = {
+ .mprotect_count = 0,
+ .monitored_pid = 0,
+};
+
+/*
+ * Define some of the structs used in the BPF program.
+ * Only the field names and their sizes need to be the
+ * same as the kernel type, the order is irrelevant.
+ */
+struct mm_struct {
+ unsigned long start_brk, brk;
+} __attribute__((preserve_access_index));
+
+struct vm_area_struct {
+ unsigned long vm_start, vm_end;
+ struct mm_struct *vm_mm;
+} __attribute__((preserve_access_index));
+
+SEC("lsm/file_mprotect")
+int BPF_PROG(mprotect_mac, struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot, int ret)
+{
+ if (ret != 0)
+ return ret;
+
+ __u32 pid = bpf_get_current_pid_tgid();
+ int is_heap = 0;
+
+ is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk);
+
+ if (is_heap && result.monitored_pid == pid) {
+ result.mprotect_count++;
+ ret = -EPERM;
+ }
+
+ return ret;
+}
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 6/8] tools/libbpf: Add support for BPF_PROG_TYPE_LSM
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
Since BPF_PROG_TYPE_LSM uses the same attaching mechanism as
BPF_PROG_TYPE_TRACING, the common logic is refactored into a static
function bpf_program__attach_btf.
A new API call bpf_program__attach_lsm is still added to avoid userspace
conflicts if this ever changes in the future.
Signed-off-by: KP Singh <kpsingh@google.com>
---
tools/lib/bpf/bpf.c | 3 ++-
tools/lib/bpf/libbpf.c | 46 ++++++++++++++++++++++++++++++++--------
tools/lib/bpf/libbpf.h | 4 ++++
tools/lib/bpf/libbpf.map | 3 +++
4 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index c6dafe563176..73220176728d 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -235,7 +235,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
memset(&attr, 0, sizeof(attr));
attr.prog_type = load_attr->prog_type;
attr.expected_attach_type = load_attr->expected_attach_type;
- if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS) {
+ if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS ||
+ attr.prog_type == BPF_PROG_TYPE_LSM) {
attr.attach_btf_id = load_attr->attach_btf_id;
} else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
attr.prog_type == BPF_PROG_TYPE_EXT) {
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514b1a524abb..d11139d5e76b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2351,16 +2351,14 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
static inline bool libbpf_prog_needs_vmlinux_btf(struct bpf_program *prog)
{
- if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
+ if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
+ prog->type == BPF_PROG_TYPE_LSM)
return true;
/* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
* also need vmlinux BTF
*/
- if (prog->type == BPF_PROG_TYPE_TRACING && !prog->attach_prog_fd)
- return true;
-
- return false;
+ return prog->type == BPF_PROG_TYPE_TRACING && !prog->attach_prog_fd;
}
static int bpf_object__load_vmlinux_btf(struct bpf_object *obj)
@@ -4855,7 +4853,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.insns = insns;
load_attr.insns_cnt = insns_cnt;
load_attr.license = license;
- if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
+ if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
+ prog->type == BPF_PROG_TYPE_LSM) {
load_attr.attach_btf_id = prog->attach_btf_id;
} else if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
@@ -4940,6 +4939,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
int err = 0, fd, i, btf_id;
if (prog->type == BPF_PROG_TYPE_TRACING ||
+ prog->type == BPF_PROG_TYPE_LSM ||
prog->type == BPF_PROG_TYPE_EXT) {
btf_id = libbpf_find_attach_btf_id(prog);
if (btf_id <= 0)
@@ -6179,6 +6179,7 @@ bool bpf_program__is_##NAME(const struct bpf_program *prog) \
} \
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
+BPF_PROG_TYPE_FNS(lsm, BPF_PROG_TYPE_LSM);
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
@@ -6245,6 +6246,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
struct bpf_program *prog);
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog);
+static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
+ struct bpf_program *prog);
struct bpf_sec_def {
const char *sec;
@@ -6291,6 +6294,10 @@ static const struct bpf_sec_def section_defs[] = {
SEC_DEF("freplace/", EXT,
.is_attach_btf = true,
.attach_fn = attach_trace),
+ SEC_DEF("lsm/", LSM,
+ .is_attach_btf = true,
+ .expected_attach_type = BPF_LSM_MAC,
+ .attach_fn = attach_lsm),
BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
@@ -6553,6 +6560,7 @@ static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
}
#define BTF_TRACE_PREFIX "btf_trace_"
+#define BTF_LSM_PREFIX "bpf_lsm_"
#define BTF_MAX_NAME_SIZE 128
static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
@@ -6580,6 +6588,9 @@ static inline int __find_vmlinux_btf_id(struct btf *btf, const char *name,
if (attach_type == BPF_TRACE_RAW_TP)
err = find_btf_by_prefix_kind(btf, BTF_TRACE_PREFIX, name,
BTF_KIND_TYPEDEF);
+ else if (attach_type == BPF_LSM_MAC)
+ err = find_btf_by_prefix_kind(btf, BTF_LSM_PREFIX, name,
+ BTF_KIND_FUNC);
else
err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
@@ -7354,7 +7365,8 @@ static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
return bpf_program__attach_raw_tracepoint(prog, tp_name);
}
-struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
+/* Common logic for all BPF program types that attach to a btf_id */
+static struct bpf_link *bpf_program__attach_btf(struct bpf_program *prog)
{
char errmsg[STRERR_BUFSIZE];
struct bpf_link_fd *link;
@@ -7376,7 +7388,7 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
if (pfd < 0) {
pfd = -errno;
free(link);
- pr_warn("program '%s': failed to attach to trace: %s\n",
+ pr_warn("program '%s': failed to attach to: %s\n",
bpf_program__title(prog, false),
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
return ERR_PTR(pfd);
@@ -7385,10 +7397,26 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
return (struct bpf_link *)link;
}
+struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
+}
+
+struct bpf_link *bpf_program__attach_lsm(struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
+}
+
static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
struct bpf_program *prog)
{
- return bpf_program__attach_trace(prog);
+ return bpf_program__attach_btf(prog);
+}
+
+static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
+ struct bpf_program *prog)
+{
+ return bpf_program__attach_btf(prog);
}
struct bpf_link *bpf_program__attach(struct bpf_program *prog)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3fe12c9d1f92..3f72323f205b 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -243,6 +243,8 @@ bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
LIBBPF_API struct bpf_link *
bpf_program__attach_trace(struct bpf_program *prog);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_lsm(struct bpf_program *prog);
struct bpf_map;
LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map);
struct bpf_insn;
@@ -316,6 +318,7 @@ LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_lsm(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
@@ -338,6 +341,7 @@ LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_lsm(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index b035122142bb..8df332a528a0 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -227,10 +227,13 @@ LIBBPF_0.0.7 {
bpf_probe_large_insn_limit;
bpf_prog_attach_xattr;
bpf_program__attach;
+ bpf_program__attach_lsm;
bpf_program__name;
bpf_program__is_extension;
+ bpf_program__is_lsm;
bpf_program__is_struct_ops;
bpf_program__set_extension;
+ bpf_program__set_lsm;
bpf_program__set_struct_ops;
btf__align_of;
libbpf_find_kernel_btf;
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 5/8] bpf: lsm: Implement attach, detach and execution
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
JITed BPF programs are dynamically attached to the LSM hooks
using fexit trampolines. The trampoline prologue generates code to handle
conversion of the signature of the hook to the BPF context and the newly
introduced BPF_TRAMP_F_OVERRIDE_RETURN allows the fexit trampoline
to override the return value of the function it is attached to.
The allocated fexit trampolines are attached to the nop function added
at the appropriate places and are executed if all the statically defined
LSM hooks allow the action.
The BPF_PROG_TYPE_LSM programs must have a GPL compatible license and
the following permissions are required to attach a program to a
hook:
- CAP_SYS_ADMIN to load the program
- CAP_MAC_ADMIN to attach it (i.e. to update the security policy)
When the program is loaded (BPF_PROG_LOAD):
* The verifier validates if the program is trying to attach to a valid
security hook and updates the prog->aux->attach_func_proto.
* The verifier then further verifies the program for memory accesses by
using the BTF information. (It also ensures that no memory is being
written to).
* An fexit trampoline is initialized (if not present in the lookup
table).
When an attachment (BPF_PROG_ATTACH) is requested:
* The fexit trampoline is updated to use the program being attached.
* The static key of the LSM hook is toggled if this is the first program
being attached to this hook. (and not a replacement).
The attached programs can override the return value of the fexit
trampoline to indicate a MAC Policy decision.
When multiple programs aree attached to the hook, each program receives
the return value from the previous program on the stack and the last
program provides the return value to the LSM hook.
Signed-off-by: KP Singh <kpsingh@google.com>
---
arch/x86/net/bpf_jit_comp.c | 21 +++++++++++++----
include/linux/bpf.h | 4 ++++
include/linux/bpf_lsm.h | 8 +++++++
kernel/bpf/bpf_lsm.c | 27 +++++++++++++++++++++
kernel/bpf/btf.c | 3 ++-
kernel/bpf/syscall.c | 47 ++++++++++++++++++++++++++++++-------
kernel/bpf/trampoline.c | 24 +++++++++++++++----
kernel/bpf/verifier.c | 19 +++++++++++----
8 files changed, 131 insertions(+), 22 deletions(-)
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 9ba08e9abc09..b710abfe06c4 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -1362,7 +1362,8 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args,
}
static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
- struct bpf_prog **progs, int prog_cnt, int stack_size)
+ struct bpf_prog **progs, int prog_cnt, int stack_size,
+ bool override_return)
{
u8 *prog = *pprog;
int cnt = 0, i;
@@ -1384,6 +1385,14 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
if (emit_call(&prog, progs[i]->bpf_func, prog))
return -EINVAL;
+
+ /* If BPF_TRAMP_F_OVERRIDE_RETURN is set, fexit trampolines can
+ * override the return value of the previous trampoline which is
+ * then passed on the stack to the next BPF program.
+ */
+ if (override_return)
+ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
+
/* arg1: mov rdi, progs[i] */
emit_mov_imm64(&prog, BPF_REG_1, (long) progs[i] >> 32,
(u32) (long) progs[i]);
@@ -1462,6 +1471,7 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end,
struct bpf_prog **fexit_progs, int fexit_cnt,
void *orig_call)
{
+ bool override_return = flags & BPF_TRAMP_F_OVERRIDE_RETURN;
int cnt = 0, nr_args = m->nr_args;
int stack_size = nr_args * 8;
u8 *prog;
@@ -1493,7 +1503,8 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end,
save_regs(m, &prog, nr_args, stack_size);
if (fentry_cnt)
- if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size))
+ if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size,
+ false))
return -EINVAL;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
@@ -1503,18 +1514,20 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end,
/* call original function */
if (emit_call(&prog, orig_call, prog))
return -EINVAL;
+
/* remember return value in a stack for bpf prog to access */
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
}
if (fexit_cnt)
- if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size))
+ if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size,
+ override_return))
return -EINVAL;
if (flags & BPF_TRAMP_F_RESTORE_REGS)
restore_regs(m, &prog, nr_args, stack_size);
- if (flags & BPF_TRAMP_F_CALL_ORIG)
+ if (flags & BPF_TRAMP_F_CALL_ORIG && !override_return)
/* restore original return value back into RAX */
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index c647cef3f4c1..e63caadbaef3 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -432,6 +432,10 @@ struct btf_func_model {
* programs only. Should not be used with normal calls and indirect calls.
*/
#define BPF_TRAMP_F_SKIP_FRAME BIT(2)
+/* Override the return value of the original function. This flag only makes
+ * sense for fexit trampolines.
+ */
+#define BPF_TRAMP_F_OVERRIDE_RETURN BIT(3)
/* Different use cases for BPF trampoline:
* 1. replace nop at the function entry (kprobe equivalent)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 53dcda8ace01..8f114affe5c6 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -41,6 +41,8 @@ void bpf_lsm_##NAME##_set_enabled(bool value);
})
int bpf_lsm_set_enabled(const char *name, bool value);
+int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
+ const struct bpf_prog *prog);
#else /* !CONFIG_BPF_LSM */
@@ -53,6 +55,12 @@ static inline int bpf_lsm_set_enabled(const char *name, bool value)
return -EOPNOTSUPP;
}
+static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
+ const struct bpf_prog *prog)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_BPF_LSM */
#endif /* _LINUX_BPF_LSM_H */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index d7c44433c003..edeb4ded1d3e 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -10,6 +10,7 @@
#include <linux/bpf_lsm.h>
#include <linux/jump_label.h>
#include <linux/kallsyms.h>
+#include <linux/bpf_verifier.h>
#define LSM_HOOK(RET, NAME, ...) \
DEFINE_STATIC_KEY_FALSE(bpf_lsm_key_##NAME); \
@@ -52,6 +53,32 @@ int bpf_lsm_set_enabled(const char *name, bool value)
return 0;
}
+#define BPF_LSM_SYM_PREFX "bpf_lsm_"
+
+int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
+ const struct bpf_prog *prog)
+{
+ /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (!prog->gpl_compatible) {
+ bpf_log(vlog,
+ "LSM programs must have a GPL compatible license\n");
+ return -EINVAL;
+ }
+
+ if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name,
+ strlen(BPF_LSM_SYM_PREFX))) {
+ bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
+ prog->aux->attach_btf_id, prog->aux->attach_func_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
const struct bpf_prog_ops lsm_prog_ops = {
};
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 805c43b083e9..0e4cad3c810b 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3710,7 +3710,8 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
nr_args--;
}
- if (prog->expected_attach_type == BPF_TRACE_FEXIT &&
+ if ((prog->expected_attach_type == BPF_TRACE_FEXIT ||
+ prog->expected_attach_type == BPF_LSM_MAC) &&
arg == nr_args) {
if (!t)
/* Default prog with 5 args. 6th arg is retval. */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a91ad518c050..e10e216463ad 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -25,6 +25,7 @@
#include <linux/nospec.h>
#include <linux/audit.h>
#include <uapi/linux/btf.h>
+#include <linux/bpf_lsm.h>
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
@@ -1931,6 +1932,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
switch (prog_type) {
case BPF_PROG_TYPE_TRACING:
+ case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
break;
@@ -2169,28 +2171,53 @@ static int bpf_obj_get(const union bpf_attr *attr)
attr->file_flags);
}
-static int bpf_tracing_prog_release(struct inode *inode, struct file *filp)
+static int bpf_tramp_prog_release(struct inode *inode, struct file *filp)
{
struct bpf_prog *prog = filp->private_data;
+ /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM hooks
+ */
+ if (prog->type == BPF_PROG_TYPE_LSM && !capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
bpf_prog_put(prog);
return 0;
}
-static const struct file_operations bpf_tracing_prog_fops = {
- .release = bpf_tracing_prog_release,
+static const struct file_operations bpf_tramp_prog_fops = {
+ .release = bpf_tramp_prog_release,
.read = bpf_dummy_read,
.write = bpf_dummy_write,
};
-static int bpf_tracing_prog_attach(struct bpf_prog *prog)
+static int bpf_tramp_prog_attach(struct bpf_prog *prog)
{
int tr_fd, err;
- if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
- prog->expected_attach_type != BPF_TRACE_FEXIT &&
- prog->type != BPF_PROG_TYPE_EXT) {
+ switch (prog->type) {
+ case BPF_PROG_TYPE_TRACING:
+ if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
+ prog->expected_attach_type != BPF_TRACE_FEXIT &&
+ prog->type != BPF_PROG_TYPE_EXT) {
+ err = -EINVAL;
+ goto out_put_prog;
+ }
+ break;
+ case BPF_PROG_TYPE_LSM:
+ if (prog->expected_attach_type != BPF_LSM_MAC) {
+ err = -EINVAL;
+ goto out_put_prog;
+ }
+ /* Only CAP_MAC_ADMIN users are allowed to make changes to LSM
+ * hooks.
+ */
+ if (!capable(CAP_MAC_ADMIN)) {
+ err = -EPERM;
+ goto out_put_prog;
+ }
+ break;
+ default:
err = -EINVAL;
goto out_put_prog;
}
@@ -2199,7 +2226,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
if (err)
goto out_put_prog;
- tr_fd = anon_inode_getfd("bpf-tracing-prog", &bpf_tracing_prog_fops,
+ tr_fd = anon_inode_getfd("bpf-tramp-prog", &bpf_tramp_prog_fops,
prog, O_CLOEXEC);
if (tr_fd < 0) {
WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog));
@@ -2258,12 +2285,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_EXT &&
+ prog->type != BPF_PROG_TYPE_LSM &&
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
err = -EINVAL;
goto out_put_prog;
}
if (prog->type == BPF_PROG_TYPE_TRACING ||
+ prog->type == BPF_PROG_TYPE_LSM ||
prog->type == BPF_PROG_TYPE_EXT) {
if (attr->raw_tracepoint.name) {
/* The attach point for this category of programs
@@ -2275,7 +2304,7 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
if (prog->expected_attach_type == BPF_TRACE_RAW_TP)
tp_name = prog->aux->attach_func_name;
else
- return bpf_tracing_prog_attach(prog);
+ return bpf_tramp_prog_attach(prog);
} else {
if (strncpy_from_user(buf,
u64_to_user_ptr(attr->raw_tracepoint.name),
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 6b264a92064b..4974c14258a9 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -5,6 +5,7 @@
#include <linux/filter.h>
#include <linux/ftrace.h>
#include <linux/rbtree_latch.h>
+#include <linux/bpf_lsm.h>
/* dummy _ops. The verifier will operate on target program's ops. */
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
@@ -195,8 +196,9 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
*/
#define BPF_MAX_TRAMP_PROGS 40
-static int bpf_trampoline_update(struct bpf_trampoline *tr)
+static int bpf_trampoline_update(struct bpf_prog *prog)
{
+ struct bpf_trampoline *tr = prog->aux->trampoline;
void *old_image = tr->image + ((tr->selector + 1) & 1) * BPF_IMAGE_SIZE/2;
void *new_image = tr->image + (tr->selector & 1) * BPF_IMAGE_SIZE/2;
struct bpf_prog *progs_to_run[BPF_MAX_TRAMP_PROGS];
@@ -223,8 +225,11 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
hlist_for_each_entry(aux, &tr->progs_hlist[BPF_TRAMP_FEXIT], tramp_hlist)
*progs++ = aux->prog;
- if (fexit_cnt)
+ if (fexit_cnt) {
flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME;
+ if (prog->type == BPF_PROG_TYPE_LSM)
+ flags |= BPF_TRAMP_F_OVERRIDE_RETURN;
+ }
/* Though the second half of trampoline page is unused a task could be
* preempted in the middle of the first half of trampoline and two
@@ -261,6 +266,7 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t)
case BPF_TRACE_FENTRY:
return BPF_TRAMP_FENTRY;
case BPF_TRACE_FEXIT:
+ case BPF_LSM_MAC:
return BPF_TRAMP_FEXIT;
default:
return BPF_TRAMP_REPLACE;
@@ -307,11 +313,17 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
}
hlist_add_head(&prog->aux->tramp_hlist, &tr->progs_hlist[kind]);
tr->progs_cnt[kind]++;
- err = bpf_trampoline_update(prog->aux->trampoline);
+ err = bpf_trampoline_update(prog);
if (err) {
hlist_del(&prog->aux->tramp_hlist);
tr->progs_cnt[kind]--;
}
+
+ /* This is the first program to be attached to the LSM hook, the hook
+ * needs to be enabled.
+ */
+ if (prog->type == BPF_PROG_TYPE_LSM && tr->progs_cnt[kind] == 1)
+ err = bpf_lsm_set_enabled(prog->aux->attach_func_name, true);
out:
mutex_unlock(&tr->mutex);
return err;
@@ -336,7 +348,11 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
}
hlist_del(&prog->aux->tramp_hlist);
tr->progs_cnt[kind]--;
- err = bpf_trampoline_update(prog->aux->trampoline);
+ err = bpf_trampoline_update(prog);
+
+ /* There are no more LSM programs, the hook should be disabled */
+ if (prog->type == BPF_PROG_TYPE_LSM && tr->progs_cnt[kind] == 0)
+ err = bpf_lsm_set_enabled(prog->aux->attach_func_name, false);
out:
mutex_unlock(&tr->mutex);
return err;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1cc945daa9c8..6be11889678b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19,6 +19,7 @@
#include <linux/sort.h>
#include <linux/perf_event.h>
#include <linux/ctype.h>
+#include <linux/bpf_lsm.h>
#include "disasm.h"
@@ -6405,8 +6406,9 @@ static int check_return_code(struct bpf_verifier_env *env)
struct tnum range = tnum_range(0, 1);
int err;
- /* The struct_ops func-ptr's return type could be "void" */
- if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS &&
+ /* LSM and struct_ops func-ptr's return type could be "void" */
+ if ((env->prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
+ env->prog->type == BPF_PROG_TYPE_LSM) &&
!prog->aux->attach_func_proto->type)
return 0;
@@ -9794,7 +9796,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
return check_struct_ops_btf_id(env);
- if (prog->type != BPF_PROG_TYPE_TRACING && !prog_extension)
+ if (prog->type != BPF_PROG_TYPE_TRACING &&
+ prog->type != BPF_PROG_TYPE_LSM &&
+ !prog_extension)
return 0;
if (!btf_id) {
@@ -9924,8 +9928,16 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (!prog_extension)
return -EINVAL;
/* fallthrough */
+ case BPF_LSM_MAC:
case BPF_TRACE_FENTRY:
case BPF_TRACE_FEXIT:
+ prog->aux->attach_func_name = tname;
+ if (prog->type == BPF_PROG_TYPE_LSM) {
+ ret = bpf_lsm_verify_prog(&env->log, prog);
+ if (ret < 0)
+ return ret;
+ }
+
if (!btf_type_is_func(t)) {
verbose(env, "attach_btf_id %u is not a function\n",
btf_id);
@@ -9940,7 +9952,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
tr = bpf_trampoline_lookup(key);
if (!tr)
return -ENOMEM;
- prog->aux->attach_func_name = tname;
/* t is either vmlinux type or another program's type */
prog->aux->attach_func_proto = t;
mutex_lock(&tr->mutex);
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 4/8] bpf: lsm: Add support for enabling/disabling BPF hooks
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
Each LSM hook defines a static key i.e. bpf_lsm_<name>
and a bpf_lsm_<name>_set_enabled function to toggle the key
which enables/disables the branch which executes the BPF programs
attached to the LSM hook.
Use of static keys was suggested in upstream discussion:
https://lore.kernel.org/bpf/1cd10710-a81b-8f9b-696d-aa40b0a67225@iogearbox.net/
and results in the following assembly:
0x0000000000001e31 <+65>: jmpq 0x1e36 <security_bprm_check+70>
0x0000000000001e36 <+70>: nopl 0x0(%rax,%rax,1)
0x0000000000001e3b <+75>: xor %eax,%eax
0x0000000000001e3d <+77>: jmp 0x1e25 <security_bprm_check+53>
which avoids an indirect branch and results in lower overhead which is
especially helpful for LSM hooks in performance hotpaths.
Given the ability to toggle the BPF trampolines, some hooks which do
not call call_<int, void>_hooks as they have different default return
values, also gain support for BPF program attachment.
There are some hooks like security_setprocattr and security_getprocattr
which are not instrumentable as they do not provide any monitoring or
access control decisions. If required, generation of BTF type
information for these hooks can be also be blacklisted.
Signed-off-by: KP Singh <kpsingh@google.com>
---
include/linux/bpf_lsm.h | 30 +++++++++++++++++++++++++++---
kernel/bpf/bpf_lsm.c | 28 ++++++++++++++++++++++++++++
security/security.c | 32 ++++++++++++++++++++++++++++++++
3 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index f867f72f6aa9..53dcda8ace01 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -8,27 +8,51 @@
#define _LINUX_BPF_LSM_H
#include <linux/bpf.h>
+#include <linux/jump_label.h>
#ifdef CONFIG_BPF_LSM
+#define LSM_HOOK(RET, NAME, ...) \
+DECLARE_STATIC_KEY_FALSE(bpf_lsm_key_##NAME); \
+void bpf_lsm_##NAME##_set_enabled(bool value);
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
+
#define LSM_HOOK(RET, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
#include <linux/lsm_hook_names.h>
#undef LSM_HOOK
-#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...) bpf_lsm_##FUNC(__VA_ARGS__)
+#define HAS_BPF_LSM_PROG(FUNC) (static_branch_unlikely(&bpf_lsm_key_##FUNC))
+
+#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...) \
+ do { \
+ if (HAS_BPF_LSM_PROG(FUNC)) \
+ bpf_lsm_##FUNC(__VA_ARGS__); \
+ } while (0)
+
#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) ({ \
do { \
- if (RC == 0) \
- RC = bpf_lsm_##FUNC(__VA_ARGS__); \
+ if (HAS_BPF_LSM_PROG(FUNC)) { \
+ if (RC == 0) \
+ RC = bpf_lsm_##FUNC(__VA_ARGS__); \
+ } \
} while (0); \
RC; \
})
+int bpf_lsm_set_enabled(const char *name, bool value);
+
#else /* !CONFIG_BPF_LSM */
+#define HAS_BPF_LSM_PROG false
#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) (RC)
#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...)
+static inline int bpf_lsm_set_enabled(const char *name, bool value)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_BPF_LSM */
#endif /* _LINUX_BPF_LSM_H */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index abc847c9b9a1..d7c44433c003 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -8,6 +8,20 @@
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/bpf_lsm.h>
+#include <linux/jump_label.h>
+#include <linux/kallsyms.h>
+
+#define LSM_HOOK(RET, NAME, ...) \
+ DEFINE_STATIC_KEY_FALSE(bpf_lsm_key_##NAME); \
+ void bpf_lsm_##NAME##_set_enabled(bool value) \
+ { \
+ if (value) \
+ static_branch_enable(&bpf_lsm_key_##NAME); \
+ else \
+ static_branch_disable(&bpf_lsm_key_##NAME); \
+ }
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
/* For every LSM hook that allows attachment of BPF programs, declare a NOP
* function where a BPF program can be attached as an fexit trampoline.
@@ -24,6 +38,20 @@
#include <linux/lsm_hook_names.h>
#undef LSM_HOOK
+int bpf_lsm_set_enabled(const char *name, bool value)
+{
+ char toggle_fn_name[KSYM_NAME_LEN];
+ void (*toggle_fn)(bool value);
+
+ snprintf(toggle_fn_name, KSYM_NAME_LEN, "%s_set_enabled", name);
+ toggle_fn = (void *)kallsyms_lookup_name(toggle_fn_name);
+ if (!toggle_fn)
+ return -ESRCH;
+
+ toggle_fn(value);
+ return 0;
+}
+
const struct bpf_prog_ops lsm_prog_ops = {
};
diff --git a/security/security.c b/security/security.c
index aa111392a700..569cc07d5e34 100644
--- a/security/security.c
+++ b/security/security.c
@@ -804,6 +804,13 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
break;
}
}
+#ifdef CONFIG_BPF_LSM
+ if (HAS_BPF_LSM_PROG(vm_enough_memory)) {
+ rc = bpf_lsm_vm_enough_memory(mm, pages);
+ if (rc <= 0)
+ cap_sys_admin = 0;
+ }
+#endif
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
@@ -1350,6 +1357,13 @@ int security_inode_getsecurity(struct inode *inode, const char *name, void **buf
if (rc != -EOPNOTSUPP)
return rc;
}
+#ifdef CONFIG_BPF_LSM
+ if (HAS_BPF_LSM_PROG(inode_getsecurity)) {
+ rc = bpf_lsm_inode_getsecurity(inode, name, buffer, alloc);
+ if (rc != -EOPNOTSUPP)
+ return rc;
+ }
+#endif
return -EOPNOTSUPP;
}
@@ -1369,6 +1383,14 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void
if (rc != -EOPNOTSUPP)
return rc;
}
+#ifdef CONFIG_BPF_LSM
+ if (HAS_BPF_LSM_PROG(inode_setsecurity)) {
+ rc = bpf_lsm_inode_setsecurity(inode, name, value, size,
+ flags);
+ if (rc != -EOPNOTSUPP)
+ return rc;
+ }
+#endif
return -EOPNOTSUPP;
}
@@ -1754,6 +1776,12 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
break;
}
}
+#ifdef CONFIG_BPF_LSM
+ if (HAS_BPF_LSM_PROG(task_prctl)) {
+ if (rc == -ENOSYS)
+ rc = bpf_lsm_task_prctl(option, arg2, arg3, arg4, arg5);
+ }
+#endif
return rc;
}
@@ -2334,6 +2362,10 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
break;
}
+#ifdef CONFIG_BPF_LSM
+ if (HAS_BPF_LSM_PROG(xfrm_state_pol_flow_match))
+ rc = bpf_lsm_xfrm_state_pol_flow_match(x, xp, fl);
+#endif
return rc;
}
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 2/8] security: Refactor declaration of LSM hooks
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
The information about the different types of LSM hooks is scattered
in two locations i.e. union security_list_options and
struct security_hook_heads. Rather than duplicating this information
even further for BPF_PROG_TYPE_LSM, define all the hooks with the
LSM_HOOK macro in lsm_hook_names.h which is then used to generate all
the data structures required by the LSM framework.
Signed-off-by: KP Singh <kpsingh@google.com>
---
include/linux/lsm_hook_names.h | 353 +++++++++++++++++++
include/linux/lsm_hooks.h | 622 +--------------------------------
2 files changed, 359 insertions(+), 616 deletions(-)
create mode 100644 include/linux/lsm_hook_names.h
diff --git a/include/linux/lsm_hook_names.h b/include/linux/lsm_hook_names.h
new file mode 100644
index 000000000000..1137a3e70bf5
--- /dev/null
+++ b/include/linux/lsm_hook_names.h
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Linux Security Module Hook declarations.
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
+ * Copyright (C) 2001 James Morris <jmorris@intercode.com.au>
+ * Copyright (C) 2001 Silicon Graphics, Inc. (Trust Technology Group)
+ * Copyright (C) 2015 Intel Corporation.
+ * Copyright (C) 2015 Casey Schaufler <casey@schaufler-ca.com>
+ * Copyright (C) 2016 Mellanox Techonologies
+ * Copyright 2019 Google LLC.
+ */
+
+/* The macro LSM_HOOK is used to define the data structures required by the
+ * the LSM framework using the pattern:
+ *
+ * struct security_hook_heads {
+ * #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
+ * #include <linux/lsm_hook_names.h>
+ * #undef LSM_HOOK
+ * };
+ */
+LSM_HOOK(int, binder_set_context_mgr, struct task_struct *mgr)
+LSM_HOOK(int, binder_transaction, struct task_struct *from,
+ struct task_struct *to)
+LSM_HOOK(int, binder_transfer_binder, struct task_struct *from,
+ struct task_struct *to)
+LSM_HOOK(int, binder_transfer_file, struct task_struct *from,
+ struct task_struct *to, struct file *file)
+LSM_HOOK(int, ptrace_access_check, struct task_struct *child, unsigned int mode)
+LSM_HOOK(int, ptrace_traceme, struct task_struct *parent)
+LSM_HOOK(int, capget, struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+LSM_HOOK(int, capset, struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective, const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
+LSM_HOOK(int, capable, const struct cred *cred, struct user_namespace *ns,
+ int cap, unsigned int opts)
+LSM_HOOK(int, quotactl, int cmds, int type, int id, struct super_block *sb)
+LSM_HOOK(int, quota_on, struct dentry *dentry)
+LSM_HOOK(int, syslog, int type)
+LSM_HOOK(int, settime, const struct timespec64 *ts, const struct timezone *tz)
+LSM_HOOK(int, vm_enough_memory, struct mm_struct *mm, long pages)
+LSM_HOOK(int, bprm_set_creds, struct linux_binprm *bprm)
+LSM_HOOK(int, bprm_check_security, struct linux_binprm *bprm)
+LSM_HOOK(void, bprm_committing_creds, struct linux_binprm *bprm)
+LSM_HOOK(void, bprm_committed_creds, struct linux_binprm *bprm)
+LSM_HOOK(int, fs_context_dup, struct fs_context *fc, struct fs_context *src_sc)
+LSM_HOOK(int, fs_context_parse_param, struct fs_context *fc,
+ struct fs_parameter *param)
+LSM_HOOK(int, sb_alloc_security, struct super_block *sb)
+LSM_HOOK(void, sb_free_security, struct super_block *sb)
+LSM_HOOK(void, sb_free_mnt_opts, void *mnt_opts)
+LSM_HOOK(int, sb_eat_lsm_opts, char *orig, void **mnt_opts)
+LSM_HOOK(int, sb_remount, struct super_block *sb, void *mnt_opts)
+LSM_HOOK(int, sb_kern_mount, struct super_block *sb)
+LSM_HOOK(int, sb_show_options, struct seq_file *m, struct super_block *sb)
+LSM_HOOK(int, sb_statfs, struct dentry *dentry)
+LSM_HOOK(int, sb_mount, const char *dev_name, const struct path *path,
+ const char *type, unsigned long flags, void *data)
+LSM_HOOK(int, sb_umount, struct vfsmount *mnt, int flags)
+LSM_HOOK(int, sb_pivotroot, const struct path *old_path,
+ const struct path *new_path)
+LSM_HOOK(int, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
+ unsigned long kern_flags, unsigned long *set_kern_flags)
+LSM_HOOK(int, sb_clone_mnt_opts, const struct super_block *oldsb,
+ struct super_block *newsb, unsigned long kern_flags,
+ unsigned long *set_kern_flags)
+LSM_HOOK(int, sb_add_mnt_opt, const char *option, const char *val, int len,
+ void **mnt_opts)
+LSM_HOOK(int, move_mount, const struct path *from_path,
+ const struct path *to_path)
+LSM_HOOK(int, dentry_init_security, struct dentry *dentry, int mode,
+ const struct qstr *name, void **ctx, u32 *ctxlen)
+LSM_HOOK(int, dentry_create_files_as, struct dentry *dentry, int mode,
+ struct qstr *name, const struct cred *old, struct cred *new)
+#ifdef CONFIG_SECURITY_PATH
+LSM_HOOK(int, path_unlink, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, path_mkdir, const struct path *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, path_rmdir, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, path_mknod, const struct path *dir, struct dentry *dentry,
+ umode_t mode, unsigned int dev)
+LSM_HOOK(int, path_truncate, const struct path *path)
+LSM_HOOK(int, path_symlink, const struct path *dir, struct dentry *dentry,
+ const char *old_name)
+LSM_HOOK(int, path_link, struct dentry *old_dentry, const struct path *new_dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, path_rename, const struct path *old_dir,
+ struct dentry *old_dentry, const struct path *new_dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, path_chmod, const struct path *path, umode_t mode)
+LSM_HOOK(int, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
+LSM_HOOK(int, path_chroot, const struct path *path)
+#endif
+
+/* Needed for inode based security check */
+LSM_HOOK(int, path_notify, const struct path *path, u64 mask,
+ unsigned int obj_type)
+LSM_HOOK(int, inode_alloc_security, struct inode *inode)
+LSM_HOOK(void, inode_free_security, struct inode *inode)
+LSM_HOOK(int, inode_init_security, struct inode *inode, struct inode *dir,
+ const struct qstr *qstr, const char **name, void **value, size_t *len)
+LSM_HOOK(int, inode_create, struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, inode_link, struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, inode_unlink, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, inode_symlink, struct inode *dir, struct dentry *dentry,
+ const char *old_name)
+LSM_HOOK(int, inode_mkdir, struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+LSM_HOOK(int, inode_rmdir, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, inode_mknod, struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+LSM_HOOK(int, inode_rename, struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+LSM_HOOK(int, inode_readlink, struct dentry *dentry)
+LSM_HOOK(int, inode_follow_link, struct dentry *dentry, struct inode *inode,
+ bool rcu)
+LSM_HOOK(int, inode_permission, struct inode *inode, int mask)
+LSM_HOOK(int, inode_setattr, struct dentry *dentry, struct iattr *attr)
+LSM_HOOK(int, inode_getattr, const struct path *path)
+LSM_HOOK(int, inode_setxattr, struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(void, inode_post_setxattr, struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(int, inode_getxattr, struct dentry *dentry, const char *name)
+LSM_HOOK(int, inode_listxattr, struct dentry *dentry)
+LSM_HOOK(int, inode_removexattr, struct dentry *dentry, const char *name)
+LSM_HOOK(int, inode_need_killpriv, struct dentry *dentry)
+LSM_HOOK(int, inode_killpriv, struct dentry *dentry)
+LSM_HOOK(int, inode_getsecurity, struct inode *inode, const char *name,
+ void **buffer, bool alloc)
+LSM_HOOK(int, inode_setsecurity, struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+LSM_HOOK(int, inode_listsecurity, struct inode *inode, char *buffer,
+ size_t buffer_size)
+LSM_HOOK(void, inode_getsecid, struct inode *inode, u32 *secid)
+LSM_HOOK(int, inode_copy_up, struct dentry *src, struct cred **new)
+LSM_HOOK(int, inode_copy_up_xattr, const char *name)
+LSM_HOOK(int, kernfs_init_security, struct kernfs_node *kn_dir,
+ struct kernfs_node *kn)
+LSM_HOOK(int, file_permission, struct file *file, int mask)
+LSM_HOOK(int, file_alloc_security, struct file *file)
+LSM_HOOK(void, file_free_security, struct file *file)
+LSM_HOOK(int, file_ioctl, struct file *file, unsigned int cmd,
+ unsigned long arg)
+LSM_HOOK(int, mmap_addr, unsigned long addr)
+LSM_HOOK(int, mmap_file, struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+LSM_HOOK(int, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
+LSM_HOOK(int, file_lock, struct file *file, unsigned int cmd)
+LSM_HOOK(int, file_fcntl, struct file *file, unsigned int cmd,
+ unsigned long arg)
+LSM_HOOK(void, file_set_fowner, struct file *file)
+LSM_HOOK(int, file_send_sigiotask, struct task_struct *tsk,
+ struct fown_struct *fown, int sig)
+LSM_HOOK(int, file_receive, struct file *file)
+LSM_HOOK(int, file_open, struct file *file)
+LSM_HOOK(int, task_alloc, struct task_struct *task, unsigned long clone_flags)
+LSM_HOOK(void, task_free, struct task_struct *task)
+LSM_HOOK(int, cred_alloc_blank, struct cred *cred, gfp_t gfp)
+LSM_HOOK(void, cred_free, struct cred *cred)
+LSM_HOOK(int, cred_prepare, struct cred *new, const struct cred *old, gfp_t gfp)
+LSM_HOOK(void, cred_transfer, struct cred *new, const struct cred *old)
+LSM_HOOK(void, cred_getsecid, const struct cred *c, u32 *secid)
+LSM_HOOK(int, kernel_act_as, struct cred *new, u32 secid)
+LSM_HOOK(int, kernel_create_files_as, struct cred *new, struct inode *inode)
+LSM_HOOK(int, kernel_module_request, char *kmod_name)
+LSM_HOOK(int, kernel_load_data, enum kernel_load_data_id id)
+LSM_HOOK(int, kernel_read_file, struct file *file, enum kernel_read_file_id id)
+LSM_HOOK(int, kernel_post_read_file, struct file *file, char *buf, loff_t size,
+ enum kernel_read_file_id id)
+LSM_HOOK(int, task_fix_setuid, struct cred *new, const struct cred *old,
+ int flags)
+LSM_HOOK(int, task_setpgid, struct task_struct *p, pid_t pgid)
+LSM_HOOK(int, task_getpgid, struct task_struct *p)
+LSM_HOOK(int, task_getsid, struct task_struct *p)
+LSM_HOOK(void, task_getsecid, struct task_struct *p, u32 *secid)
+LSM_HOOK(int, task_setnice, struct task_struct *p, int nice)
+LSM_HOOK(int, task_setioprio, struct task_struct *p, int ioprio)
+LSM_HOOK(int, task_getioprio, struct task_struct *p)
+LSM_HOOK(int, task_prlimit, const struct cred *cred, const struct cred *tcred,
+ unsigned int flags)
+LSM_HOOK(int, task_setrlimit, struct task_struct *p, unsigned int resource,
+ struct rlimit *new_rlim)
+LSM_HOOK(int, task_setscheduler, struct task_struct *p)
+LSM_HOOK(int, task_getscheduler, struct task_struct *p)
+LSM_HOOK(int, task_movememory, struct task_struct *p)
+LSM_HOOK(int, task_kill, struct task_struct *p, struct kernel_siginfo *info,
+ int sig, const struct cred *cred)
+LSM_HOOK(int, task_prctl, int option, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5)
+LSM_HOOK(void, task_to_inode, struct task_struct *p, struct inode *inode)
+LSM_HOOK(int, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
+LSM_HOOK(void, ipc_getsecid, struct kern_ipc_perm *ipcp, u32 *secid)
+LSM_HOOK(int, msg_msg_alloc_security, struct msg_msg *msg)
+LSM_HOOK(void, msg_msg_free_security, struct msg_msg *msg)
+LSM_HOOK(int, msg_queue_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, msg_queue_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg)
+LSM_HOOK(int, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, msg_queue_msgsnd, struct kern_ipc_perm *perm, struct msg_msg *msg,
+ int msqflg)
+LSM_HOOK(int, msg_queue_msgrcv, struct kern_ipc_perm *perm, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+LSM_HOOK(int, shm_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, shm_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, shm_associate, struct kern_ipc_perm *perm, int shmflg)
+LSM_HOOK(int, shm_shmctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, shm_shmat, struct kern_ipc_perm *perm, char __user *shmaddr,
+ int shmflg)
+LSM_HOOK(int, sem_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, sem_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, sem_associate, struct kern_ipc_perm *perm, int semflg)
+LSM_HOOK(int, sem_semctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops,
+ unsigned nsops, int alter)
+LSM_HOOK(int, netlink_send, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(void, d_instantiate, struct dentry *dentry, struct inode *inode)
+LSM_HOOK(int, getprocattr, struct task_struct *p, char *name, char **value)
+LSM_HOOK(int, setprocattr, const char *name, void *value, size_t size)
+LSM_HOOK(int, ismaclabel, const char *name)
+LSM_HOOK(int, secid_to_secctx, u32 secid, char **secdata, u32 *seclen)
+LSM_HOOK(int, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
+LSM_HOOK(void, release_secctx, char *secdata, u32 seclen)
+LSM_HOOK(void, inode_invalidate_secctx, struct inode *inode)
+LSM_HOOK(int, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
+LSM_HOOK(int, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen)
+LSM_HOOK(int, inode_getsecctx, struct inode *inode, void **ctx, u32 *ctxlen)
+#ifdef CONFIG_SECURITY_NETWORK
+LSM_HOOK(int, unix_stream_connect, struct sock *sock, struct sock *other,
+ struct sock *newsk)
+LSM_HOOK(int, unix_may_send, struct socket *sock, struct socket *other)
+LSM_HOOK(int, socket_create, int family, int type, int protocol, int kern)
+LSM_HOOK(int, socket_post_create, struct socket *sock, int family, int type,
+ int protocol, int kern)
+LSM_HOOK(int, socket_socketpair, struct socket *socka, struct socket *sockb)
+LSM_HOOK(int, socket_bind, struct socket *sock, struct sockaddr *address,
+ int addrlen)
+LSM_HOOK(int, socket_connect, struct socket *sock, struct sockaddr *address,
+ int addrlen)
+LSM_HOOK(int, socket_listen, struct socket *sock, int backlog)
+LSM_HOOK(int, socket_accept, struct socket *sock, struct socket *newsock)
+LSM_HOOK(int, socket_sendmsg, struct socket *sock, struct msghdr *msg, int size)
+LSM_HOOK(int, socket_recvmsg, struct socket *sock, struct msghdr *msg, int size,
+ int flags)
+LSM_HOOK(int, socket_getsockname, struct socket *sock)
+LSM_HOOK(int, socket_getpeername, struct socket *sock)
+LSM_HOOK(int, socket_getsockopt, struct socket *sock, int level, int optname)
+LSM_HOOK(int, socket_setsockopt, struct socket *sock, int level, int optname)
+LSM_HOOK(int, socket_shutdown, struct socket *sock, int how)
+LSM_HOOK(int, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(int, socket_getpeersec_stream, struct socket *sock,
+ char __user *optval, int __user *optlen, unsigned len)
+LSM_HOOK(int, socket_getpeersec_dgram, struct socket *sock, struct sk_buff *skb,
+ u32 *secid)
+LSM_HOOK(int, sk_alloc_security, struct sock *sk, int family, gfp_t priority)
+LSM_HOOK(void, sk_free_security, struct sock *sk)
+LSM_HOOK(void, sk_clone_security, const struct sock *sk, struct sock *newsk)
+LSM_HOOK(void, sk_getsecid, struct sock *sk, u32 *secid)
+LSM_HOOK(void, sock_graft, struct sock *sk, struct socket *parent)
+LSM_HOOK(int, inet_conn_request, struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+LSM_HOOK(void, inet_csk_clone, struct sock *newsk,
+ const struct request_sock *req)
+LSM_HOOK(void, inet_conn_established, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(int, secmark_relabel_packet, u32 secid)
+LSM_HOOK(void, secmark_refcount_inc, void)
+LSM_HOOK(void, secmark_refcount_dec, void)
+LSM_HOOK(void, req_classify_flow, const struct request_sock *req,
+ struct flowi *fl)
+LSM_HOOK(int, tun_dev_alloc_security, void **security)
+LSM_HOOK(void, tun_dev_free_security, void *security)
+LSM_HOOK(int, tun_dev_create, void)
+LSM_HOOK(int, tun_dev_attach_queue, void *security)
+LSM_HOOK(int, tun_dev_attach, struct sock *sk, void *security)
+LSM_HOOK(int, tun_dev_open, void *security)
+LSM_HOOK(int, sctp_assoc_request, struct sctp_endpoint *ep, struct sk_buff *skb)
+LSM_HOOK(int, sctp_bind_connect, struct sock *sk, int optname,
+ struct sockaddr *address, int addrlen)
+LSM_HOOK(void, sctp_sk_clone, struct sctp_endpoint *ep, struct sock *sk,
+ struct sock *newsk)
+#endif /* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_SECURITY_INFINIBAND
+LSM_HOOK(int, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey)
+LSM_HOOK(int, ib_endport_manage_subnet, void *sec, const char *dev_name,
+ u8 port_num)
+LSM_HOOK(int, ib_alloc_security, void **sec)
+LSM_HOOK(void, ib_free_security, void *sec)
+#endif /* CONFIG_SECURITY_INFINIBAND */
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+LSM_HOOK(int, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *sec_ctx, gfp_t gfp)
+LSM_HOOK(int, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctx)
+LSM_HOOK(void, xfrm_policy_free_security, struct xfrm_sec_ctx *ctx)
+LSM_HOOK(int, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx)
+LSM_HOOK(int, xfrm_state_alloc, struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx)
+LSM_HOOK(int, xfrm_state_alloc_acquire, struct xfrm_state *x,
+ struct xfrm_sec_ctx *polsec, u32 secid)
+LSM_HOOK(void, xfrm_state_free_security, struct xfrm_state *x)
+LSM_HOOK(int, xfrm_state_delete_security, struct xfrm_state *x)
+LSM_HOOK(int, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid,
+ u8 dir)
+LSM_HOOK(int, xfrm_state_pol_flow_match, struct xfrm_state *x,
+ struct xfrm_policy *xp, const struct flowi *fl)
+LSM_HOOK(int, xfrm_decode_session, struct sk_buff *skb, u32 *secid, int ckall)
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+
+/* key management security hooks */
+#ifdef CONFIG_KEYS
+LSM_HOOK(int, key_alloc, struct key *key, const struct cred *cred,
+ unsigned long flags)
+LSM_HOOK(void, key_free, struct key *key)
+LSM_HOOK(int, key_permission, key_ref_t key_ref, const struct cred *cred,
+ unsigned perm)
+LSM_HOOK(int, key_getsecurity, struct key *key, char **_buffer)
+#endif /* CONFIG_KEYS */
+
+#ifdef CONFIG_AUDIT
+LSM_HOOK(int, audit_rule_init, u32 field, u32 op, char *rulestr, void **lsmrule)
+LSM_HOOK(int, audit_rule_known, struct audit_krule *krule)
+LSM_HOOK(int, audit_rule_match, u32 secid, u32 field, u32 op, void *lsmrule)
+LSM_HOOK(void, audit_rule_free, void *lsmrule)
+#endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_BPF_SYSCALL
+LSM_HOOK(int, bpf, int cmd, union bpf_attr *attr, unsigned int size)
+LSM_HOOK(int, bpf_map, struct bpf_map *map, fmode_t fmode)
+LSM_HOOK(int, bpf_prog, struct bpf_prog *prog)
+LSM_HOOK(int, bpf_map_alloc_security, struct bpf_map *map)
+LSM_HOOK(void, bpf_map_free_security, struct bpf_map *map)
+LSM_HOOK(int, bpf_prog_alloc_security, struct bpf_prog_aux *aux)
+LSM_HOOK(void, bpf_prog_free_security, struct bpf_prog_aux *aux)
+#endif /* CONFIG_BPF_SYSCALL */
+
+LSM_HOOK(int, locked_down, enum lockdown_reason what)
+#ifdef CONFIG_PERF_EVENTS
+LSM_HOOK(int, perf_event_open, struct perf_event_attr *attr, int type)
+LSM_HOOK(int, perf_event_alloc, struct perf_event *event)
+LSM_HOOK(void, perf_event_free, struct perf_event *event)
+LSM_HOOK(int, perf_event_read, struct perf_event *event)
+LSM_HOOK(int, perf_event_write, struct perf_event *event)
+#endif
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 20d8cf194fb7..905954c650ff 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1456,625 +1456,15 @@
* @what: kernel feature being accessed
*/
union security_list_options {
- int (*binder_set_context_mgr)(struct task_struct *mgr);
- int (*binder_transaction)(struct task_struct *from,
- struct task_struct *to);
- int (*binder_transfer_binder)(struct task_struct *from,
- struct task_struct *to);
- int (*binder_transfer_file)(struct task_struct *from,
- struct task_struct *to,
- struct file *file);
-
- int (*ptrace_access_check)(struct task_struct *child,
- unsigned int mode);
- int (*ptrace_traceme)(struct task_struct *parent);
- int (*capget)(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted);
- int (*capset)(struct cred *new, const struct cred *old,
- const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted);
- int (*capable)(const struct cred *cred,
- struct user_namespace *ns,
- int cap,
- unsigned int opts);
- int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
- int (*quota_on)(struct dentry *dentry);
- int (*syslog)(int type);
- int (*settime)(const struct timespec64 *ts, const struct timezone *tz);
- int (*vm_enough_memory)(struct mm_struct *mm, long pages);
-
- int (*bprm_set_creds)(struct linux_binprm *bprm);
- int (*bprm_check_security)(struct linux_binprm *bprm);
- void (*bprm_committing_creds)(struct linux_binprm *bprm);
- void (*bprm_committed_creds)(struct linux_binprm *bprm);
-
- int (*fs_context_dup)(struct fs_context *fc, struct fs_context *src_sc);
- int (*fs_context_parse_param)(struct fs_context *fc, struct fs_parameter *param);
-
- int (*sb_alloc_security)(struct super_block *sb);
- void (*sb_free_security)(struct super_block *sb);
- void (*sb_free_mnt_opts)(void *mnt_opts);
- int (*sb_eat_lsm_opts)(char *orig, void **mnt_opts);
- int (*sb_remount)(struct super_block *sb, void *mnt_opts);
- int (*sb_kern_mount)(struct super_block *sb);
- int (*sb_show_options)(struct seq_file *m, struct super_block *sb);
- int (*sb_statfs)(struct dentry *dentry);
- int (*sb_mount)(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data);
- int (*sb_umount)(struct vfsmount *mnt, int flags);
- int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
- int (*sb_set_mnt_opts)(struct super_block *sb,
- void *mnt_opts,
- unsigned long kern_flags,
- unsigned long *set_kern_flags);
- int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
- struct super_block *newsb,
- unsigned long kern_flags,
- unsigned long *set_kern_flags);
- int (*sb_add_mnt_opt)(const char *option, const char *val, int len,
- void **mnt_opts);
- int (*move_mount)(const struct path *from_path, const struct path *to_path);
- int (*dentry_init_security)(struct dentry *dentry, int mode,
- const struct qstr *name, void **ctx,
- u32 *ctxlen);
- int (*dentry_create_files_as)(struct dentry *dentry, int mode,
- struct qstr *name,
- const struct cred *old,
- struct cred *new);
-
-
-#ifdef CONFIG_SECURITY_PATH
- int (*path_unlink)(const struct path *dir, struct dentry *dentry);
- int (*path_mkdir)(const struct path *dir, struct dentry *dentry,
- umode_t mode);
- int (*path_rmdir)(const struct path *dir, struct dentry *dentry);
- int (*path_mknod)(const struct path *dir, struct dentry *dentry,
- umode_t mode, unsigned int dev);
- int (*path_truncate)(const struct path *path);
- int (*path_symlink)(const struct path *dir, struct dentry *dentry,
- const char *old_name);
- int (*path_link)(struct dentry *old_dentry, const struct path *new_dir,
- struct dentry *new_dentry);
- int (*path_rename)(const struct path *old_dir, struct dentry *old_dentry,
- const struct path *new_dir,
- struct dentry *new_dentry);
- int (*path_chmod)(const struct path *path, umode_t mode);
- int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid);
- int (*path_chroot)(const struct path *path);
-#endif
- /* Needed for inode based security check */
- int (*path_notify)(const struct path *path, u64 mask,
- unsigned int obj_type);
- int (*inode_alloc_security)(struct inode *inode);
- void (*inode_free_security)(struct inode *inode);
- int (*inode_init_security)(struct inode *inode, struct inode *dir,
- const struct qstr *qstr,
- const char **name, void **value,
- size_t *len);
- int (*inode_create)(struct inode *dir, struct dentry *dentry,
- umode_t mode);
- int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
- struct dentry *new_dentry);
- int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
- int (*inode_symlink)(struct inode *dir, struct dentry *dentry,
- const char *old_name);
- int (*inode_mkdir)(struct inode *dir, struct dentry *dentry,
- umode_t mode);
- int (*inode_rmdir)(struct inode *dir, struct dentry *dentry);
- int (*inode_mknod)(struct inode *dir, struct dentry *dentry,
- umode_t mode, dev_t dev);
- int (*inode_rename)(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir,
- struct dentry *new_dentry);
- int (*inode_readlink)(struct dentry *dentry);
- int (*inode_follow_link)(struct dentry *dentry, struct inode *inode,
- bool rcu);
- int (*inode_permission)(struct inode *inode, int mask);
- int (*inode_setattr)(struct dentry *dentry, struct iattr *attr);
- int (*inode_getattr)(const struct path *path);
- int (*inode_setxattr)(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
- void (*inode_post_setxattr)(struct dentry *dentry, const char *name,
- const void *value, size_t size,
- int flags);
- int (*inode_getxattr)(struct dentry *dentry, const char *name);
- int (*inode_listxattr)(struct dentry *dentry);
- int (*inode_removexattr)(struct dentry *dentry, const char *name);
- int (*inode_need_killpriv)(struct dentry *dentry);
- int (*inode_killpriv)(struct dentry *dentry);
- int (*inode_getsecurity)(struct inode *inode, const char *name,
- void **buffer, bool alloc);
- int (*inode_setsecurity)(struct inode *inode, const char *name,
- const void *value, size_t size,
- int flags);
- int (*inode_listsecurity)(struct inode *inode, char *buffer,
- size_t buffer_size);
- void (*inode_getsecid)(struct inode *inode, u32 *secid);
- int (*inode_copy_up)(struct dentry *src, struct cred **new);
- int (*inode_copy_up_xattr)(const char *name);
-
- int (*kernfs_init_security)(struct kernfs_node *kn_dir,
- struct kernfs_node *kn);
-
- int (*file_permission)(struct file *file, int mask);
- int (*file_alloc_security)(struct file *file);
- void (*file_free_security)(struct file *file);
- int (*file_ioctl)(struct file *file, unsigned int cmd,
- unsigned long arg);
- int (*mmap_addr)(unsigned long addr);
- int (*mmap_file)(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags);
- int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
- unsigned long prot);
- int (*file_lock)(struct file *file, unsigned int cmd);
- int (*file_fcntl)(struct file *file, unsigned int cmd,
- unsigned long arg);
- void (*file_set_fowner)(struct file *file);
- int (*file_send_sigiotask)(struct task_struct *tsk,
- struct fown_struct *fown, int sig);
- int (*file_receive)(struct file *file);
- int (*file_open)(struct file *file);
-
- int (*task_alloc)(struct task_struct *task, unsigned long clone_flags);
- void (*task_free)(struct task_struct *task);
- int (*cred_alloc_blank)(struct cred *cred, gfp_t gfp);
- void (*cred_free)(struct cred *cred);
- int (*cred_prepare)(struct cred *new, const struct cred *old,
- gfp_t gfp);
- void (*cred_transfer)(struct cred *new, const struct cred *old);
- void (*cred_getsecid)(const struct cred *c, u32 *secid);
- int (*kernel_act_as)(struct cred *new, u32 secid);
- int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
- int (*kernel_module_request)(char *kmod_name);
- int (*kernel_load_data)(enum kernel_load_data_id id);
- int (*kernel_read_file)(struct file *file, enum kernel_read_file_id id);
- int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size,
- enum kernel_read_file_id id);
- int (*task_fix_setuid)(struct cred *new, const struct cred *old,
- int flags);
- int (*task_setpgid)(struct task_struct *p, pid_t pgid);
- int (*task_getpgid)(struct task_struct *p);
- int (*task_getsid)(struct task_struct *p);
- void (*task_getsecid)(struct task_struct *p, u32 *secid);
- int (*task_setnice)(struct task_struct *p, int nice);
- int (*task_setioprio)(struct task_struct *p, int ioprio);
- int (*task_getioprio)(struct task_struct *p);
- int (*task_prlimit)(const struct cred *cred, const struct cred *tcred,
- unsigned int flags);
- int (*task_setrlimit)(struct task_struct *p, unsigned int resource,
- struct rlimit *new_rlim);
- int (*task_setscheduler)(struct task_struct *p);
- int (*task_getscheduler)(struct task_struct *p);
- int (*task_movememory)(struct task_struct *p);
- int (*task_kill)(struct task_struct *p, struct kernel_siginfo *info,
- int sig, const struct cred *cred);
- int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5);
- void (*task_to_inode)(struct task_struct *p, struct inode *inode);
-
- int (*ipc_permission)(struct kern_ipc_perm *ipcp, short flag);
- void (*ipc_getsecid)(struct kern_ipc_perm *ipcp, u32 *secid);
-
- int (*msg_msg_alloc_security)(struct msg_msg *msg);
- void (*msg_msg_free_security)(struct msg_msg *msg);
-
- int (*msg_queue_alloc_security)(struct kern_ipc_perm *perm);
- void (*msg_queue_free_security)(struct kern_ipc_perm *perm);
- int (*msg_queue_associate)(struct kern_ipc_perm *perm, int msqflg);
- int (*msg_queue_msgctl)(struct kern_ipc_perm *perm, int cmd);
- int (*msg_queue_msgsnd)(struct kern_ipc_perm *perm, struct msg_msg *msg,
- int msqflg);
- int (*msg_queue_msgrcv)(struct kern_ipc_perm *perm, struct msg_msg *msg,
- struct task_struct *target, long type,
- int mode);
-
- int (*shm_alloc_security)(struct kern_ipc_perm *perm);
- void (*shm_free_security)(struct kern_ipc_perm *perm);
- int (*shm_associate)(struct kern_ipc_perm *perm, int shmflg);
- int (*shm_shmctl)(struct kern_ipc_perm *perm, int cmd);
- int (*shm_shmat)(struct kern_ipc_perm *perm, char __user *shmaddr,
- int shmflg);
-
- int (*sem_alloc_security)(struct kern_ipc_perm *perm);
- void (*sem_free_security)(struct kern_ipc_perm *perm);
- int (*sem_associate)(struct kern_ipc_perm *perm, int semflg);
- int (*sem_semctl)(struct kern_ipc_perm *perm, int cmd);
- int (*sem_semop)(struct kern_ipc_perm *perm, struct sembuf *sops,
- unsigned nsops, int alter);
-
- int (*netlink_send)(struct sock *sk, struct sk_buff *skb);
-
- void (*d_instantiate)(struct dentry *dentry, struct inode *inode);
-
- int (*getprocattr)(struct task_struct *p, char *name, char **value);
- int (*setprocattr)(const char *name, void *value, size_t size);
- int (*ismaclabel)(const char *name);
- int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen);
- int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid);
- void (*release_secctx)(char *secdata, u32 seclen);
-
- void (*inode_invalidate_secctx)(struct inode *inode);
- int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
- int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
- int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
-
-#ifdef CONFIG_SECURITY_NETWORK
- int (*unix_stream_connect)(struct sock *sock, struct sock *other,
- struct sock *newsk);
- int (*unix_may_send)(struct socket *sock, struct socket *other);
-
- int (*socket_create)(int family, int type, int protocol, int kern);
- int (*socket_post_create)(struct socket *sock, int family, int type,
- int protocol, int kern);
- int (*socket_socketpair)(struct socket *socka, struct socket *sockb);
- int (*socket_bind)(struct socket *sock, struct sockaddr *address,
- int addrlen);
- int (*socket_connect)(struct socket *sock, struct sockaddr *address,
- int addrlen);
- int (*socket_listen)(struct socket *sock, int backlog);
- int (*socket_accept)(struct socket *sock, struct socket *newsock);
- int (*socket_sendmsg)(struct socket *sock, struct msghdr *msg,
- int size);
- int (*socket_recvmsg)(struct socket *sock, struct msghdr *msg,
- int size, int flags);
- int (*socket_getsockname)(struct socket *sock);
- int (*socket_getpeername)(struct socket *sock);
- int (*socket_getsockopt)(struct socket *sock, int level, int optname);
- int (*socket_setsockopt)(struct socket *sock, int level, int optname);
- int (*socket_shutdown)(struct socket *sock, int how);
- int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
- int (*socket_getpeersec_stream)(struct socket *sock,
- char __user *optval,
- int __user *optlen, unsigned len);
- int (*socket_getpeersec_dgram)(struct socket *sock,
- struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security)(struct sock *sk, int family, gfp_t priority);
- void (*sk_free_security)(struct sock *sk);
- void (*sk_clone_security)(const struct sock *sk, struct sock *newsk);
- void (*sk_getsecid)(struct sock *sk, u32 *secid);
- void (*sock_graft)(struct sock *sk, struct socket *parent);
- int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
- struct request_sock *req);
- void (*inet_csk_clone)(struct sock *newsk,
- const struct request_sock *req);
- void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
- int (*secmark_relabel_packet)(u32 secid);
- void (*secmark_refcount_inc)(void);
- void (*secmark_refcount_dec)(void);
- void (*req_classify_flow)(const struct request_sock *req,
- struct flowi *fl);
- int (*tun_dev_alloc_security)(void **security);
- void (*tun_dev_free_security)(void *security);
- int (*tun_dev_create)(void);
- int (*tun_dev_attach_queue)(void *security);
- int (*tun_dev_attach)(struct sock *sk, void *security);
- int (*tun_dev_open)(void *security);
- int (*sctp_assoc_request)(struct sctp_endpoint *ep,
- struct sk_buff *skb);
- int (*sctp_bind_connect)(struct sock *sk, int optname,
- struct sockaddr *address, int addrlen);
- void (*sctp_sk_clone)(struct sctp_endpoint *ep, struct sock *sk,
- struct sock *newsk);
-#endif /* CONFIG_SECURITY_NETWORK */
-
-#ifdef CONFIG_SECURITY_INFINIBAND
- int (*ib_pkey_access)(void *sec, u64 subnet_prefix, u16 pkey);
- int (*ib_endport_manage_subnet)(void *sec, const char *dev_name,
- u8 port_num);
- int (*ib_alloc_security)(void **sec);
- void (*ib_free_security)(void *sec);
-#endif /* CONFIG_SECURITY_INFINIBAND */
-
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
- int (*xfrm_policy_alloc_security)(struct xfrm_sec_ctx **ctxp,
- struct xfrm_user_sec_ctx *sec_ctx,
- gfp_t gfp);
- int (*xfrm_policy_clone_security)(struct xfrm_sec_ctx *old_ctx,
- struct xfrm_sec_ctx **new_ctx);
- void (*xfrm_policy_free_security)(struct xfrm_sec_ctx *ctx);
- int (*xfrm_policy_delete_security)(struct xfrm_sec_ctx *ctx);
- int (*xfrm_state_alloc)(struct xfrm_state *x,
- struct xfrm_user_sec_ctx *sec_ctx);
- int (*xfrm_state_alloc_acquire)(struct xfrm_state *x,
- struct xfrm_sec_ctx *polsec,
- u32 secid);
- void (*xfrm_state_free_security)(struct xfrm_state *x);
- int (*xfrm_state_delete_security)(struct xfrm_state *x);
- int (*xfrm_policy_lookup)(struct xfrm_sec_ctx *ctx, u32 fl_secid,
- u8 dir);
- int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
- struct xfrm_policy *xp,
- const struct flowi *fl);
- int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-
- /* key management security hooks */
-#ifdef CONFIG_KEYS
- int (*key_alloc)(struct key *key, const struct cred *cred,
- unsigned long flags);
- void (*key_free)(struct key *key);
- int (*key_permission)(key_ref_t key_ref, const struct cred *cred,
- unsigned perm);
- int (*key_getsecurity)(struct key *key, char **_buffer);
-#endif /* CONFIG_KEYS */
-
-#ifdef CONFIG_AUDIT
- int (*audit_rule_init)(u32 field, u32 op, char *rulestr,
- void **lsmrule);
- int (*audit_rule_known)(struct audit_krule *krule);
- int (*audit_rule_match)(u32 secid, u32 field, u32 op, void *lsmrule);
- void (*audit_rule_free)(void *lsmrule);
-#endif /* CONFIG_AUDIT */
-
-#ifdef CONFIG_BPF_SYSCALL
- int (*bpf)(int cmd, union bpf_attr *attr,
- unsigned int size);
- int (*bpf_map)(struct bpf_map *map, fmode_t fmode);
- int (*bpf_prog)(struct bpf_prog *prog);
- int (*bpf_map_alloc_security)(struct bpf_map *map);
- void (*bpf_map_free_security)(struct bpf_map *map);
- int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
- void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
-#endif /* CONFIG_BPF_SYSCALL */
- int (*locked_down)(enum lockdown_reason what);
-#ifdef CONFIG_PERF_EVENTS
- int (*perf_event_open)(struct perf_event_attr *attr, int type);
- int (*perf_event_alloc)(struct perf_event *event);
- void (*perf_event_free)(struct perf_event *event);
- int (*perf_event_read)(struct perf_event *event);
- int (*perf_event_write)(struct perf_event *event);
-
-#endif
+ #define LSM_HOOK(RET, NAME, ...) RET (*NAME)(__VA_ARGS__);
+ #include "lsm_hook_names.h"
+ #undef LSM_HOOK
};
struct security_hook_heads {
- struct hlist_head binder_set_context_mgr;
- struct hlist_head binder_transaction;
- struct hlist_head binder_transfer_binder;
- struct hlist_head binder_transfer_file;
- struct hlist_head ptrace_access_check;
- struct hlist_head ptrace_traceme;
- struct hlist_head capget;
- struct hlist_head capset;
- struct hlist_head capable;
- struct hlist_head quotactl;
- struct hlist_head quota_on;
- struct hlist_head syslog;
- struct hlist_head settime;
- struct hlist_head vm_enough_memory;
- struct hlist_head bprm_set_creds;
- struct hlist_head bprm_check_security;
- struct hlist_head bprm_committing_creds;
- struct hlist_head bprm_committed_creds;
- struct hlist_head fs_context_dup;
- struct hlist_head fs_context_parse_param;
- struct hlist_head sb_alloc_security;
- struct hlist_head sb_free_security;
- struct hlist_head sb_free_mnt_opts;
- struct hlist_head sb_eat_lsm_opts;
- struct hlist_head sb_remount;
- struct hlist_head sb_kern_mount;
- struct hlist_head sb_show_options;
- struct hlist_head sb_statfs;
- struct hlist_head sb_mount;
- struct hlist_head sb_umount;
- struct hlist_head sb_pivotroot;
- struct hlist_head sb_set_mnt_opts;
- struct hlist_head sb_clone_mnt_opts;
- struct hlist_head sb_add_mnt_opt;
- struct hlist_head move_mount;
- struct hlist_head dentry_init_security;
- struct hlist_head dentry_create_files_as;
-#ifdef CONFIG_SECURITY_PATH
- struct hlist_head path_unlink;
- struct hlist_head path_mkdir;
- struct hlist_head path_rmdir;
- struct hlist_head path_mknod;
- struct hlist_head path_truncate;
- struct hlist_head path_symlink;
- struct hlist_head path_link;
- struct hlist_head path_rename;
- struct hlist_head path_chmod;
- struct hlist_head path_chown;
- struct hlist_head path_chroot;
-#endif
- /* Needed for inode based modules as well */
- struct hlist_head path_notify;
- struct hlist_head inode_alloc_security;
- struct hlist_head inode_free_security;
- struct hlist_head inode_init_security;
- struct hlist_head inode_create;
- struct hlist_head inode_link;
- struct hlist_head inode_unlink;
- struct hlist_head inode_symlink;
- struct hlist_head inode_mkdir;
- struct hlist_head inode_rmdir;
- struct hlist_head inode_mknod;
- struct hlist_head inode_rename;
- struct hlist_head inode_readlink;
- struct hlist_head inode_follow_link;
- struct hlist_head inode_permission;
- struct hlist_head inode_setattr;
- struct hlist_head inode_getattr;
- struct hlist_head inode_setxattr;
- struct hlist_head inode_post_setxattr;
- struct hlist_head inode_getxattr;
- struct hlist_head inode_listxattr;
- struct hlist_head inode_removexattr;
- struct hlist_head inode_need_killpriv;
- struct hlist_head inode_killpriv;
- struct hlist_head inode_getsecurity;
- struct hlist_head inode_setsecurity;
- struct hlist_head inode_listsecurity;
- struct hlist_head inode_getsecid;
- struct hlist_head inode_copy_up;
- struct hlist_head inode_copy_up_xattr;
- struct hlist_head kernfs_init_security;
- struct hlist_head file_permission;
- struct hlist_head file_alloc_security;
- struct hlist_head file_free_security;
- struct hlist_head file_ioctl;
- struct hlist_head mmap_addr;
- struct hlist_head mmap_file;
- struct hlist_head file_mprotect;
- struct hlist_head file_lock;
- struct hlist_head file_fcntl;
- struct hlist_head file_set_fowner;
- struct hlist_head file_send_sigiotask;
- struct hlist_head file_receive;
- struct hlist_head file_open;
- struct hlist_head task_alloc;
- struct hlist_head task_free;
- struct hlist_head cred_alloc_blank;
- struct hlist_head cred_free;
- struct hlist_head cred_prepare;
- struct hlist_head cred_transfer;
- struct hlist_head cred_getsecid;
- struct hlist_head kernel_act_as;
- struct hlist_head kernel_create_files_as;
- struct hlist_head kernel_load_data;
- struct hlist_head kernel_read_file;
- struct hlist_head kernel_post_read_file;
- struct hlist_head kernel_module_request;
- struct hlist_head task_fix_setuid;
- struct hlist_head task_setpgid;
- struct hlist_head task_getpgid;
- struct hlist_head task_getsid;
- struct hlist_head task_getsecid;
- struct hlist_head task_setnice;
- struct hlist_head task_setioprio;
- struct hlist_head task_getioprio;
- struct hlist_head task_prlimit;
- struct hlist_head task_setrlimit;
- struct hlist_head task_setscheduler;
- struct hlist_head task_getscheduler;
- struct hlist_head task_movememory;
- struct hlist_head task_kill;
- struct hlist_head task_prctl;
- struct hlist_head task_to_inode;
- struct hlist_head ipc_permission;
- struct hlist_head ipc_getsecid;
- struct hlist_head msg_msg_alloc_security;
- struct hlist_head msg_msg_free_security;
- struct hlist_head msg_queue_alloc_security;
- struct hlist_head msg_queue_free_security;
- struct hlist_head msg_queue_associate;
- struct hlist_head msg_queue_msgctl;
- struct hlist_head msg_queue_msgsnd;
- struct hlist_head msg_queue_msgrcv;
- struct hlist_head shm_alloc_security;
- struct hlist_head shm_free_security;
- struct hlist_head shm_associate;
- struct hlist_head shm_shmctl;
- struct hlist_head shm_shmat;
- struct hlist_head sem_alloc_security;
- struct hlist_head sem_free_security;
- struct hlist_head sem_associate;
- struct hlist_head sem_semctl;
- struct hlist_head sem_semop;
- struct hlist_head netlink_send;
- struct hlist_head d_instantiate;
- struct hlist_head getprocattr;
- struct hlist_head setprocattr;
- struct hlist_head ismaclabel;
- struct hlist_head secid_to_secctx;
- struct hlist_head secctx_to_secid;
- struct hlist_head release_secctx;
- struct hlist_head inode_invalidate_secctx;
- struct hlist_head inode_notifysecctx;
- struct hlist_head inode_setsecctx;
- struct hlist_head inode_getsecctx;
-#ifdef CONFIG_SECURITY_NETWORK
- struct hlist_head unix_stream_connect;
- struct hlist_head unix_may_send;
- struct hlist_head socket_create;
- struct hlist_head socket_post_create;
- struct hlist_head socket_socketpair;
- struct hlist_head socket_bind;
- struct hlist_head socket_connect;
- struct hlist_head socket_listen;
- struct hlist_head socket_accept;
- struct hlist_head socket_sendmsg;
- struct hlist_head socket_recvmsg;
- struct hlist_head socket_getsockname;
- struct hlist_head socket_getpeername;
- struct hlist_head socket_getsockopt;
- struct hlist_head socket_setsockopt;
- struct hlist_head socket_shutdown;
- struct hlist_head socket_sock_rcv_skb;
- struct hlist_head socket_getpeersec_stream;
- struct hlist_head socket_getpeersec_dgram;
- struct hlist_head sk_alloc_security;
- struct hlist_head sk_free_security;
- struct hlist_head sk_clone_security;
- struct hlist_head sk_getsecid;
- struct hlist_head sock_graft;
- struct hlist_head inet_conn_request;
- struct hlist_head inet_csk_clone;
- struct hlist_head inet_conn_established;
- struct hlist_head secmark_relabel_packet;
- struct hlist_head secmark_refcount_inc;
- struct hlist_head secmark_refcount_dec;
- struct hlist_head req_classify_flow;
- struct hlist_head tun_dev_alloc_security;
- struct hlist_head tun_dev_free_security;
- struct hlist_head tun_dev_create;
- struct hlist_head tun_dev_attach_queue;
- struct hlist_head tun_dev_attach;
- struct hlist_head tun_dev_open;
- struct hlist_head sctp_assoc_request;
- struct hlist_head sctp_bind_connect;
- struct hlist_head sctp_sk_clone;
-#endif /* CONFIG_SECURITY_NETWORK */
-#ifdef CONFIG_SECURITY_INFINIBAND
- struct hlist_head ib_pkey_access;
- struct hlist_head ib_endport_manage_subnet;
- struct hlist_head ib_alloc_security;
- struct hlist_head ib_free_security;
-#endif /* CONFIG_SECURITY_INFINIBAND */
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
- struct hlist_head xfrm_policy_alloc_security;
- struct hlist_head xfrm_policy_clone_security;
- struct hlist_head xfrm_policy_free_security;
- struct hlist_head xfrm_policy_delete_security;
- struct hlist_head xfrm_state_alloc;
- struct hlist_head xfrm_state_alloc_acquire;
- struct hlist_head xfrm_state_free_security;
- struct hlist_head xfrm_state_delete_security;
- struct hlist_head xfrm_policy_lookup;
- struct hlist_head xfrm_state_pol_flow_match;
- struct hlist_head xfrm_decode_session;
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-#ifdef CONFIG_KEYS
- struct hlist_head key_alloc;
- struct hlist_head key_free;
- struct hlist_head key_permission;
- struct hlist_head key_getsecurity;
-#endif /* CONFIG_KEYS */
-#ifdef CONFIG_AUDIT
- struct hlist_head audit_rule_init;
- struct hlist_head audit_rule_known;
- struct hlist_head audit_rule_match;
- struct hlist_head audit_rule_free;
-#endif /* CONFIG_AUDIT */
-#ifdef CONFIG_BPF_SYSCALL
- struct hlist_head bpf;
- struct hlist_head bpf_map;
- struct hlist_head bpf_prog;
- struct hlist_head bpf_map_alloc_security;
- struct hlist_head bpf_map_free_security;
- struct hlist_head bpf_prog_alloc_security;
- struct hlist_head bpf_prog_free_security;
-#endif /* CONFIG_BPF_SYSCALL */
- struct hlist_head locked_down;
-#ifdef CONFIG_PERF_EVENTS
- struct hlist_head perf_event_open;
- struct hlist_head perf_event_alloc;
- struct hlist_head perf_event_free;
- struct hlist_head perf_event_read;
- struct hlist_head perf_event_write;
-#endif
+ #define LSM_HOOK(RET, NAME, ...) struct hlist_head NAME;
+ #include "lsm_hook_names.h"
+ #undef LSM_HOOK
} __randomize_layout;
/*
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 3/8] bpf: lsm: provide attachment points for BPF LSM programs
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
The BPF LSM programs are implemented as fexit trampolines to avoid the
overhead of retpolines. These programs cannot be attached to security_*
wrappers as there are quite a few security_* functions that do more than
just calling the LSM callbacks.
This was discussed on the lists in:
https://lore.kernel.org/bpf/20200123152440.28956-1-kpsingh@chromium.org/T/#m068becce588a0cdf01913f368a97aea4c62d8266
Adding a NOP callback after all the static LSM callbacks are called has
the following benefits:
- The BPF programs run at the right stage of the security_* wrappers.
- They run after all the static LSM hooks allowed the operation,
therefore cannot allow an action that was already denied.
There are some hooks which do not call call_int_hooks or
call_void_hooks. It's not possible to call the bpf_lsm_* functions
without checking if there is BPF LSM program attached to these hooks.
This is added further in a subsequent patch. For now, these hooks are
marked as NO_BPF (i.e. attachment of BPF programs is not possible).
Signed-off-by: KP Singh <kpsingh@google.com>
---
include/linux/bpf_lsm.h | 34 ++++++++++++++++++++++++++++++++++
kernel/bpf/bpf_lsm.c | 16 ++++++++++++++++
security/security.c | 3 +++
3 files changed, 53 insertions(+)
create mode 100644 include/linux/bpf_lsm.h
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
new file mode 100644
index 000000000000..f867f72f6aa9
--- /dev/null
+++ b/include/linux/bpf_lsm.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+
+#ifndef _LINUX_BPF_LSM_H
+#define _LINUX_BPF_LSM_H
+
+#include <linux/bpf.h>
+
+#ifdef CONFIG_BPF_LSM
+
+#define LSM_HOOK(RET, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__);
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
+
+#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...) bpf_lsm_##FUNC(__VA_ARGS__)
+#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) ({ \
+ do { \
+ if (RC == 0) \
+ RC = bpf_lsm_##FUNC(__VA_ARGS__); \
+ } while (0); \
+ RC; \
+})
+
+#else /* !CONFIG_BPF_LSM */
+
+#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) (RC)
+#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...)
+
+#endif /* CONFIG_BPF_LSM */
+
+#endif /* _LINUX_BPF_LSM_H */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index affb6941622e..abc847c9b9a1 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -7,6 +7,22 @@
#include <linux/filter.h>
#include <linux/bpf.h>
#include <linux/btf.h>
+#include <linux/bpf_lsm.h>
+
+/* For every LSM hook that allows attachment of BPF programs, declare a NOP
+ * function where a BPF program can be attached as an fexit trampoline.
+ */
+#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__)
+#define LSM_HOOK_int(NAME, ...) noinline int bpf_lsm_##NAME(__VA_ARGS__) \
+{ \
+ return 0; \
+}
+
+#define LSM_HOOK_void(NAME, ...) \
+ noinline void bpf_lsm_##NAME(__VA_ARGS__) {}
+
+#include <linux/lsm_hook_names.h>
+#undef LSM_HOOK
const struct bpf_prog_ops lsm_prog_ops = {
};
diff --git a/security/security.c b/security/security.c
index 565bc9b67276..aa111392a700 100644
--- a/security/security.c
+++ b/security/security.c
@@ -28,6 +28,7 @@
#include <linux/string.h>
#include <linux/msg.h>
#include <net/flow.h>
+#include <linux/bpf_lsm.h>
#define MAX_LSM_EVM_XATTR 2
@@ -684,6 +685,7 @@ static void __init lsm_early_task(struct task_struct *task)
\
hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \
P->hook.FUNC(__VA_ARGS__); \
+ RUN_BPF_LSM_VOID_PROGS(FUNC, __VA_ARGS__); \
} while (0)
#define call_int_hook(FUNC, IRC, ...) ({ \
@@ -696,6 +698,7 @@ static void __init lsm_early_task(struct task_struct *task)
if (RC != 0) \
break; \
} \
+ RC = RUN_BPF_LSM_INT_PROGS(RC, FUNC, __VA_ARGS__); \
} while (0); \
RC; \
})
--
2.20.1
^ permalink raw reply related
* [PATCH bpf-next v4 1/8] bpf: Introduce BPF_PROG_TYPE_LSM
From: KP Singh @ 2020-02-20 17:52 UTC (permalink / raw)
To: linux-kernel, bpf, linux-security-module
Cc: Brendan Jackman, Florent Revest, Thomas Garnier,
Alexei Starovoitov, Daniel Borkmann, James Morris, Kees Cook,
Thomas Garnier, Michael Halcrow, Paul Turner, Brendan Gregg,
Jann Horn, Matthew Garrett, Christian Brauner, Florent Revest,
Brendan Jackman, Martin KaFai Lau, Song Liu, Yonghong Song,
Serge E. Hallyn, David S. Miller, Greg Kroah-Hartman,
Nicolas Ferre, Stanislav Fomichev, Quentin Monnet, Andrey Ignatov,
Joe Stringer
In-Reply-To: <20200220175250.10795-1-kpsingh@chromium.org>
From: KP Singh <kpsingh@google.com>
Introduce types and configs for bpf programs that can be attached to
LSM hooks. The programs can be enabled by the config option
CONFIG_BPF_LSM.
Signed-off-by: KP Singh <kpsingh@google.com>
Reviewed-by: Brendan Jackman <jackmanb@google.com>
Reviewed-by: Florent Revest <revest@google.com>
Reviewed-by: Thomas Garnier <thgarnie@google.com>
---
MAINTAINERS | 1 +
include/linux/bpf.h | 3 +++
include/linux/bpf_types.h | 4 ++++
include/uapi/linux/bpf.h | 2 ++
init/Kconfig | 11 +++++++++++
kernel/bpf/Makefile | 1 +
kernel/bpf/bpf_lsm.c | 17 +++++++++++++++++
kernel/trace/bpf_trace.c | 12 ++++++------
tools/include/uapi/linux/bpf.h | 2 ++
tools/lib/bpf/libbpf_probes.c | 1 +
10 files changed, 48 insertions(+), 6 deletions(-)
create mode 100644 kernel/bpf/bpf_lsm.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a0d86490c2c6..0f603e8928d5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3147,6 +3147,7 @@ R: Martin KaFai Lau <kafai@fb.com>
R: Song Liu <songliubraving@fb.com>
R: Yonghong Song <yhs@fb.com>
R: Andrii Nakryiko <andriin@fb.com>
+R: KP Singh <kpsingh@chromium.org>
L: netdev@vger.kernel.org
L: bpf@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 49b1a70e12c8..c647cef3f4c1 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1429,6 +1429,9 @@ extern const struct bpf_func_proto bpf_strtoul_proto;
extern const struct bpf_func_proto bpf_tcp_sock_proto;
extern const struct bpf_func_proto bpf_jiffies64_proto;
+const struct bpf_func_proto *bpf_tracing_func_proto(
+ enum bpf_func_id func_id, const struct bpf_prog *prog);
+
/* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void);
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index c81d4ece79a4..ba0c2d56f8a3 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -70,6 +70,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
void *, void *)
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
void *, void *)
+#ifdef CONFIG_BPF_LSM
+BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
+ void *, void *)
+#endif /* CONFIG_BPF_LSM */
#endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f1d74a2bd234..2f1e24a8c4a4 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
+ BPF_PROG_TYPE_LSM,
};
enum bpf_attach_type {
@@ -210,6 +211,7 @@ enum bpf_attach_type {
BPF_TRACE_RAW_TP,
BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT,
+ BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/init/Kconfig b/init/Kconfig
index 452bc1835cd4..7d5db2982875 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1617,6 +1617,17 @@ config KALLSYMS_BASE_RELATIVE
# end of the "standard kernel features (expert users)" menu
# syscall, maps, verifier
+
+config BPF_LSM
+ bool "LSM Instrumentation with BPF"
+ depends on BPF_SYSCALL
+ help
+ This enables instrumentation of the security hooks with eBPF programs.
+ The programs are executed after all the statically defined LSM hooks
+ allow the action.
+
+ If you are unsure how to answer this question, answer N.
+
config BPF_SYSCALL
bool "Enable bpf() system call"
select BPF
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 046ce5d98033..f2d7be596966 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
endif
ifeq ($(CONFIG_BPF_JIT),y)
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
+obj-${CONFIG_BPF_LSM} += bpf_lsm.o
endif
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
new file mode 100644
index 000000000000..affb6941622e
--- /dev/null
+++ b/kernel/bpf/bpf_lsm.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2019 Google LLC.
+ */
+
+#include <linux/filter.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
+
+const struct bpf_prog_ops lsm_prog_ops = {
+};
+
+const struct bpf_verifier_ops lsm_verifier_ops = {
+ .get_func_proto = bpf_tracing_func_proto,
+ .is_valid_access = btf_ctx_access,
+};
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4ddd5ac46094..a69cb8a0042d 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -781,8 +781,8 @@ static const struct bpf_func_proto bpf_send_signal_thread_proto = {
.arg1_type = ARG_ANYTHING,
};
-static const struct bpf_func_proto *
-tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+const struct bpf_func_proto *
+bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
@@ -865,7 +865,7 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_override_return_proto;
#endif
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -975,7 +975,7 @@ tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_tp;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -1041,7 +1041,7 @@ pe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_perf_prog_read_value:
return &bpf_perf_prog_read_value_proto;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
@@ -1168,7 +1168,7 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_get_stack:
return &bpf_get_stack_proto_raw_tp;
default:
- return tracing_func_proto(func_id, prog);
+ return bpf_tracing_func_proto(func_id, prog);
}
}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index f1d74a2bd234..2f1e24a8c4a4 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -181,6 +181,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
+ BPF_PROG_TYPE_LSM,
};
enum bpf_attach_type {
@@ -210,6 +211,7 @@ enum bpf_attach_type {
BPF_TRACE_RAW_TP,
BPF_TRACE_FENTRY,
BPF_TRACE_FEXIT,
+ BPF_LSM_MAC,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index b782ebef6ac9..2c92059c0c90 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -108,6 +108,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
+ case BPF_PROG_TYPE_LSM:
default:
break;
}
--
2.20.1
^ permalink raw reply related
* Re: [RFC PATCH] security,anon_inodes,kvm: enable security support for anon inodes
From: Casey Schaufler @ 2020-02-20 18:11 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-fsdevel, viro
Cc: selinux, kvm, dancol, nnk, Stephen Smalley
In-Reply-To: <CAHC9VhSsjrgu2Jn+yiV5Bz_wt2x5bgEXdhjqLA+duWYNo4gOtw@mail.gmail.com>
On 2/17/2020 4:14 PM, Paul Moore wrote:
> On Thu, Feb 13, 2020 at 2:41 PM Stephen Smalley <sds@tycho.nsa.gov> wrote:
>> Add support for labeling and controlling access to files attached to anon
>> inodes. Introduce extended interfaces for creating such files to permit
>> passing a related file as an input to decide how to label the anon
>> inode. Define a security hook for initializing the anon inode security
>> attributes. Security attributes are either inherited from a related file
>> or determined based on some combination of the creating task and policy
>> (in the case of SELinux, using type_transition rules). As an
>> example user of the inheritance support, convert kvm to use the new
>> interface for passing the related file so that the anon inode can inherit
>> the security attributes of /dev/kvm and provide consistent access control
>> for subsequent ioctl operations. Other users of anon inodes, including
>> userfaultfd, will default to the transition-based mechanism instead.
>>
>> Compared to the series in
>> https://lore.kernel.org/selinux/20200211225547.235083-1-dancol@google.com/,
>> this approach differs in that it does not require creation of a separate
>> anonymous inode for each file (instead storing the per-instance security
>> information in the file security blob), it applies labeling and control
>> to all users of anonymous inodes rather than requiring opt-in via a new
>> flag, it supports labeling based on a related inode if provided,
>> it relies on type transitions to compute the label of the anon inode
>> when there is no related inode, and it does not require introducing a new
>> security class for each user of anonymous inodes.
>>
>> On the other hand, the approach in this patch does expose the name passed
>> by the creator of the anon inode to the policy (an indirect mapping could
>> be provided within SELinux if these names aren't considered to be stable),
>> requires the definition of type_transition rules to distinguish userfaultfd
>> inodes from proc inodes based on type since they share the same class,
>> doesn't support denying the creation of anonymous inodes (making the hook
>> added by this patch return something other than void is problematic due to
>> it being called after the file is already allocated and error handling in
>> the callers can't presently account for this scenario and end up calling
>> release methods multiple times), and may be more expensive
>> (security_transition_sid overhead on each anon inode allocation).
>>
>> We are primarily posting this RFC patch now so that the two different
>> approaches can be concretely compared. We anticipate a hybrid of the
>> two approaches being the likely outcome in the end. In particular
>> if support for allocating a separate inode for each of these files
>> is acceptable, then we would favor storing the security information
>> in the inode security blob and using it instead of the file security
>> blob.
> Bringing this back up in hopes of attracting some attention from the
> fs-devel crowd and Al. As Stephen already mentioned, from a SELinux
> perspective we would prefer to attach the security blob to the inode
> as opposed to the file struct; does anyone have any objections to
> that?
Sorry for the delay - been sick the past few days.
I agree that the inode is a better place than the file for information
about the inode. This is especially true for Smack, which uses
multiple extended attributes in some cases. I don't believe that any
except the access label will be relevant to anonymous inodes, but
I can imagine security modules with policies that would.
I am always an advocate of full xattr support. It goes a long
way in reducing the number and complexity of special case interfaces.
^ permalink raw reply
* Re: [PATCH v26 10/22] x86/sgx: Linux Enclave Driver
From: Sean Christopherson @ 2020-02-20 18:13 UTC (permalink / raw)
To: Jordan Hand
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes, cedric.xing,
puiterwijk, linux-security-module, Suresh Siddha, Haitao Huang
In-Reply-To: <15074c16-4832-456d-dd12-af8548e46d6d@linux.microsoft.com>
On Tue, Feb 18, 2020 at 07:26:31PM -0800, Jordan Hand wrote:
> I ran our validation tests for the Open Enclave SDK against this patch
> set and came across a potential issue.
>
> On 2/9/20 1:25 PM, Jarkko Sakkinen wrote:
> > +/**
> > + * sgx_encl_may_map() - Check if a requested VMA mapping is allowed
> > + * @encl: an enclave
> > + * @start: lower bound of the address range, inclusive
> > + * @end: upper bound of the address range, exclusive
> > + * @vm_prot_bits: requested protections of the address range
> > + *
> > + * Iterate through the enclave pages contained within [@start, @end) to verify
> > + * the permissions requested by @vm_prot_bits do not exceed that of any enclave
> > + * page to be mapped. Page addresses that do not have an associated enclave
> > + * page are interpreted to zero permissions.
> > + *
> > + * Return:
> > + * 0 on success,
> > + * -EACCES if VMA permissions exceed enclave page permissions
> > + */
> > +int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start,
> > + unsigned long end, unsigned long vm_prot_bits)
> > +{
> > + unsigned long idx, idx_start, idx_end;
> > + struct sgx_encl_page *page;
> > +
> > + /* PROT_NONE always succeeds. */
> > + if (!vm_prot_bits)
> > + return 0;
> > +
> > + idx_start = PFN_DOWN(start);
> > + idx_end = PFN_DOWN(end - 1);
> > +
> > + for (idx = idx_start; idx <= idx_end; ++idx) {
> > + mutex_lock(&encl->lock);
> > + page = radix_tree_lookup(&encl->page_tree, idx);
> > + mutex_unlock(&encl->lock);
> > +
> > + if (!page || (~page->vm_max_prot_bits & vm_prot_bits))
> > + return -EACCES;
> > + }
> > +
> > + return 0;
> > +}
> > +static struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl,
> > + unsigned long offset,
> > + u64 secinfo_flags)
> > +{
> > + struct sgx_encl_page *encl_page;
> > + unsigned long prot;
> > +
> > + encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL);
> > + if (!encl_page)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + encl_page->desc = encl->base + offset;
> > + encl_page->encl = encl;
> > +
> > + prot = _calc_vm_trans(secinfo_flags, SGX_SECINFO_R, PROT_READ) |
> > + _calc_vm_trans(secinfo_flags, SGX_SECINFO_W, PROT_WRITE) |
> > + _calc_vm_trans(secinfo_flags, SGX_SECINFO_X, PROT_EXEC);
> > +
> > + /*
> > + * TCS pages must always RW set for CPU access while the SECINFO
> > + * permissions are *always* zero - the CPU ignores the user provided
> > + * values and silently overwrites them with zero permissions.
> > + */
> > + if ((secinfo_flags & SGX_SECINFO_PAGE_TYPE_MASK) == SGX_SECINFO_TCS)
> > + prot |= PROT_READ | PROT_WRITE;
> > +
> > + /* Calculate maximum of the VM flags for the page. */
> > + encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
>
> During mprotect (in mm/mprotect.c line 525) the following checks if
> READ_IMPLIES_EXECUTE and a PROT_READ is being requested. If so and
> VM_MAYEXEC is set, it also adds PROT_EXEC to the request.
>
> if (rier && (vma->vm_flags & VM_MAYEXEC))
> prot |= PROT_EXEC;
>
> But if we look at sgx_encl_page_alloc(), we see vm_max_prot_bits is set
> without taking VM_MAYEXEC into account:
>
> encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
>
> sgx_encl_may_map() checks that the requested protection can be added with:
>
> if (!page || (~page->vm_max_prot_bits & vm_prot_bits))
> return -EACCESS
>
> This means that for any process where READ_IMPLIES_EXECUTE is set and
> page where (vma->vm_flags & VM_MAYEXEC) == true, mmap/mprotect calls to
> that request PROT_READ on a page that was not added with PROT_EXEC will
> fail.
I could've sworn this was discussed on the SGX list at one point, but
apparently we only discussed it internally. Anyways...
More than likely, the READ_IMPLIES_EXECUTE (RIE) crud rears its head
because part of the enclave loader is written in assembly. Unless
explicitly told otherwise, the linker assumes that any program with
assembly code may need an executable stack, which leads to the RIE
personality being set for the process. Here's a fantastic write up for
more details: https://www.airs.com/blog/archives/518
There are essentially two paths we can take:
1) Exempt EPC pages from RIE during mmap()/mprotect(), i.e. don't add
PROT_EXEC for enclaves.
2) Punt the issue to userspace.
Option (1) is desirable in some ways:
- Enclaves will get an executable stack if and only if the loader/creator
intentionally configures it to have an executable stack.
- Separates enclaves from the personality of the loader.
- Userspace doesn't have to do anything for the common case of not
wanting an executable stack for its enclaves.
The big down side to (1) is that it'd require an ugly hook in architecture
agnostic code. And arguably, it reduces the overall security of the
platform (more below).
For (2), userspace has a few options:
a) Tell the linker the enclave loader doesn't need RIE, either via a .note
in assembly files or via the global "-z noexecstack" flag.
b) Spawn a separate process to run/map the enclave if the enclave loader
needs RIE.
c) Require enclaves to allow PROT_EXEC on all pages. Note, this is an
absolutely terrible idea and only included for completeness.
As shown by the lack of a mmap()/mprotect() hook in this series to squash
RIE, we chose option (2). Given that enclave loaders are not legacy code
and hopefully following decent coding practices, option (2a) should suffice
for all loaders. The security benefit mentioned above is that forcing
enclave loaders to squash RIE eliminates an exectuable stack as an attack
vector on the loader.
If for some reason a loader "needs" an executable stack, requiring such a
beast to jump through a few hoops to run sane enclaves doesn't seem too
onerous.
This obviously needs to be called out in the kernel docs.
^ permalink raw reply
* Re: [PATCH v26 10/22] x86/sgx: Linux Enclave Driver
From: Jordan Hand @ 2020-02-20 18:33 UTC (permalink / raw)
To: Sean Christopherson
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes, cedric.xing,
puiterwijk, linux-security-module, Suresh Siddha, Haitao Huang
In-Reply-To: <20200220181345.GD3972@linux.intel.com>
On 2/20/20 10:13 AM, Sean Christopherson wrote:
> On Tue, Feb 18, 2020 at 07:26:31PM -0800, Jordan Hand wrote:
>> During mprotect (in mm/mprotect.c line 525) the following checks if
>> READ_IMPLIES_EXECUTE and a PROT_READ is being requested. If so and
>> VM_MAYEXEC is set, it also adds PROT_EXEC to the request.
>>
>> if (rier && (vma->vm_flags & VM_MAYEXEC))
>> prot |= PROT_EXEC;
>>
>> But if we look at sgx_encl_page_alloc(), we see vm_max_prot_bits is set
>> without taking VM_MAYEXEC into account:
>>
>> encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
>>
>> sgx_encl_may_map() checks that the requested protection can be added with:
>>
>> if (!page || (~page->vm_max_prot_bits & vm_prot_bits))
>> return -EACCESS
>>
>> This means that for any process where READ_IMPLIES_EXECUTE is set and
>> page where (vma->vm_flags & VM_MAYEXEC) == true, mmap/mprotect calls to
>> that request PROT_READ on a page that was not added with PROT_EXEC will
>> fail.
>
> I could've sworn this was discussed on the SGX list at one point, but
> apparently we only discussed it internally. Anyways...
>
> More than likely, the READ_IMPLIES_EXECUTE (RIE) crud rears its head
> because part of the enclave loader is written in assembly. Unless
> explicitly told otherwise, the linker assumes that any program with
> assembly code may need an executable stack, which leads to the RIE
> personality being set for the process. Here's a fantastic write up for
> more details: https://www.airs.com/blog/archives/518
>
> There are essentially two paths we can take:
>
> 1) Exempt EPC pages from RIE during mmap()/mprotect(), i.e. don't add
> PROT_EXEC for enclaves.
>
> 2) Punt the issue to userspace.
>
> Option (1) is desirable in some ways:
>
> - Enclaves will get an executable stack if and only if the loader/creator
> intentionally configures it to have an executable stack.
>
> - Separates enclaves from the personality of the loader.
>
> - Userspace doesn't have to do anything for the common case of not
> wanting an executable stack for its enclaves.
>
> The big down side to (1) is that it'd require an ugly hook in architecture
> agnostic code. And arguably, it reduces the overall security of the
> platform (more below).
>
> For (2), userspace has a few options:
>
> a) Tell the linker the enclave loader doesn't need RIE, either via a .note
> in assembly files or via the global "-z noexecstack" flag.
>
> b) Spawn a separate process to run/map the enclave if the enclave loader
> needs RIE.
>
> c) Require enclaves to allow PROT_EXEC on all pages. Note, this is an
> absolutely terrible idea and only included for completeness.
>
> As shown by the lack of a mmap()/mprotect() hook in this series to squash
> RIE, we chose option (2). Given that enclave loaders are not legacy code
> and hopefully following decent coding practices, option (2a) should suffice
> for all loaders. The security benefit mentioned above is that forcing
> enclave loaders to squash RIE eliminates an exectuable stack as an attack
> vector on the loader.
I see your point and I do agree that there are security benefits to (2a)
and I think we could do that for our loader. That said, it does concern
me that this breaks perfectly valid userspace behavior. If a userspace
process decides to use RIE, I don't know that the SGX driver should
disobey that decision.
So option (3) would be to just honor RIE for enclave pages and when page
permissions are set to PROT_READ in sgx_encl_page_alloc and RIE is set,
also add PROT_EXEC.
I understand your concerns that this using RIE is bad security practice
and I'm not convinced that (3) is the way to go, but from a philosophy
perspective I don't know that the kernel should be in the business of
stopping userspace from doing valid things.
If option (3) can't/shouldn't be done for some reason, option (1) at
least keeps from breaking expected userspace behavior. But I do agree
that (1) is ugly to implement.
-Jordan
^ permalink raw reply
* Re: [PATCH v26 10/22] x86/sgx: Linux Enclave Driver
From: Sean Christopherson @ 2020-02-20 18:48 UTC (permalink / raw)
To: Jordan Hand
Cc: Jarkko Sakkinen, linux-kernel, x86, linux-sgx, akpm, dave.hansen,
nhorman, npmccallum, haitao.huang, andriy.shevchenko, tglx,
kai.svahn, bp, josh, luto, kai.huang, rientjes, cedric.xing,
puiterwijk, linux-security-module, Suresh Siddha, Haitao Huang
In-Reply-To: <7738b3cf-fb32-5306-5740-59974444e327@linux.microsoft.com>
On Thu, Feb 20, 2020 at 10:33:36AM -0800, Jordan Hand wrote:
> On 2/20/20 10:13 AM, Sean Christopherson wrote:
> > There are essentially two paths we can take:
> >
> > 1) Exempt EPC pages from RIE during mmap()/mprotect(), i.e. don't add
> > PROT_EXEC for enclaves.
> >
> > 2) Punt the issue to userspace.
> >
> > Option (1) is desirable in some ways:
> >
> > - Enclaves will get an executable stack if and only if the loader/creator
> > intentionally configures it to have an executable stack.
> >
> > - Separates enclaves from the personality of the loader.
> >
> > - Userspace doesn't have to do anything for the common case of not
> > wanting an executable stack for its enclaves.
> >
> > The big down side to (1) is that it'd require an ugly hook in architecture
> > agnostic code. And arguably, it reduces the overall security of the
> > platform (more below).
> >
> > For (2), userspace has a few options:
> >
> > a) Tell the linker the enclave loader doesn't need RIE, either via a .note
> > in assembly files or via the global "-z noexecstack" flag.
> >
> > b) Spawn a separate process to run/map the enclave if the enclave loader
> > needs RIE.
> >
> > c) Require enclaves to allow PROT_EXEC on all pages. Note, this is an
> > absolutely terrible idea and only included for completeness.
> >
> > As shown by the lack of a mmap()/mprotect() hook in this series to squash
> > RIE, we chose option (2). Given that enclave loaders are not legacy code
> > and hopefully following decent coding practices, option (2a) should suffice
> > for all loaders. The security benefit mentioned above is that forcing
> > enclave loaders to squash RIE eliminates an exectuable stack as an attack
> > vector on the loader.
>
> I see your point and I do agree that there are security benefits to (2a)
> and I think we could do that for our loader. That said, it does concern
> me that this breaks perfectly valid userspace behavior. If a userspace
> process decides to use RIE, I don't know that the SGX driver should
> disobey that decision.
>
> So option (3) would be to just honor RIE for enclave pages and when page
> permissions are set to PROT_READ in sgx_encl_page_alloc and RIE is set,
> also add PROT_EXEC.
Ah, right, IIRC that idea also came up in our internal discussions. Note,
SGX would need to implement this option by checking for RIE in
sgx_encl_may_map(), as the process that built the enclave may not be the
same process that is running the enclave.
> I understand your concerns that this using RIE is bad security practice
> and I'm not convinced that (3) is the way to go, but from a philosophy
> perspective I don't know that the kernel should be in the business of
> stopping userspace from doing valid things.
>
> If option (3) can't/shouldn't be done for some reason, option (1) at
> least keeps from breaking expected userspace behavior. But I do agree
> that (1) is ugly to implement.
My biggest concern for allowing PROT_EXEC if RIE is that it would result
in #PF(SGX) (#GP on Skylake) due to an EPCM violation if the enclave
actually tried to execute from such a page. This isn't a problem for the
kernel as the fault will be reported cleanly through the vDSO (or get
delivered as a SIGSEGV if the enclave isn't entered through the vDSO), but
it's a bit weird for userspace as userspace will see the #PF(SGX) and
likely assume the EPC was lost, e.g. silently restart the enclave instead
of logging an error that the enclave is broken.
^ permalink raw reply
* Re: [RFC PATCH] security,anon_inodes,kvm: enable security support for anon inodes
From: Daniel Colascione @ 2020-02-20 18:50 UTC (permalink / raw)
To: Casey Schaufler
Cc: Paul Moore, LSM List, Linux FS Devel, Al Viro, SElinux list, kvm,
Nick Kralevich, Stephen Smalley
In-Reply-To: <eb2dbe22-91af-17c6-3dfb-d9ec619a4d7a@schaufler-ca.com>
On Thu, Feb 20, 2020 at 10:11 AM Casey Schaufler <casey@schaufler-ca.com> wrote:
>
> On 2/17/2020 4:14 PM, Paul Moore wrote:
> > On Thu, Feb 13, 2020 at 2:41 PM Stephen Smalley <sds@tycho.nsa.gov> wrote:
> >> Add support for labeling and controlling access to files attached to anon
> >> inodes. Introduce extended interfaces for creating such files to permit
> >> passing a related file as an input to decide how to label the anon
> >> inode. Define a security hook for initializing the anon inode security
> >> attributes. Security attributes are either inherited from a related file
> >> or determined based on some combination of the creating task and policy
> >> (in the case of SELinux, using type_transition rules). As an
> >> example user of the inheritance support, convert kvm to use the new
> >> interface for passing the related file so that the anon inode can inherit
> >> the security attributes of /dev/kvm and provide consistent access control
> >> for subsequent ioctl operations. Other users of anon inodes, including
> >> userfaultfd, will default to the transition-based mechanism instead.
> >>
> >> Compared to the series in
> >> https://lore.kernel.org/selinux/20200211225547.235083-1-dancol@google.com/,
> >> this approach differs in that it does not require creation of a separate
> >> anonymous inode for each file (instead storing the per-instance security
> >> information in the file security blob), it applies labeling and control
> >> to all users of anonymous inodes rather than requiring opt-in via a new
> >> flag, it supports labeling based on a related inode if provided,
> >> it relies on type transitions to compute the label of the anon inode
> >> when there is no related inode, and it does not require introducing a new
> >> security class for each user of anonymous inodes.
> >>
> >> On the other hand, the approach in this patch does expose the name passed
> >> by the creator of the anon inode to the policy (an indirect mapping could
> >> be provided within SELinux if these names aren't considered to be stable),
> >> requires the definition of type_transition rules to distinguish userfaultfd
> >> inodes from proc inodes based on type since they share the same class,
> >> doesn't support denying the creation of anonymous inodes (making the hook
> >> added by this patch return something other than void is problematic due to
> >> it being called after the file is already allocated and error handling in
> >> the callers can't presently account for this scenario and end up calling
> >> release methods multiple times), and may be more expensive
> >> (security_transition_sid overhead on each anon inode allocation).
> >>
> >> We are primarily posting this RFC patch now so that the two different
> >> approaches can be concretely compared. We anticipate a hybrid of the
> >> two approaches being the likely outcome in the end. In particular
> >> if support for allocating a separate inode for each of these files
> >> is acceptable, then we would favor storing the security information
> >> in the inode security blob and using it instead of the file security
> >> blob.
> > Bringing this back up in hopes of attracting some attention from the
> > fs-devel crowd and Al. As Stephen already mentioned, from a SELinux
> > perspective we would prefer to attach the security blob to the inode
> > as opposed to the file struct; does anyone have any objections to
> > that?
>
> Sorry for the delay - been sick the past few days.
>
> I agree that the inode is a better place than the file for information
> about the inode. This is especially true for Smack, which uses
> multiple extended attributes in some cases. I don't believe that any
> except the access label will be relevant to anonymous inodes, but
> I can imagine security modules with policies that would.
>
> I am always an advocate of full xattr support. It goes a long
> way in reducing the number and complexity of special case interfaces.
It sounds like we have broad consensus on using the inode to hold
security information, implying that anon_inodes should create new
inodes. Do any of the VFS people want to object?
^ permalink raw reply
* Re: [PATCH v26 10/22] x86/sgx: Linux Enclave Driver
From: Andy Lutomirski @ 2020-02-20 18:51 UTC (permalink / raw)
To: Sean Christopherson
Cc: Jordan Hand, Jarkko Sakkinen, LKML, X86 ML, linux-sgx,
Andrew Morton, Dave Hansen, Neil Horman, npmccallum,
Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
Borislav Petkov, Josh Triplett, Andrew Lutomirski, Huang, Kai,
David Rientjes, Xing, Cedric, puiterwijk, LSM List, Suresh Siddha,
Haitao Huang
In-Reply-To: <20200220181345.GD3972@linux.intel.com>
On Thu, Feb 20, 2020 at 10:13 AM Sean Christopherson
<sean.j.christopherson@intel.com> wrote:
>
> On Tue, Feb 18, 2020 at 07:26:31PM -0800, Jordan Hand wrote:
> > I ran our validation tests for the Open Enclave SDK against this patch
> > set and came across a potential issue.
> >
> > On 2/9/20 1:25 PM, Jarkko Sakkinen wrote:
> > > +/**
> > > + * sgx_encl_may_map() - Check if a requested VMA mapping is allowed
> > > + * @encl: an enclave
> > > + * @start: lower bound of the address range, inclusive
> > > + * @end: upper bound of the address range, exclusive
> > > + * @vm_prot_bits: requested protections of the address range
> > > + *
> > > + * Iterate through the enclave pages contained within [@start, @end) to verify
> > > + * the permissions requested by @vm_prot_bits do not exceed that of any enclave
> > > + * page to be mapped. Page addresses that do not have an associated enclave
> > > + * page are interpreted to zero permissions.
> > > + *
> > > + * Return:
> > > + * 0 on success,
> > > + * -EACCES if VMA permissions exceed enclave page permissions
> > > + */
> > > +int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start,
> > > + unsigned long end, unsigned long vm_prot_bits)
> > > +{
> > > + unsigned long idx, idx_start, idx_end;
> > > + struct sgx_encl_page *page;
> > > +
> > > + /* PROT_NONE always succeeds. */
> > > + if (!vm_prot_bits)
> > > + return 0;
> > > +
> > > + idx_start = PFN_DOWN(start);
> > > + idx_end = PFN_DOWN(end - 1);
> > > +
> > > + for (idx = idx_start; idx <= idx_end; ++idx) {
> > > + mutex_lock(&encl->lock);
> > > + page = radix_tree_lookup(&encl->page_tree, idx);
> > > + mutex_unlock(&encl->lock);
> > > +
> > > + if (!page || (~page->vm_max_prot_bits & vm_prot_bits))
> > > + return -EACCES;
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +static struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl,
> > > + unsigned long offset,
> > > + u64 secinfo_flags)
> > > +{
> > > + struct sgx_encl_page *encl_page;
> > > + unsigned long prot;
> > > +
> > > + encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL);
> > > + if (!encl_page)
> > > + return ERR_PTR(-ENOMEM);
> > > +
> > > + encl_page->desc = encl->base + offset;
> > > + encl_page->encl = encl;
> > > +
> > > + prot = _calc_vm_trans(secinfo_flags, SGX_SECINFO_R, PROT_READ) |
> > > + _calc_vm_trans(secinfo_flags, SGX_SECINFO_W, PROT_WRITE) |
> > > + _calc_vm_trans(secinfo_flags, SGX_SECINFO_X, PROT_EXEC);
> > > +
> > > + /*
> > > + * TCS pages must always RW set for CPU access while the SECINFO
> > > + * permissions are *always* zero - the CPU ignores the user provided
> > > + * values and silently overwrites them with zero permissions.
> > > + */
> > > + if ((secinfo_flags & SGX_SECINFO_PAGE_TYPE_MASK) == SGX_SECINFO_TCS)
> > > + prot |= PROT_READ | PROT_WRITE;
> > > +
> > > + /* Calculate maximum of the VM flags for the page. */
> > > + encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
> >
> > During mprotect (in mm/mprotect.c line 525) the following checks if
> > READ_IMPLIES_EXECUTE and a PROT_READ is being requested. If so and
> > VM_MAYEXEC is set, it also adds PROT_EXEC to the request.
> >
> > if (rier && (vma->vm_flags & VM_MAYEXEC))
> > prot |= PROT_EXEC;
> >
> > But if we look at sgx_encl_page_alloc(), we see vm_max_prot_bits is set
> > without taking VM_MAYEXEC into account:
> >
> > encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
> >
> > sgx_encl_may_map() checks that the requested protection can be added with:
> >
> > if (!page || (~page->vm_max_prot_bits & vm_prot_bits))
> > return -EACCESS
> >
> > This means that for any process where READ_IMPLIES_EXECUTE is set and
> > page where (vma->vm_flags & VM_MAYEXEC) == true, mmap/mprotect calls to
> > that request PROT_READ on a page that was not added with PROT_EXEC will
> > fail.
>
> I could've sworn this was discussed on the SGX list at one point, but
> apparently we only discussed it internally. Anyways...
>
> More than likely, the READ_IMPLIES_EXECUTE (RIE) crud rears its head
> because part of the enclave loader is written in assembly. Unless
> explicitly told otherwise, the linker assumes that any program with
> assembly code may need an executable stack, which leads to the RIE
> personality being set for the process. Here's a fantastic write up for
> more details: https://www.airs.com/blog/archives/518
>
> There are essentially two paths we can take:
>
> 1) Exempt EPC pages from RIE during mmap()/mprotect(), i.e. don't add
> PROT_EXEC for enclaves.
Seems reasonable.
Honestly, it probably makes sense to try to exempt almost everything
from RIE. I'd be a bit surprised if RIE is actually useful for
anything other than plain anonymous pages and private file mappings.
^ permalink raw reply
* Re: [PATCH v26 10/22] x86/sgx: Linux Enclave Driver
From: Sean Christopherson @ 2020-02-20 19:15 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Jordan Hand, Jarkko Sakkinen, LKML, X86 ML, linux-sgx,
Andrew Morton, Dave Hansen, Neil Horman, npmccallum,
Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
Xing, Cedric, puiterwijk, LSM List, Suresh Siddha, Haitao Huang
In-Reply-To: <CALCETrVXnSR8fBQtqv=3zFxJCFhcHE-6XNAy3suPW+uPgFvfvg@mail.gmail.com>
On Thu, Feb 20, 2020 at 10:51:37AM -0800, Andy Lutomirski wrote:
> On Thu, Feb 20, 2020 at 10:13 AM Sean Christopherson
> <sean.j.christopherson@intel.com> wrote:
> > More than likely, the READ_IMPLIES_EXECUTE (RIE) crud rears its head
> > because part of the enclave loader is written in assembly. Unless
> > explicitly told otherwise, the linker assumes that any program with
> > assembly code may need an executable stack, which leads to the RIE
> > personality being set for the process. Here's a fantastic write up for
> > more details: https://www.airs.com/blog/archives/518
> >
> > There are essentially two paths we can take:
> >
> > 1) Exempt EPC pages from RIE during mmap()/mprotect(), i.e. don't add
> > PROT_EXEC for enclaves.
>
> Seems reasonable.
>
> Honestly, it probably makes sense to try to exempt almost everything
> from RIE. I'd be a bit surprised if RIE is actually useful for
> anything other than plain anonymous pages and private file mappings.
Hmm, last I looked at this I was focused on adding a generic protections
manipulator, e.g. vm_ops->mprotect_adjust() and f_op->???, and I thought
those options were too ugly to pursue.
But if we want to start killing RIE specifically, adding a boolean flag
to and f_op wouldn't be _that_ heinous, e.g.
static int do_mprotect_pkey(...)
{
...
/* Does the application expect PROT_READ to imply PROT_EXEC */
if (rier && (vma->vm_flags & VM_MAYEXEC) &&
(!vma->vm_file || !vma->vm_file->f_op->no_read_implies_exec))
prot |= PROT_EXEC;
}
unsigned long do_mmap(...)
{
if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
if (!file || (!path_noexec(&file->f_path) &&
!file->f_op->no_read_implies_exec))
prot |= PROT_EXEC;
}
^ permalink raw reply
* [PATCH 0/7] proc: Dentry flushing without proc_mnt
From: Eric W. Biederman @ 2020-02-20 20:46 UTC (permalink / raw)
To: Linus Torvalds
Cc: Al Viro, LKML, Kernel Hardening, Linux API, Linux FS Devel,
Linux Security Module, Akinobu Mita, Alexey Dobriyan,
Andrew Morton, Andy Lutomirski, Daniel Micay, Djalal Harouni,
Dmitry V . Levin, Greg Kroah-Hartman, Ingo Molnar,
J . Bruce Fields, Jeff Layton, Jonathan Corbet, Kees Cook,
Oleg Nesterov, Solar Designer
In-Reply-To: <87pnejf6fz.fsf@x220.int.ebiederm.org>
Just because it is less of a fundamental change and less testing I went
and looked at updating proc_flush_task to use a list as Al suggested.
If we can stand an sget/deactivate_super pair for every dentry we want
to invalidate I think I have something.
Comments from anyone will be appreciated I gave this some light testing
and the code is based on something similar already present in proc so
I think there is a high chance this code is correct but I could easily
be wrong.
Linus, does this approach look like something you can stand?
Eric
Eric W. Biederman (7):
proc: Rename in proc_inode rename sysctl_inodes sibling_inodes
proc: Generalize proc_sys_prune_dcache into proc_prune_siblings_dcache
proc: Mov rcu_read_(lock|unlock) in proc_prune_siblings_dcache
proc: Use d_invalidate in proc_prune_siblings_dcache
proc: Clear the pieces of proc_inode that proc_evict_inode cares about
proc: Use a list of inodes to flush from proc
proc: Ensure we see the exit of each process tid exactly once
fs/exec.c | 5 +--
fs/proc/base.c | 111 ++++++++++++++++--------------------------------
fs/proc/inode.c | 60 +++++++++++++++++++++++---
fs/proc/internal.h | 4 +-
fs/proc/proc_sysctl.c | 45 +++-----------------
include/linux/pid.h | 2 +
include/linux/proc_fs.h | 4 +-
kernel/exit.c | 4 +-
kernel/pid.c | 16 +++++++
9 files changed, 124 insertions(+), 127 deletions(-)
^ permalink raw reply
* [PATCH 1/7] proc: Rename in proc_inode rename sysctl_inodes sibling_inodes
From: Eric W. Biederman @ 2020-02-20 20:47 UTC (permalink / raw)
To: Linus Torvalds
Cc: Al Viro, LKML, Kernel Hardening, Linux API, Linux FS Devel,
Linux Security Module, Akinobu Mita, Alexey Dobriyan,
Andrew Morton, Andy Lutomirski, Daniel Micay, Djalal Harouni,
Dmitry V . Levin, Greg Kroah-Hartman, Ingo Molnar,
J . Bruce Fields, Jeff Layton, Jonathan Corbet, Kees Cook,
Oleg Nesterov, Solar Designer
In-Reply-To: <871rqpaswu.fsf_-_@x220.int.ebiederm.org>
I about to need and use the same functionality for pid based
inodes and there is no point in adding a second field when
this field is already here and serving the same purporse.
Just give the field a generic name so it is clear that
it is no longer sysctl specific.
Also for good measure initialize sibling_inodes when
proc_inode is initialized.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
---
fs/proc/inode.c | 1 +
fs/proc/internal.h | 2 +-
fs/proc/proc_sysctl.c | 8 ++++----
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 6da18316d209..bdae442d5262 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -68,6 +68,7 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
ei->pde = NULL;
ei->sysctl = NULL;
ei->sysctl_entry = NULL;
+ INIT_HLIST_NODE(&ei->sibling_inodes);
ei->ns_ops = NULL;
return &ei->vfs_inode;
}
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 41587276798e..366cd3aa690b 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -91,7 +91,7 @@ struct proc_inode {
struct proc_dir_entry *pde;
struct ctl_table_header *sysctl;
struct ctl_table *sysctl_entry;
- struct hlist_node sysctl_inodes;
+ struct hlist_node sibling_inodes;
const struct proc_ns_operations *ns_ops;
struct inode vfs_inode;
} __randomize_layout;
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index c75bb4632ed1..42fbb7f3c587 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -279,9 +279,9 @@ static void proc_sys_prune_dcache(struct ctl_table_header *head)
node = hlist_first_rcu(&head->inodes);
if (!node)
break;
- ei = hlist_entry(node, struct proc_inode, sysctl_inodes);
+ ei = hlist_entry(node, struct proc_inode, sibling_inodes);
spin_lock(&sysctl_lock);
- hlist_del_init_rcu(&ei->sysctl_inodes);
+ hlist_del_init_rcu(&ei->sibling_inodes);
spin_unlock(&sysctl_lock);
inode = &ei->vfs_inode;
@@ -483,7 +483,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
}
ei->sysctl = head;
ei->sysctl_entry = table;
- hlist_add_head_rcu(&ei->sysctl_inodes, &head->inodes);
+ hlist_add_head_rcu(&ei->sibling_inodes, &head->inodes);
head->count++;
spin_unlock(&sysctl_lock);
@@ -514,7 +514,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head)
{
spin_lock(&sysctl_lock);
- hlist_del_init_rcu(&PROC_I(inode)->sysctl_inodes);
+ hlist_del_init_rcu(&PROC_I(inode)->sibling_inodes);
if (!--head->count)
kfree_rcu(head, rcu);
spin_unlock(&sysctl_lock);
--
2.20.1
^ permalink raw reply related
* [PATCH 2/7] proc: Generalize proc_sys_prune_dcache into proc_prune_siblings_dcache
From: Eric W. Biederman @ 2020-02-20 20:48 UTC (permalink / raw)
To: Linus Torvalds
Cc: Al Viro, LKML, Kernel Hardening, Linux API, Linux FS Devel,
Linux Security Module, Akinobu Mita, Alexey Dobriyan,
Andrew Morton, Andy Lutomirski, Daniel Micay, Djalal Harouni,
Dmitry V . Levin, Greg Kroah-Hartman, Ingo Molnar,
J . Bruce Fields, Jeff Layton, Jonathan Corbet, Kees Cook,
Oleg Nesterov, Solar Designer
In-Reply-To: <871rqpaswu.fsf_-_@x220.int.ebiederm.org>
This prepares the way for allowing the pid part of proc to use this
dcache pruning code as well.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
---
fs/proc/inode.c | 38 ++++++++++++++++++++++++++++++++++++++
fs/proc/internal.h | 1 +
fs/proc/proc_sysctl.c | 35 +----------------------------------
3 files changed, 40 insertions(+), 34 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index bdae442d5262..74ce4a8d05eb 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -103,6 +103,44 @@ void __init proc_init_kmemcache(void)
BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE);
}
+void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
+{
+ struct inode *inode;
+ struct proc_inode *ei;
+ struct hlist_node *node;
+ struct super_block *sb;
+
+ rcu_read_lock();
+ for (;;) {
+ node = hlist_first_rcu(inodes);
+ if (!node)
+ break;
+ ei = hlist_entry(node, struct proc_inode, sibling_inodes);
+ spin_lock(lock);
+ hlist_del_init_rcu(&ei->sibling_inodes);
+ spin_unlock(lock);
+
+ inode = &ei->vfs_inode;
+ sb = inode->i_sb;
+ if (!atomic_inc_not_zero(&sb->s_active))
+ continue;
+ inode = igrab(inode);
+ rcu_read_unlock();
+ if (unlikely(!inode)) {
+ deactivate_super(sb);
+ rcu_read_lock();
+ continue;
+ }
+
+ d_prune_aliases(inode);
+ iput(inode);
+ deactivate_super(sb);
+
+ rcu_read_lock();
+ }
+ rcu_read_unlock();
+}
+
static int proc_show_options(struct seq_file *seq, struct dentry *root)
{
struct super_block *sb = root->d_sb;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 366cd3aa690b..ba9a991824a5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -210,6 +210,7 @@ extern const struct inode_operations proc_pid_link_inode_operations;
extern const struct super_operations proc_sops;
void proc_init_kmemcache(void);
+void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock);
void set_proc_pid_nlink(void);
extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
extern void proc_entry_rundown(struct proc_dir_entry *);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 42fbb7f3c587..5da9d7f7ae34 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -269,40 +269,7 @@ static void unuse_table(struct ctl_table_header *p)
static void proc_sys_prune_dcache(struct ctl_table_header *head)
{
- struct inode *inode;
- struct proc_inode *ei;
- struct hlist_node *node;
- struct super_block *sb;
-
- rcu_read_lock();
- for (;;) {
- node = hlist_first_rcu(&head->inodes);
- if (!node)
- break;
- ei = hlist_entry(node, struct proc_inode, sibling_inodes);
- spin_lock(&sysctl_lock);
- hlist_del_init_rcu(&ei->sibling_inodes);
- spin_unlock(&sysctl_lock);
-
- inode = &ei->vfs_inode;
- sb = inode->i_sb;
- if (!atomic_inc_not_zero(&sb->s_active))
- continue;
- inode = igrab(inode);
- rcu_read_unlock();
- if (unlikely(!inode)) {
- deactivate_super(sb);
- rcu_read_lock();
- continue;
- }
-
- d_prune_aliases(inode);
- iput(inode);
- deactivate_super(sb);
-
- rcu_read_lock();
- }
- rcu_read_unlock();
+ proc_prune_siblings_dcache(&head->inodes, &sysctl_lock);
}
/* called under sysctl_lock, will reacquire if has to wait */
--
2.20.1
^ permalink raw reply related
* [PATCH 3/7] proc: Mov rcu_read_(lock|unlock) in proc_prune_siblings_dcache
From: Eric W. Biederman @ 2020-02-20 20:49 UTC (permalink / raw)
To: Linus Torvalds
Cc: Al Viro, LKML, Kernel Hardening, Linux API, Linux FS Devel,
Linux Security Module, Akinobu Mita, Alexey Dobriyan,
Andrew Morton, Andy Lutomirski, Daniel Micay, Djalal Harouni,
Dmitry V . Levin, Greg Kroah-Hartman, Ingo Molnar,
J . Bruce Fields, Jeff Layton, Jonathan Corbet, Kees Cook,
Oleg Nesterov, Solar Designer
In-Reply-To: <871rqpaswu.fsf_-_@x220.int.ebiederm.org>
Don't make it look like rcu_read_lock is held over the entire loop
instead just take the rcu_read_lock over the part of the loop that
matters. This makes the intent of the code a little clearer.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
fs/proc/inode.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 74ce4a8d05eb..38a7baa41aba 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -110,11 +110,13 @@ void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
struct hlist_node *node;
struct super_block *sb;
- rcu_read_lock();
for (;;) {
+ rcu_read_lock();
node = hlist_first_rcu(inodes);
- if (!node)
+ if (!node) {
+ rcu_read_unlock();
break;
+ }
ei = hlist_entry(node, struct proc_inode, sibling_inodes);
spin_lock(lock);
hlist_del_init_rcu(&ei->sibling_inodes);
@@ -122,23 +124,21 @@ void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
inode = &ei->vfs_inode;
sb = inode->i_sb;
- if (!atomic_inc_not_zero(&sb->s_active))
+ if (!atomic_inc_not_zero(&sb->s_active)) {
+ rcu_read_unlock();
continue;
+ }
inode = igrab(inode);
rcu_read_unlock();
if (unlikely(!inode)) {
deactivate_super(sb);
- rcu_read_lock();
continue;
}
d_prune_aliases(inode);
iput(inode);
deactivate_super(sb);
-
- rcu_read_lock();
}
- rcu_read_unlock();
}
static int proc_show_options(struct seq_file *seq, struct dentry *root)
--
2.20.1
^ permalink raw reply related
* [PATCH 4/7] proc: Use d_invalidate in proc_prune_siblings_dcache
From: Eric W. Biederman @ 2020-02-20 20:49 UTC (permalink / raw)
To: Linus Torvalds
Cc: Al Viro, LKML, Kernel Hardening, Linux API, Linux FS Devel,
Linux Security Module, Akinobu Mita, Alexey Dobriyan,
Andrew Morton, Andy Lutomirski, Daniel Micay, Djalal Harouni,
Dmitry V . Levin, Greg Kroah-Hartman, Ingo Molnar,
J . Bruce Fields, Jeff Layton, Jonathan Corbet, Kees Cook,
Oleg Nesterov, Solar Designer
In-Reply-To: <871rqpaswu.fsf_-_@x220.int.ebiederm.org>
The function d_prune_aliases has the problem that it will only prune
aliases thare are completely unused. It will not remove aliases for
the dcache or even think of removing mounts from the dcache. For that
behavior d_invalidate is needed.
To use d_invalidate replace d_prune_aliases with d_find_alias
followed by d_invalidate and dput. This is safe and complete
because no inode in proc has any hardlinks or aliases.
To make this behavior change clear rename
proc_prune_siblings_dache proc_invalidate_siblings_dcache,
and rename proc_sys_prune_dcache proc_sys_invalidate_dcache.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
fs/proc/inode.c | 9 +++++++--
fs/proc/internal.h | 2 +-
fs/proc/proc_sysctl.c | 8 ++++----
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 38a7baa41aba..c4528c419876 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -103,12 +103,13 @@ void __init proc_init_kmemcache(void)
BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE);
}
-void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
+void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
{
struct inode *inode;
struct proc_inode *ei;
struct hlist_node *node;
struct super_block *sb;
+ struct dentry *dentry;
for (;;) {
rcu_read_lock();
@@ -135,7 +136,11 @@ void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock)
continue;
}
- d_prune_aliases(inode);
+ dentry = d_find_alias(inode);
+ if (dentry) {
+ d_invalidate(dentry);
+ dput(dentry);
+ }
iput(inode);
deactivate_super(sb);
}
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index ba9a991824a5..fd470172675f 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -210,7 +210,7 @@ extern const struct inode_operations proc_pid_link_inode_operations;
extern const struct super_operations proc_sops;
void proc_init_kmemcache(void);
-void proc_prune_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock);
+void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock);
void set_proc_pid_nlink(void);
extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
extern void proc_entry_rundown(struct proc_dir_entry *);
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 5da9d7f7ae34..b6f5d459b087 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -267,9 +267,9 @@ static void unuse_table(struct ctl_table_header *p)
complete(p->unregistering);
}
-static void proc_sys_prune_dcache(struct ctl_table_header *head)
+static void proc_sys_invalidate_dcache(struct ctl_table_header *head)
{
- proc_prune_siblings_dcache(&head->inodes, &sysctl_lock);
+ proc_invalidate_siblings_dcache(&head->inodes, &sysctl_lock);
}
/* called under sysctl_lock, will reacquire if has to wait */
@@ -291,10 +291,10 @@ static void start_unregistering(struct ctl_table_header *p)
spin_unlock(&sysctl_lock);
}
/*
- * Prune dentries for unregistered sysctls: namespaced sysctls
+ * Invalidate dentries for unregistered sysctls: namespaced sysctls
* can have duplicate names and contaminate dcache very badly.
*/
- proc_sys_prune_dcache(p);
+ proc_sys_invalidate_dcache(p);
/*
* do not remove from the list until nobody holds it; walking the
* list in do_sysctl() relies on that.
--
2.20.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox