[RFC PATCH v3 3/4] X86/sgx: Introduce EMA as a new LSM module
Cedric Xing
cedric.xing at intel.com
Sun Jul 7 23:41:33 UTC 2019
As enclave pages have different lifespan than the existing MAP_PRIVATE and
MAP_SHARED pages, a new data structure is required outside of VMA to track
their protections and/or origins. Enclave Memory Area (or EMA for short) has
been introduced to address the need. EMAs are maintained by a new LSM module
named “ema”, which is similar to the idea of the “capability” LSM module.
This new “ema” module has LSM_ORDER_FIRST so will always be dispatched before
other LSM_ORDER_MUTABLE modules (e.g. selinux, apparmor, etc.). It is
responsible for initializing EMA maps, and inserting and freeing EMA nodes, and
offers APIs for other LSM modules to query/update EMAs. Details could be found
in include/linux/lsm_ema.h and security/commonema.c.
Signed-off-by: Cedric Xing <cedric.xing at intel.com>
---
include/linux/lsm_ema.h | 97 ++++++++++++++
security/Makefile | 1 +
security/commonema.c | 277 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 375 insertions(+)
create mode 100644 include/linux/lsm_ema.h
create mode 100644 security/commonema.c
diff --git a/include/linux/lsm_ema.h b/include/linux/lsm_ema.h
new file mode 100644
index 000000000000..59fc4ea6fa78
--- /dev/null
+++ b/include/linux/lsm_ema.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Enclave Memory Area interface for LSM modules
+ *
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+
+#ifndef _LSM_EMA_H_
+#define _LSM_EMA_H_
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+
+/**
+ * ema - Enclave Memory Area structure for LSM modules
+ *
+ * Data structure to track origins of enclave pages
+ *
+ * @link:
+ * Link to adjacent EMAs. EMAs are sorted by their addresses in ascending
+ * order
+ * @start:
+ * Starting address
+ * @end:
+ * Ending address
+ * @source:
+ * File from which this range was loaded from, or NULL if not loaded from
+ * any files
+ */
+struct ema {
+ struct list_head link;
+ size_t start;
+ size_t end;
+ struct file *source;
+};
+
+#define ema_data(ema, offset) \
+ ((void *)((char *)((struct ema *)(ema) + 1) + offset))
+
+/**
+ * ema_map - LSM Enclave Memory Map structure for LSM modules
+ *
+ * Container for EMAs of an enclave
+ *
+ * @list:
+ * Head of a list of sorted EMAs
+ * @lock:
+ * Acquire before querying/updateing the list EMAs
+ */
+struct ema_map {
+ struct list_head list;
+ struct mutex lock;
+};
+
+size_t __init ema_request_blob(size_t blob_size);
+struct ema_map *ema_get_map(struct file *encl);
+int ema_apply_to_range(struct ema_map *map, size_t start, size_t end,
+ int (*cb)(struct ema *ema, void *arg), void *arg);
+void ema_remove_range(struct ema_map *map, size_t start, size_t end);
+
+static inline int __must_check ema_lock_map(struct ema_map *map)
+{
+ return mutex_lock_interruptible(&map->lock);
+}
+
+static inline void ema_unlock_map(struct ema_map *map)
+{
+ mutex_unlock(&map->lock);
+}
+
+static inline int ema_lock_apply_to_range(struct ema_map *map,
+ size_t start, size_t end,
+ int (*cb)(struct ema *, void *),
+ void *arg)
+{
+ int rc = ema_lock_map(map);
+ if (!rc) {
+ rc = ema_apply_to_range(map, start, end, cb, arg);
+ ema_unlock_map(map);
+ }
+ return rc;
+}
+
+static inline int ema_lock_remove_range(struct ema_map *map,
+ size_t start, size_t end)
+{
+ int rc = ema_lock_map(map);
+ if (!rc) {
+ ema_remove_range(map, start, end);
+ ema_unlock_map(map);
+ }
+ return rc;
+}
+
+#endif /* _LSM_EMA_H_ */
diff --git a/security/Makefile b/security/Makefile
index c598b904938f..b66d03a94853 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
+obj-$(CONFIG_INTEL_SGX) += commonema.o
# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
diff --git a/security/commonema.c b/security/commonema.c
new file mode 100644
index 000000000000..c5b0bdfdc013
--- /dev/null
+++ b/security/commonema.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/lsm_ema.h>
+#include <linux/lsm_hooks.h>
+#include <linux/slab.h>
+
+static struct kmem_cache *_map_cache;
+static struct kmem_cache *_node_cache;
+static size_t _data_size __lsm_ro_after_init;
+
+static struct lsm_blob_sizes ema_blob_sizes __lsm_ro_after_init = {
+ .lbs_file = sizeof(atomic_long_t),
+};
+
+static atomic_long_t *_map_file(struct file *encl)
+{
+ return (void *)((char *)(encl->f_security) + ema_blob_sizes.lbs_file);
+}
+
+static struct ema_map *_alloc_map(void)
+{
+ struct ema_map *m;
+
+ m = kmem_cache_zalloc(_map_cache, GFP_KERNEL);
+ if (likely(m)) {
+ INIT_LIST_HEAD(&m->list);
+ mutex_init(&m->lock);
+ }
+ return m;
+}
+
+static struct ema *_new_ema(size_t start, size_t end, struct file *src)
+{
+ struct ema *ema;
+
+ if (unlikely(!_node_cache)) {
+ struct kmem_cache *c;
+
+ c = kmem_cache_create("lsm-ema", sizeof(*ema) + _data_size,
+ __alignof__(typeof(*ema)), SLAB_PANIC,
+ NULL);
+ if (atomic_long_cmpxchg((atomic_long_t *)&_node_cache, 0,
+ (long)c))
+ kmem_cache_destroy(c);
+ }
+
+ ema = kmem_cache_zalloc(_node_cache, GFP_KERNEL);
+ if (likely(ema)) {
+ INIT_LIST_HEAD(&ema->link);
+ ema->start = start;
+ ema->end = end;
+ if (src)
+ ema->source = get_file(src);
+ }
+ return ema;
+}
+
+static void _free_ema(struct ema *ema)
+{
+ if (ema->source)
+ fput(ema->source);
+ kmem_cache_free(_node_cache, ema);
+}
+
+static void _free_map(struct ema_map *map)
+{
+ struct ema *p, *n;
+
+ WARN_ON(mutex_is_locked(&map->lock));
+ list_for_each_entry_safe(p, n, &map->list, link)
+ _free_ema(p);
+ kmem_cache_free(_map_cache, map);
+}
+
+static struct ema_map *_init_map(struct file *encl)
+{
+ struct ema_map *m = ema_get_map(encl);
+ if (!m) {
+ m = _alloc_map();
+ if (atomic_long_cmpxchg(_map_file(encl), 0, (long)m)) {
+ _free_map(m);
+ m = ema_get_map(encl);
+ }
+ }
+ return m;
+}
+
+static inline struct ema *_next_ema(struct ema *p, struct ema_map *map)
+{
+ p = list_next_entry(p, link);
+ return &p->link == &map->list ? NULL : p;
+}
+
+static inline struct ema *_find_ema(struct ema_map *map, size_t a)
+{
+ struct ema *p;
+
+ WARN_ON(!mutex_is_locked(&map->lock));
+
+ list_for_each_entry(p, &map->list, link)
+ if (a < p->end)
+ break;
+ return &p->link == &map->list ? NULL : p;
+}
+
+static struct ema *_split_ema(struct ema *p, size_t at)
+{
+ typeof(p) n;
+
+ if (at <= p->start || at >= p->end)
+ return p;
+
+ n = _new_ema(p->start, at, p->source);
+ if (likely(n)) {
+ memcpy(n + 1, p + 1, _data_size);
+ p->start = at;
+ list_add_tail(&n->link, &p->link);
+ }
+ return n;
+}
+
+static int _merge_ema(struct ema *p, struct ema_map *map)
+{
+ typeof(p) prev = list_prev_entry(p, link);
+
+ WARN_ON(!mutex_is_locked(&map->lock));
+
+ if (&prev->link == &map->list || prev->end != p->start ||
+ prev->source != p->source || memcmp(prev + 1, p + 1, _data_size))
+ return 0;
+
+ p->start = prev->start;
+ fput(prev->source);
+ _free_ema(prev);
+ return 1;
+}
+
+static inline int _insert_ema(struct ema_map *map, struct ema *n)
+{
+ typeof(n) p = _find_ema(map, n->start);
+
+ if (!p)
+ list_add_tail(&n->link, &map->list);
+ else if (n->end <= p->start)
+ list_add_tail(&n->link, &p->link);
+ else
+ return -EEXIST;
+
+ _merge_ema(n, map);
+ if (p)
+ _merge_ema(p, map);
+ return 0;
+}
+
+static void ema_file_free_security(struct file *encl)
+{
+ struct ema_map *m = ema_get_map(encl);
+ if (m)
+ _free_map(m);
+}
+
+static int ema_enclave_load(struct file *encl, size_t start, size_t end,
+ size_t flags, struct vm_area_struct *vma)
+{
+ struct ema_map *m;
+ struct ema *ema;
+ int rc;
+
+ m = _init_map(encl);
+ if (unlikely(!m))
+ return -ENOMEM;
+
+ ema = _new_ema(start, end, vma ? vma->vm_file : NULL);
+ if (unlikely(!ema))
+ return -ENOMEM;
+
+ rc = ema_lock_map(m);
+ if (!rc) {
+ rc = _insert_ema(m, ema);
+ ema_unlock_map(m);
+ }
+ if (rc)
+ _free_ema(ema);
+ return rc;
+}
+
+static int ema_enclave_init(struct file *encl, struct sgx_sigstruct *sigstruct,
+ struct vm_area_struct *vma)
+{
+ if (unlikely(!_init_map(encl)))
+ return -ENOMEM;
+ return 0;
+}
+
+static struct security_hook_list ema_hooks[] __lsm_ro_after_init = {
+ LSM_HOOK_INIT(file_free_security, ema_file_free_security),
+ LSM_HOOK_INIT(enclave_load, ema_enclave_load),
+ LSM_HOOK_INIT(enclave_init, ema_enclave_init),
+};
+
+static int __init ema_init(void)
+{
+ _map_cache = kmem_cache_create("lsm-ema_map", sizeof(struct ema_map),
+ __alignof__(struct ema_map), SLAB_PANIC,
+ NULL);
+ security_add_hooks(ema_hooks, ARRAY_SIZE(ema_hooks), "ema");
+ return 0;
+}
+
+DEFINE_LSM(ema) = {
+ .name = "ema",
+ .order = LSM_ORDER_FIRST,
+ .init = ema_init,
+ .blobs = &ema_blob_sizes,
+};
+
+/* ema_request_blob shall only be called from LSM module init function */
+size_t __init ema_request_blob(size_t size)
+{
+ typeof(_data_size) offset = _data_size;
+ _data_size += size;
+ return offset;
+}
+
+struct ema_map *ema_get_map(struct file *encl)
+{
+ return (struct ema_map *)atomic_long_read(_map_file(encl));
+}
+
+/**
+ * Invoke a callback function on every EMA falls within range, split EMAs as
+ * needed
+ */
+int ema_apply_to_range(struct ema_map *map, size_t start, size_t end,
+ int (*cb)(struct ema *, void *), void *arg)
+{
+ struct ema *ema;
+ int rc;
+
+ ema = _find_ema(map, start);
+ while (ema && end > ema->start) {
+ if (start > ema->start)
+ _split_ema(ema, start);
+ if (end < ema->end)
+ ema = _split_ema(ema, end);
+
+ rc = (*cb)(ema, arg);
+ _merge_ema(ema, map);
+ if (rc)
+ return rc;
+
+ ema = _next_ema(ema, map);
+ }
+
+ if (ema)
+ _merge_ema(ema, map);
+ return 0;
+}
+
+/* Remove all EMAs falling within range, split EMAs as needed */
+void ema_remove_range(struct ema_map *map, size_t start, size_t end)
+{
+ struct ema *ema, *n;
+
+ ema = _find_ema(map, start);
+ while (ema && end > ema->start) {
+ if (start > ema->start)
+ _split_ema(ema, start);
+ if (end < ema->end)
+ ema = _split_ema(ema, end);
+
+ n = _next_ema(ema, map);
+ _free_ema(ema);
+ ema = n;
+ }
+}
--
2.17.1
More information about the Linux-security-module-archive
mailing list