diff --git a/fs/fs_context.c b/fs/fs_context.c
index b7e43a780a62..e411826127f3 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -282,6 +282,10 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
 		break;
 	}
 
+	ret = security_fs_context_init(fc, reference);
+	if (ret < 0)
+		goto err_fc;
+
 	/* TODO: Make all filesystems support this unconditionally */
 	init_fs_context = fc->fs_type->init_fs_context;
 	if (!init_fs_context)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 7f5c35d72082..f1eddaf2cd93 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -54,6 +54,7 @@ LSM_HOOK(int, 0, bprm_creds_from_file, struct linux_binprm *bprm, struct file *f
 LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
 LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
 LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)
+LSM_HOOK(int, 0, fs_context_init, struct fs_context *fc, struct dentry *reference)
 LSM_HOOK(int, 0, fs_context_dup, struct fs_context *fc,
 	 struct fs_context *src_sc)
 LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 73cb0ab2bc03..6b43f5f22fff 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -87,8 +87,12 @@
  * Security hooks for mount using fs_context.
  *	[See also Documentation/filesystems/mount_api.rst]
  *
+ * @fs_context_init:
+ *	Initialises a new security context.
+ *	@fc indicates the new filesystem context.
+ *	@reference points to a reference dentry for a submount (or is NULL).
  * @fs_context_dup:
- *	Allocate and attach a security structure to sc->security.  This pointer
+ *	Allocate and attach a security structure to fc->security.  This pointer
  *	is initialised to NULL by the caller.
  *	@fc indicates the new filesystem context.
  *	@src_fc indicates the original filesystem context.
diff --git a/include/linux/security.h b/include/linux/security.h
index a4f0c421dd0c..647f43d464f0 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -289,6 +289,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file);
 int security_bprm_check(struct linux_binprm *bprm);
 void security_bprm_committing_creds(struct linux_binprm *bprm);
 void security_bprm_committed_creds(struct linux_binprm *bprm);
+int security_fs_context_init(struct fs_context *fc, struct dentry *reference);
 int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);
 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);
 int security_sb_alloc(struct super_block *sb);
@@ -618,11 +619,18 @@ static inline void security_bprm_committed_creds(struct linux_binprm *bprm)
 {
 }
 
+static inline int security_fs_context_init(struct fs_context *fc,
+					   struct dentry *reference)
+{
+	return 0;
+}
+
 static inline int security_fs_context_dup(struct fs_context *fc,
 					  struct fs_context *src_fc)
 {
 	return 0;
 }
+
 static inline int security_fs_context_parse_param(struct fs_context *fc,
 						  struct fs_parameter *param)
 {
diff --git a/security/security.c b/security/security.c
index 0c49a1f05ac4..50bd3cd77a49 100644
--- a/security/security.c
+++ b/security/security.c
@@ -879,6 +879,11 @@ void security_bprm_committed_creds(struct linux_binprm *bprm)
 	call_void_hook(bprm_committed_creds, bprm);
 }
 
+int security_fs_context_init(struct fs_context *fc, struct dentry *reference)
+{
+	return call_int_hook(fs_context_init, 0, fc, reference);
+}
+
 int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
 {
 	return call_int_hook(fs_context_dup, 0, fc, src_fc);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8ea92f08e6bd..170ed9854153 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -354,7 +354,7 @@ static void inode_free_security(struct inode *inode)
 }
 
 struct selinux_mnt_opts {
-	const char *fscontext, *context, *rootcontext, *defcontext;
+	char *fscontext, *context, *rootcontext, *defcontext;
 };
 
 static void selinux_free_mnt_opts(void *mnt_opts)
@@ -980,7 +980,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
 	return rc;
 }
 
-static int selinux_add_opt(int token, const char *s, void **mnt_opts)
+static int selinux_add_opt(int token, char *s, void **mnt_opts)
 {
 	struct selinux_mnt_opts *opts = *mnt_opts;
 
@@ -2800,6 +2800,58 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
 				   FILESYSTEM__UNMOUNT, NULL);
 }
 
+static int selinux_fs_context_init(struct fs_context *fc,
+				   struct dentry *reference)
+{
+	const struct superblock_security_struct *ref_sbsec;
+	struct inode_security_struct *ref_isec;
+	struct selinux_mnt_opts *opts;
+	u32 len;
+	int ret;
+
+	if (fc->purpose != FS_CONTEXT_FOR_SUBMOUNT)
+		return 0;
+
+	ref_sbsec = selinux_superblock(reference->d_sb);
+	if (!ref_sbsec)
+		return 0;
+
+	opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+	if (!opts)
+		return -ENOMEM;
+
+	fc->security = opts;
+
+	if (ref_sbsec->flags & FSCONTEXT_MNT) {
+		ret = security_sid_to_context(&selinux_state, ref_sbsec->sid,
+					      &opts->fscontext, &len);
+		if (ret < 0)
+			return ret;
+	}
+	if (ref_sbsec->flags & CONTEXT_MNT) {
+		ret = security_sid_to_context(&selinux_state, ref_sbsec->mntpoint_sid,
+					      &opts->context, &len);
+		if (ret < 0)
+			return ret;
+	}
+	if (ref_sbsec->flags & DEFCONTEXT_MNT) {
+		ret = security_sid_to_context(&selinux_state, ref_sbsec->def_sid,
+					      &opts->defcontext, &len);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Should we use the mountpoint context or the root inode context? */
+	if (ref_sbsec->flags & ROOTCONTEXT_MNT) {
+		ref_isec = backing_inode_security(reference);
+		ret = security_sid_to_context(&selinux_state, ref_isec->sid,
+					      &opts->rootcontext, &len);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
 static int selinux_fs_context_dup(struct fs_context *fc,
 				  struct fs_context *src_fc)
 {
@@ -7267,6 +7319,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	/*
 	 * PUT "ALLOCATING" HOOKS HERE
 	 */
+	LSM_HOOK_INIT(fs_context_init, selinux_fs_context_init),
 	LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security),
 	LSM_HOOK_INIT(msg_queue_alloc_security,
 		      selinux_msg_queue_alloc_security),
