diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 0c77ede1cc11..094f27ed2e56 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -8,7 +8,8 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
 selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
 	     netnode.o netport.o status.o \
 	     ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
-	     ss/policydb.o ss/services.o ss/conditional.o ss/mls.o
+	     ss/policydb.o ss/services.o ss/conditional.o ss/mls.o \
+	     ss/sandbox.o
 
 selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
 
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 986f3ac14282..7d5e9c8a1b47 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -42,7 +42,8 @@ struct security_class_mapping secclass_map[] = {
 	  { "compute_av", "compute_create", "compute_member",
 	    "check_context", "load_policy", "compute_relabel",
 	    "compute_user", "setenforce", "setbool", "setsecparam",
-	    "setcheckreqprot", "read_policy", "validate_trans", NULL } },
+	    "setcheckreqprot", "read_policy", "validate_trans",
+	    "sandbox", NULL } },
 	{ "process",
 	  { "fork", "transition", "sigchld", "sigkill",
 	    "sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
@@ -248,6 +249,8 @@ struct security_class_mapping secclass_map[] = {
 	  {"open", "cpu", "kernel", "tracepoint", "read", "write"} },
 	{ "lockdown",
 	  { "integrity", "confidentiality", NULL } },
+	{ "sandbox",
+	  { "load_policy", "unload_policy"} },
 	{ NULL }
   };
 
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index d6036c018cf2..8275fa46a587 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -231,6 +231,12 @@ size_t security_policydb_len(struct selinux_state *state);
 int security_policycap_supported(struct selinux_state *state,
 				 unsigned int req_cap);
 
+int security_sandbox_load(struct selinux_state *state, u32 ssid,
+			  void *data, size_t len);
+int security_sandbox_unload(struct selinux_state *state, u32 ssid,
+			    void *data, size_t len);
+int security_sandbox_list(struct selinux_state *state);
+
 #define SEL_VEC_MAX 32
 struct av_decision {
 	u32 allowed;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 4781314c2510..5d31b079b85b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1935,6 +1935,154 @@ static int sel_make_policycap(struct selinux_fs_info *fsi)
 	return 0;
 }
 
+#define SEL_SANDBOX_LOAD_SIZELIMIT (32*1024*1024)
+#define SEL_SANDBOX_UNLOAD_SIZELIMIT (32*1024*1024)
+
+static ssize_t sel_sandbox_unload_write(struct file *file,
+					const char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	struct selinux_fs_info *fsi;
+	unsigned long length;
+	u32 ssid;
+	int denied;
+	int rc;
+	void *data = NULL;
+
+	if (count == 0 || count >= SEL_SANDBOX_UNLOAD_SIZELIMIT)
+		return -ERANGE;
+
+	/* no partial writes */
+	if (*ppos != 0)
+		return -EINVAL;
+
+	ssid = current_sid();
+	denied = avc_has_perm(&selinux_state,
+			      ssid, ssid,
+			      SECCLASS_SECURITY, SECURITY__SANDBOX, NULL);
+	if (denied)
+		return denied;
+
+	data = vmalloc(count);
+	if (!data) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	length = copy_from_user(data, buf, count);
+	if (length != 0) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	fsi = file_inode(file)->i_sb->s_fs_info;
+	mutex_lock(&fsi->mutex);
+	rc = security_sandbox_unload(fsi->state, ssid, data, count);
+	mutex_unlock(&fsi->mutex);
+	if (rc)
+		goto out;
+
+	rc = count;
+out:
+	vfree(data);
+
+	return rc;
+}
+
+static const struct file_operations sel_sandbox_unload_ops = {
+	.write		= sel_sandbox_unload_write,
+	.llseek		= generic_file_llseek,
+};
+
+static ssize_t sel_sandbox_load_write(struct file *file, const char __user *buf,
+				      size_t count, loff_t *ppos)
+
+{
+	struct selinux_fs_info *fsi;
+	unsigned long length;
+	u32 ssid;
+	int denied;
+	int rc;
+	void *data = NULL;
+
+	if (count <= 0 || count >= SEL_SANDBOX_LOAD_SIZELIMIT)
+		return -ENOMEM;
+
+	/* no partial writes */
+	if (*ppos != 0)
+		return -EINVAL;
+
+	ssid = current_sid();
+	denied = avc_has_perm(&selinux_state,
+			      ssid, ssid,
+			      SECCLASS_SECURITY, SECURITY__SANDBOX, NULL);
+	if (denied)
+		return denied;
+
+	data = vmalloc(count);
+	if (!data) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	length = copy_from_user(data, buf, count);
+	if (length != 0) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	fsi = file_inode(file)->i_sb->s_fs_info;
+	mutex_lock(&fsi->mutex);
+	rc = security_sandbox_load(fsi->state, ssid, data, count);
+	mutex_unlock(&fsi->mutex);
+	if (rc)
+		goto out;
+
+	rc = count;
+out:
+	vfree(data);
+
+	return rc;
+}
+
+static const struct file_operations sel_sandbox_load_ops = {
+	.write		= sel_sandbox_load_write,
+	.llseek		= generic_file_llseek,
+};
+
+static int sel_make_sandbox_files(struct dentry *dir)
+{
+	struct super_block *sb = dir->d_sb;
+	struct selinux_fs_info *fsi = sb->s_fs_info;
+	int i;
+
+	static const struct tree_descr files[] = {
+		{"sandbox_load", &sel_sandbox_load_ops, 0222},
+		{"sandbox_unload", &sel_sandbox_unload_ops, 0222},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(files); i++) {
+		struct inode *inode;
+		struct dentry *dentry;
+
+		dentry = d_alloc_name(dir, files[i].name);
+		if (!dentry)
+			return -ENOMEM;
+
+		inode = sel_make_inode(sb, S_IFREG|files[i].mode);
+		if (!inode) {
+			dput(dentry);
+			return -ENOMEM;
+		}
+
+		inode->i_fop = files[i].ops;
+		inode->i_ino = ++fsi->last_ino;
+		d_add(dentry, inode);
+	}
+
+	return 0;
+}
+
 static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
 			unsigned long *ino)
 {
@@ -2078,6 +2226,17 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
 	ret = sel_make_policy_nodes(fsi);
 	if (ret)
 		goto err;
+
+	dentry = sel_make_dir(sb->s_root, "sandbox", &fsi->last_ino);
+	if (IS_ERR(dentry)) {
+		ret = PTR_ERR(dentry);
+		goto err;
+	}
+
+	ret = sel_make_sandbox_files(dentry);
+	if (ret)
+		goto err;
+
 	return 0;
 err:
 	pr_err("SELinux: %s:  failed while creating inodes\n",
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 01b300a4a882..584925034981 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -103,7 +103,8 @@ avtab_insert_node(struct avtab *h, int hvalue,
 	return newnode;
 }
 
-static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
+int avtab_insert(struct avtab *h, struct avtab_key *key,
+		 struct avtab_datum *datum)
 {
 	int hvalue;
 	struct avtab_node *prev, *cur, *newnode;
@@ -375,6 +376,7 @@ static uint16_t spec_order[] = {
 	AVTAB_ALLOWED,
 	AVTAB_AUDITDENY,
 	AVTAB_AUDITALLOW,
+	AVTAB_SANDBOXDENY,
 	AVTAB_TRANSITION,
 	AVTAB_CHANGE,
 	AVTAB_MEMBER,
@@ -654,6 +656,29 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
 	return rc;
 }
 
+int avtab_map(struct avtab *h,
+	      int (*apply)(struct avtab_key *k, struct avtab_datum *d,
+			   void *args),
+	      void *args)
+{
+	int i;
+	int rc;
+	struct avtab_node *cur;
+
+	if (!h)
+		return 0;
+
+	for (i = 0; i < h->nslot; i++) {
+		for (cur = h->htable[i]; cur; cur = cur->next) {
+			rc = apply(&cur->key, &cur->datum, args);
+			if (rc)
+				return rc;
+		}
+	}
+
+	return 0;
+}
+
 void __init avtab_cache_init(void)
 {
 	avtab_node_cachep = kmem_cache_create("avtab_node",
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 5fdcb6696bcc..40524f902147 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -30,7 +30,9 @@ struct avtab_key {
 #define AVTAB_ALLOWED		0x0001
 #define AVTAB_AUDITALLOW	0x0002
 #define AVTAB_AUDITDENY		0x0004
-#define AVTAB_AV		(AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
+#define AVTAB_SANDBOXDENY	0x1000
+#define AVTAB_AV		(AVTAB_ALLOWED | AVTAB_AUDITALLOW | \
+				 AVTAB_AUDITDENY | AVTAB_SANDBOXDENY)
 #define AVTAB_TRANSITION	0x0010
 #define AVTAB_MEMBER		0x0020
 #define AVTAB_CHANGE		0x0040
@@ -105,10 +107,16 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp);
 
 struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
 					  struct avtab_datum *datum);
+int avtab_insert(struct avtab *h, struct avtab_key *key,
+		 struct avtab_datum *datum);
 
 struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
 
 struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
+int avtab_map(struct avtab *h,
+	      int (*apply)(struct avtab_key *k, struct avtab_datum *d,
+			   void *args),
+	      void *args);
 
 #define MAX_AVTAB_HASH_BITS 16
 #define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index 883f19d32c28..0e777e538041 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -116,6 +116,41 @@ void *hashtab_search(struct hashtab *h, const void *key)
 	return cur->datum;
 }
 
+struct hashtab_node *hashtab_rsearch(struct hashtab *h,
+				     int (*cmp_f)(void *d, u32 value),
+				     u32 value)
+{
+	struct hashtab_node *cur;
+	int i;
+
+	if (!h)
+		return NULL;
+
+	for (i = 0; i < h->size; i++) {
+		cur = h->htable[i];
+		while (cur) {
+			if (cmp_f(cur->datum, value) == 0)
+				return cur;
+			cur = cur->next;
+		}
+	}
+
+	return NULL;
+
+}
+
+void *hashtab_compare_search(struct hashtab *base, struct hashtab *child,
+			     int (cmp_f)(void *d, u32 value),
+			     u32 value)
+{
+	struct hashtab_node *node;
+
+	node = hashtab_rsearch(child, cmp_f, value);
+	if (!node)
+		return NULL;
+	return hashtab_search(base, node->key);
+}
+
 void hashtab_destroy(struct hashtab *h)
 {
 	u32 i;
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
index dde54d9ff01c..4a766f5f6a3f 100644
--- a/security/selinux/ss/hashtab.h
+++ b/security/selinux/ss/hashtab.h
@@ -62,6 +62,14 @@ int hashtab_insert(struct hashtab *h, void *k, void *d);
  */
 void *hashtab_search(struct hashtab *h, const void *k);
 
+struct hashtab_node *hashtab_rsearch(struct hashtab *h,
+				     int (*cmp_f)(void *d, u32 value),
+				     u32 value);
+
+void *hashtab_compare_search(struct hashtab *base, struct hashtab *child,
+			     int (*cmp_f)(void *d, u32 value),
+			     u32 value);
+
 /*
  * Destroys the specified hash table.
  */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 932b2b9bcdb2..2bb93f152870 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2541,6 +2541,48 @@ int policydb_read(struct policydb *p, void *fp)
 	goto out;
 }
 
+int policydb_read_symbols(struct policydb *p, struct symtab *s,
+			  int symbol_index, void *fp)
+{
+	__le32 data;
+	u32 nprim, nel;
+	int rc, i;
+
+	rc = next_entry(&data, fp, sizeof(__le32));
+	if (rc)
+		return rc;
+	nprim = le32_to_cpu(data);
+
+	rc = next_entry(&data, fp, sizeof(__le32));
+	if (rc)
+		return rc;
+	nel = le32_to_cpu(data);
+
+	rc = symtab_init(s, nel);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < nel; i++) {
+		rc = read_f[symbol_index](p, s->table, fp);
+		if (rc)
+			goto bad;
+	}
+
+	return 0;
+bad:
+	policydb_destroy_symbols(s, symbol_index);
+	s->table = NULL;
+	return rc;
+}
+
+void policydb_destroy_symbols(struct symtab *s, int symbol_index)
+{
+	if (!s)
+		return;
+	hashtab_map(s->table, destroy_f[symbol_index], NULL);
+	hashtab_destroy(s->table);
+}
+
 /*
  * Write a MLS level structure to a policydb binary
  * representation file.
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 72e2932fb12d..ebf6ddd13714 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -29,6 +29,7 @@
 #include "mls_types.h"
 #include "context.h"
 #include "constraint.h"
+#include "sandbox.h"
 
 /*
  * A datum type is defined for each kind of symbol
@@ -320,6 +321,9 @@ extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
 extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
 extern int policydb_read(struct policydb *p, void *fp);
 extern int policydb_write(struct policydb *p, void *fp);
+extern int policydb_read_symbols(struct policydb *p, struct symtab *s,
+				 int symbol_index, void *fp);
+extern void policydb_destroy_symbols(struct symtab *s, int symbol_index);
 
 #define POLICYDB_CONFIG_MLS    1
 
diff --git a/security/selinux/ss/sandbox.c b/security/selinux/ss/sandbox.c
new file mode 100644
index 000000000000..cf93135877cd
--- /dev/null
+++ b/security/selinux/ss/sandbox.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "services.h"
+#include "policydb.h"
+#include "sandbox.h"
+#include "avtab.h"
+#include "symtab.h"
+#include "hashtab.h"
+
+#define SANDBOX_CONTEXT_LENGTH PAGE_SIZE
+
+struct fix_args {
+	struct policydb *p;
+	struct symtab *symbols;
+	struct sandbox *s;
+};
+
+/*
+ * It is expected that the caller frees the string.
+ */
+static int context_read(char **context_str, size_t *context_strlen, void *fp)
+{
+	int rc;
+	__le32 data;
+	char *str = NULL;
+	size_t strlen = 0;
+
+	/* read the context length */
+	rc = next_entry(&data, fp, sizeof(data));
+	if (rc)
+		goto out;
+
+	strlen = le32_to_cpu(data);
+	if (strlen == 0 || strlen >= SANDBOX_CONTEXT_LENGTH) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* read context */
+	str = kmalloc(strlen + 1, GFP_KERNEL);
+	if (!str) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	rc = next_entry(str, fp, strlen);
+	if (rc) {
+		kfree(str);
+		goto out;
+	}
+
+	/* null-terminate */
+	str[strlen] = '\0';
+
+out:
+	*context_strlen = strlen;
+	*context_str = str;
+	return rc;
+}
+
+static int type_value_compare(void *d, u32 value)
+{
+	struct type_datum *datum = d;
+
+	if (datum->value == value)
+		return 0;
+	return -EINVAL;
+}
+
+static int class_value_compare(void *d, u32 value)
+{
+	struct class_datum *datum = d;
+
+	if (datum->value == value)
+		return 0;
+	return -EINVAL;
+}
+
+static int perm_value_compare(void *d, u32 value)
+{
+	struct perm_datum *datum = d;
+
+	if (datum->value == value)
+		return 0;
+	return -EINVAL;
+}
+
+void sandbox_destroy(struct sandbox *s)
+{
+	if (!s)
+		return;
+	kfree(s->context_str);
+	avtab_destroy(&s->te);
+	kfree(s);
+}
+
+void sandbox_detach(struct sidtab_entry *entry)
+{
+	struct sandbox *s_old;
+
+	s_old = sidtab_sandbox_detach(entry);
+	if (s_old)
+		sandbox_destroy(s_old);
+}
+
+int sandbox_attach(struct sidtab_entry *entry, struct sandbox *s)
+{
+	sandbox_detach(entry);
+	return sidtab_sandbox_attach(entry, s);
+}
+
+int sandbox_fix_node(struct avtab_key *k, struct avtab_datum *d, void *args)
+{
+	struct fix_args *fargs = args;
+	struct policydb *p = fargs->p;
+	struct symtab *symbols = fargs->symbols;
+	struct sandbox *s_new = fargs->s;
+
+	struct avtab_key key;
+	struct avtab_datum datum;
+
+	struct type_datum *base_type;
+	struct class_datum *base_class;
+	struct perm_datum *base_perm;
+	struct common_datum *base_common;
+
+	struct hashtab_node *rnode;
+	struct class_datum *sandbox_class;
+	struct common_datum *sandbox_common;
+	int rc, i;
+
+	/* fix source type */
+	base_type = hashtab_compare_search(p->symtab[SYM_TYPES].table,
+					   symbols[SYM_TYPES].table,
+					   type_value_compare,
+					   k->source_type);
+	if (!base_type)
+		return -EINVAL;
+	key.source_type = base_type->value;
+
+	/* fix target type */
+	base_type = hashtab_compare_search(p->symtab[SYM_TYPES].table,
+					   symbols[SYM_TYPES].table,
+					   type_value_compare,
+					   k->target_type);
+	if (!base_type)
+		return -EINVAL;
+	key.target_type = base_type->value;
+
+	/* fix class */
+	base_class = hashtab_compare_search(p->symtab[SYM_CLASSES].table,
+					    symbols[SYM_CLASSES].table,
+					    class_value_compare,
+					    k->target_class);
+	if (!base_class)
+		return -EINVAL;
+	key.target_class = base_class->value;
+
+	/* fix specified */
+	key.specified = k->specified;
+
+	/* fix data */
+	rnode = hashtab_rsearch(symbols[SYM_CLASSES].table,
+				class_value_compare,
+				k->target_class);
+	if (!rnode)
+		return -EINVAL;
+
+	sandbox_class = rnode->datum;
+	sandbox_common = sandbox_class->comdatum;
+	base_common = base_class->comdatum;
+	base_perm = NULL;
+	datum.u.data = 0x00;
+	rnode = NULL;
+
+	for (i = 0; i < (sizeof(u32)*8); i++) {
+		if (d->u.data & (1<<i)) {
+			if (sandbox_common)
+				rnode = hashtab_rsearch(
+					sandbox_common->permissions.table,
+					perm_value_compare,
+					i+1);
+			if (!rnode)
+				rnode = hashtab_rsearch(
+					sandbox_class->permissions.table,
+					perm_value_compare,
+					i+1);
+			if (!rnode)
+				return -EINVAL;
+
+			if (base_common)
+				base_perm = hashtab_search(
+					base_common->permissions.table,
+					rnode->key);
+			if (!base_perm)
+				base_perm = hashtab_search(
+					base_class->permissions.table,
+					rnode->key);
+			if (!base_perm)
+				return -EINVAL;
+			datum.u.data |= (1<<(base_perm->value-1));
+		}
+	}
+
+	rc = avtab_insert(&s_new->te, &key, &datum);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+int sandbox_fix(struct policydb *p, struct symtab *symbols, struct sandbox **sp)
+{
+	struct sandbox *s;
+	struct sandbox *s_new;
+	int rc;
+	struct fix_args args = { p, symbols, NULL };
+
+	s = *sp;
+	s_new = kmalloc(sizeof(*s_new), GFP_KERNEL);
+	if (!s_new)
+		return -ENOMEM;
+
+	s_new->context_strlen = s->context_strlen;
+	s_new->context_str = kstrdup(s->context_str, GFP_KERNEL);
+	s_new->mode = s->mode;
+	s_new->te.htable = NULL;
+	s_new->te.nel = 0;
+	if (s_new->context_str == NULL) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	rc = avtab_alloc(&s_new->te, s->te.nel);
+	if (rc)
+		goto bad;
+	args.s = s_new;
+
+	rc = avtab_map(&s->te, sandbox_fix_node, &args);
+	if (rc)
+		goto bad;
+
+	*sp = s_new;
+	sandbox_destroy(s);
+	return 0;
+
+bad:
+	sandbox_destroy(s_new);
+	return rc;
+}
+
+int sandbox_load(struct selinux_state *state, u32 ssid, void *fp)
+{
+	u32 tsid;
+	int rc;
+	struct sandbox *s = NULL;
+	char *context_str = NULL;
+	size_t context_strlen = 0;
+	struct sidtab_entry *entry = NULL;
+	struct policydb *policydb = &state->ss->policydb;
+	struct symtab symbols[SYM_NUM] = {{ NULL, 0 }};
+	u32 mode = 0;
+
+	rc = context_read(&context_str, &context_strlen, fp);
+	if (rc)
+		return rc;
+
+	rc = security_context_to_sid(state, context_str, context_strlen,
+				     &tsid, GFP_KERNEL);
+	if (rc)
+		goto exit;
+
+	rc = avc_has_perm(state,
+			  ssid, tsid,
+			  SECCLASS_SANDBOX, SANDBOX__LOAD_POLICY, NULL);
+	if (rc)
+		goto exit;
+
+	rc = next_entry(&mode, fp, sizeof(mode));
+	if (rc)
+		goto exit;
+	if (mode != SANDBOX_MODE_ALLOW && mode != SANDBOX_MODE_DENY) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	rc = policydb_read_symbols(policydb, &symbols[SYM_COMMONS],
+				   SYM_COMMONS, fp);
+	if (rc)
+		goto exit;
+
+	rc = policydb_read_symbols(policydb, &symbols[SYM_CLASSES],
+				   SYM_CLASSES, fp);
+	if (rc)
+		goto exit;
+
+	rc = policydb_read_symbols(policydb, &symbols[SYM_TYPES],
+				   SYM_TYPES, fp);
+	if (rc)
+		goto exit;
+
+	s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (!s) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	s->context_strlen = context_strlen;
+	s->context_str = kstrdup(context_str, GFP_KERNEL);
+	s->mode = mode;
+	s->te.htable = NULL;
+	s->te.nel = 0;
+	if (s->context_str == NULL) {
+		rc = -ENOMEM;
+		goto bad;
+	}
+
+	rc = avtab_read(&s->te, fp, policydb);
+	if (rc)
+		goto bad;
+
+	rc = sandbox_fix(policydb, symbols, &s);
+	if (rc)
+		goto bad;
+
+	entry = sidtab_search_entry(state->ss->sidtab, tsid);
+	if (!entry) {
+		rc = -EINVAL;
+		goto bad;
+	}
+
+	write_lock_irq(&state->ss->policy_rwlock);
+	rc = sandbox_attach(entry, s);
+	write_unlock_irq(&state->ss->policy_rwlock);
+	if (rc)
+		goto bad;
+
+exit:
+	kfree(context_str);
+	policydb_destroy_symbols(&symbols[SYM_COMMONS], SYM_COMMONS);
+	policydb_destroy_symbols(&symbols[SYM_CLASSES], SYM_CLASSES);
+	policydb_destroy_symbols(&symbols[SYM_TYPES], SYM_TYPES);
+	return rc;
+
+bad:
+	sandbox_destroy(s);
+	goto exit;
+}
+
+int sandbox_unload(struct selinux_state *state, u32 ssid, void *fp)
+{
+	u32 tsid;
+	int rc;
+	char *context_str = NULL;
+	size_t context_strlen = 0;
+	struct sidtab_entry *entry = NULL;
+
+	rc = context_read(&context_str, &context_strlen, fp);
+	if (rc)
+		return rc;
+
+	rc = security_context_to_sid(state, context_str, context_strlen,
+				     &tsid, GFP_KERNEL);
+	kfree(context_str);
+	context_str = NULL;
+	context_strlen = 0;
+	if (rc)
+		return rc;
+
+	rc = avc_has_perm(state,
+			  ssid, tsid,
+			  SECCLASS_SANDBOX, SANDBOX__UNLOAD_POLICY, NULL);
+	if (rc)
+		return rc;
+
+	entry = sidtab_search_entry(state->ss->sidtab, tsid);
+	if (!entry)
+		return -EINVAL;
+
+	write_lock_irq(&state->ss->policy_rwlock);
+	sandbox_detach(entry);
+	write_unlock_irq(&state->ss->policy_rwlock);
+	return 0;
+}
diff --git a/security/selinux/ss/sandbox.h b/security/selinux/ss/sandbox.h
new file mode 100644
index 000000000000..339f6f52bbde
--- /dev/null
+++ b/security/selinux/ss/sandbox.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _SS_SANDBOX_H_
+#define _SS_SANDBOX_H_
+
+#include "security.h"
+#include "symtab.h"
+#include "avtab.h"
+
+#define SANDBOX_MODE_ALLOW 0x00
+#define SANDBOX_MODE_DENY 0x01
+
+struct sandbox {
+	char *context_str;
+	u32 context_strlen;
+	u32 mode;
+	struct avtab te;
+};
+
+int sandbox_load(struct selinux_state *state, u32 ssid, void *fp);
+int sandbox_unload(struct selinux_state *state, u32 ssid, void *fp);
+void sandbox_destroy(struct sandbox *s);
+
+#endif /* _SS_SANDBOX_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 8ad34fd031d1..a41ca000ec48 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -101,7 +101,8 @@ static void context_struct_compute_av(struct policydb *policydb,
 				      struct context *tcontext,
 				      u16 tclass,
 				      struct av_decision *avd,
-				      struct extended_perms *xperms);
+				      struct extended_perms *xperms,
+				      struct sandbox *sandbox);
 
 static int selinux_set_mapping(struct policydb *pol,
 			       struct security_class_mapping *map,
@@ -571,7 +572,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
 				  tcontextp,
 				  tclass,
 				  &lo_avd,
-				  NULL);
+				  NULL, NULL);
 
 	masked = ~lo_avd.allowed & avd->allowed;
 
@@ -620,7 +621,8 @@ static void context_struct_compute_av(struct policydb *policydb,
 				      struct context *tcontext,
 				      u16 tclass,
 				      struct av_decision *avd,
-				      struct extended_perms *xperms)
+				      struct extended_perms *xperms,
+				      struct sandbox *sandbox)
 {
 	struct constraint_node *constraint;
 	struct role_allow *ra;
@@ -629,6 +631,10 @@ static void context_struct_compute_av(struct policydb *policydb,
 	struct class_datum *tclass_datum;
 	struct ebitmap *sattr, *tattr;
 	struct ebitmap_node *snode, *tnode;
+	u32 sandbox_allowed = 0;
+	u32 sandbox_deny = 0;
+	u32 sandbox_auditallow = 0;
+	u32 sandbox_auditdeny = 0xffffffff;
 	unsigned int i, j;
 
 	avd->allowed = 0;
@@ -655,6 +661,7 @@ static void context_struct_compute_av(struct policydb *policydb,
 	avkey.specified = AVTAB_AV | AVTAB_XPERMS;
 	sattr = &policydb->type_attr_map_array[scontext->type - 1];
 	tattr = &policydb->type_attr_map_array[tcontext->type - 1];
+
 	ebitmap_for_each_positive_bit(sattr, snode, i) {
 		ebitmap_for_each_positive_bit(tattr, tnode, j) {
 			avkey.source_type = i + 1;
@@ -673,13 +680,45 @@ static void context_struct_compute_av(struct policydb *policydb,
 					services_compute_xperms_drivers(xperms, node);
 			}
 
+
 			/* Check conditional av table for additional permissions */
 			cond_compute_av(&policydb->te_cond_avtab, &avkey,
 					avd, xperms);
 
+			/* Compute sandbox policy */
+			if (likely(!sandbox))
+				continue;
+			for (node = avtab_search_node(&sandbox->te, &avkey);
+			     node;
+			     node = avtab_search_node_next(node,
+							   avkey.specified)) {
+				if (node->key.specified == AVTAB_ALLOWED)
+					sandbox_allowed |= node->datum.u.data;
+				else if (node->key.specified ==
+					 AVTAB_SANDBOXDENY)
+					sandbox_deny |= node->datum.u.data;
+				else if (node->key.specified ==
+					 AVTAB_AUDITALLOW)
+					sandbox_auditallow |=
+						node->datum.u.data;
+				else if (node->key.specified ==
+					 AVTAB_AUDITDENY)
+					sandbox_auditdeny &= node->datum.u.data;
+				/* TODO: add support for xperms */
+			}
 		}
 	}
 
+	if (sandbox != NULL) {
+		/* Apply sandbox restrictions */
+		if (sandbox->mode == SANDBOX_MODE_DENY)
+			avd->allowed &= (~sandbox_deny);
+		else
+			avd->allowed &= sandbox_allowed;
+		avd->auditallow &= sandbox_auditallow;
+		avd->auditdeny &= sandbox_auditdeny;
+	}
+
 	/*
 	 * Remove any permissions prohibited by a constraint (this includes
 	 * the MLS policy).
@@ -1106,7 +1145,9 @@ void security_compute_av(struct selinux_state *state,
 	struct policydb *policydb;
 	struct sidtab *sidtab;
 	u16 tclass;
+	struct sidtab_entry *entry = NULL;
 	struct context *scontext = NULL, *tcontext = NULL;
+	struct sandbox *sandbox = NULL;
 
 	read_lock(&state->ss->policy_rwlock);
 	avd_init(state, avd);
@@ -1117,13 +1158,18 @@ void security_compute_av(struct selinux_state *state,
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
-	scontext = sidtab_search(sidtab, ssid);
-	if (!scontext) {
+	entry = sidtab_search_entry(sidtab, ssid);
+	if (!entry) {
 		pr_err("SELinux: %s:  unrecognized SID %d\n",
 		       __func__, ssid);
 		goto out;
 	}
 
+	sandbox = entry->sandbox;
+	scontext = &entry->context;
+	if (!scontext)
+		goto out;
+
 	/* permissive domain? */
 	if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
 		avd->flags |= AVD_FLAGS_PERMISSIVE;
@@ -1142,7 +1188,7 @@ void security_compute_av(struct selinux_state *state,
 		goto out;
 	}
 	context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
-				  xperms);
+				  xperms, sandbox);
 	map_decision(&state->ss->map, orig_tclass, avd,
 		     policydb->allow_unknown);
 out:
@@ -1161,7 +1207,9 @@ void security_compute_av_user(struct selinux_state *state,
 {
 	struct policydb *policydb;
 	struct sidtab *sidtab;
+	struct sidtab_entry *entry = NULL;
 	struct context *scontext = NULL, *tcontext = NULL;
+	struct sandbox *sandbox = NULL;
 
 	read_lock(&state->ss->policy_rwlock);
 	avd_init(state, avd);
@@ -1171,13 +1219,18 @@ void security_compute_av_user(struct selinux_state *state,
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
-	scontext = sidtab_search(sidtab, ssid);
-	if (!scontext) {
+	entry = sidtab_search_entry(sidtab, ssid);
+	if (!entry) {
 		pr_err("SELinux: %s:  unrecognized SID %d\n",
 		       __func__, ssid);
 		goto out;
 	}
 
+	sandbox = entry->sandbox;
+	scontext = &entry->context;
+	if (!scontext)
+		goto out;
+
 	/* permissive domain? */
 	if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
 		avd->flags |= AVD_FLAGS_PERMISSIVE;
@@ -1196,7 +1249,7 @@ void security_compute_av_user(struct selinux_state *state,
 	}
 
 	context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
-				  NULL);
+				  NULL, sandbox);
  out:
 	read_unlock(&state->ss->policy_rwlock);
 	return;
@@ -3787,3 +3840,57 @@ int security_read_policy(struct selinux_state *state,
 	return 0;
 
 }
+
+int security_sandbox_unload(struct selinux_state *state, u32 ssid,
+			    void *data, size_t len)
+{
+	struct policy_file file = { data, len }, *fp = &file;
+	int rc = 0;
+	u32 seqno;
+
+	if (!state->initialized)
+		return -EINVAL;
+
+	/* unload */
+	rc = sandbox_unload(state, ssid, fp);
+	if (rc)
+		return rc;
+
+	write_lock_irq(&state->ss->policy_rwlock);
+	seqno = ++state->ss->latest_granting;
+	write_unlock_irq(&state->ss->policy_rwlock);
+
+	/* reset AVC - this will clear the cache */
+	avc_ss_reset(state->avc, seqno);
+	selnl_notify_policyload(seqno);
+	selinux_status_update_policyload(state, seqno);
+
+	return 0;
+}
+
+int security_sandbox_load(struct selinux_state *state, u32 ssid,
+			  void *data, size_t len)
+{
+	struct policy_file file = { data, len }, *fp = &file;
+	int rc = 0;
+	u32 seqno;
+
+	if (!state->initialized)
+		return -EINVAL;
+
+	/* load */
+	rc = sandbox_load(state, ssid, fp);
+	if (rc)
+		return rc;
+
+	write_lock_irq(&state->ss->policy_rwlock);
+	seqno = ++state->ss->latest_granting;
+	write_unlock_irq(&state->ss->policy_rwlock);
+
+	/* reset AVC - this will clear the cache */
+	avc_ss_reset(state->avc, seqno);
+	selnl_notify_policyload(seqno);
+	selinux_status_update_policyload(state, seqno);
+
+	return 0;
+}
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index a308ce1e6a13..3fe125da429b 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -88,6 +88,7 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
 #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
 	isid->entry.cache = NULL;
 #endif
+	isid->entry.sandbox = NULL;
 	isid->set = 1;
 
 	/*
@@ -292,6 +293,7 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
 		goto out_unlock;
 
 	dst->sid = index_to_sid(count);
+	dst->sandbox = NULL;
 
 	rc = context_cpy(&dst->context, context);
 	if (rc)
@@ -465,6 +467,7 @@ static void sidtab_destroy_entry(struct sidtab_entry *entry)
 #if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
 	kfree(rcu_dereference_raw(entry->cache));
 #endif
+	sandbox_destroy(entry->sandbox);
 }
 
 static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
@@ -595,4 +598,28 @@ int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
 	return rc;
 }
 
+int sidtab_sandbox_attach(struct sidtab_entry *entry,
+			  struct sandbox *sandbox)
+{
+	if (!entry)
+		return -EINVAL;
+
+	entry->sandbox = sandbox;
+
+	return 0;
+}
+
+struct sandbox *sidtab_sandbox_detach(struct sidtab_entry *entry)
+{
+	struct sandbox *sandbox;
+
+	if (!entry)
+		return NULL;
+
+	sandbox = entry->sandbox;
+	entry->sandbox = NULL;
+
+	return sandbox;
+}
+
 #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index 3311d9f236c0..bb2a74b0db9d 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -16,6 +16,7 @@
 #include <linux/hashtable.h>
 
 #include "context.h"
+#include "sandbox.h"
 
 struct sidtab_entry {
 	u32 sid;
@@ -24,6 +25,7 @@ struct sidtab_entry {
 	struct sidtab_str_cache __rcu *cache;
 #endif
 	struct hlist_node list;
+	struct sandbox *sandbox;
 };
 
 union sidtab_entry_inner {
@@ -147,6 +149,10 @@ static inline int sidtab_sid2str_get(struct sidtab *s,
 }
 #endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
 
+int sidtab_sandbox_attach(struct sidtab_entry *entry,
+			  struct sandbox *sandbox);
+struct sandbox *sidtab_sandbox_detach(struct sidtab_entry *entry);
+
 #endif	/* _SS_SIDTAB_H_ */
 
 
