diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index ffbf7863306d..cd225121aff7 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -185,8 +185,7 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
 		 * persistent copy in kernfs_node.
 		 */
 		set_inode_attr(inode, &attrs->ia_iattr);
-		security_inode_notifysecctx(inode, attrs->ia_context.context,
-					    attrs->ia_context.len);
+		security_inode_notifysecctx(inode, &attrs->ia_context);
 	}
 
 	if (kernfs_type(kn) == KERNFS_DIR)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 414a90d48493..8acc5eef4d08 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -341,13 +341,16 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
 					struct nfs4_label *label)
 {
 	int error;
+	struct lsmcontext context = { .slot = LSMBLOB_FIRST };
 
 	if (label == NULL)
 		return;
 
-	if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
-		error = security_inode_notifysecctx(inode, label->label,
-				label->len);
+	if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) &&
+	    inode->i_security) {
+		context.context = label->label;
+		context.len = label->len;
+		error = security_inode_notifysecctx(inode, &context);
 		if (error)
 			printk(KERN_ERR "%s() %s %d "
 					"security_inode_notifysecctx() %d\n",
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 0cfd257ffdaf..0f166c81f596 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -54,12 +54,14 @@
 static inline void
 nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
 {
+	struct lsmcontext context = { .slot = LSMBLOB_FIRST };
 	struct inode *inode = d_inode(resfh->fh_dentry);
 	int status;
 
 	inode_lock(inode);
-	status = security_inode_setsecctx(resfh->fh_dentry,
-		label->data, label->len);
+	context.context = label->data;
+	context.len = label->len;
+	status = security_inode_setsecctx(resfh->fh_dentry, &context);
 	inode_unlock(inode);
 
 	if (status)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 7dc98e14655d..274a998cc123 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -531,6 +531,7 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	__be32 error;
 	int host_error;
 	struct dentry *dentry;
+	struct lsmcontext context = { .slot = LSMBLOB_FIRST };
 
 	error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
 	if (error)
@@ -539,7 +540,9 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	dentry = fhp->fh_dentry;
 
 	inode_lock(d_inode(dentry));
-	host_error = security_inode_setsecctx(dentry, label->data, label->len);
+	context.context = label->data;
+	context.len = label->len;
+	host_error = security_inode_setsecctx(dentry, &context);
 	inode_unlock(d_inode(dentry));
 	return nfserrno(host_error);
 }
diff --git a/include/linux/security.h b/include/linux/security.h
index 0665a27a2891..2f442746dede 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -493,8 +493,8 @@ int security_secctx_to_secid(const char *secdata, u32 seclen,
 void security_release_secctx(struct lsmcontext *cp);
 
 void security_inode_invalidate_secctx(struct inode *inode);
-int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
+int security_inode_notifysecctx(struct inode *inode, struct lsmcontext *cp);
+int security_inode_setsecctx(struct dentry *dentry, struct lsmcontext *cp);
 int security_inode_getsecctx(struct inode *inode, struct lsmcontext *cp);
 #else /* CONFIG_SECURITY */
 
@@ -1288,11 +1288,13 @@ static inline void security_inode_invalidate_secctx(struct inode *inode)
 {
 }
 
-static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+static inline int security_inode_notifysecctx(struct inode *inode,
+					      struct lsmcontext *cp);
 {
 	return -EOPNOTSUPP;
 }
-static inline int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+static inline int security_inode_setsecctx(struct dentry *dentry,
+					   struct lsmcontext *cp)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/security/security.c b/security/security.c
index 13102d16bf2c..c71ddae6760e 100644
--- a/security/security.c
+++ b/security/security.c
@@ -743,6 +743,42 @@ static int lsm_superblock_alloc(struct super_block *sb)
 	return 0;
 }
 
+/**
+ * append_ctx - append a lsm/context pair to a compound context
+ * @ctx: the existing compound context
+ * @ctxlen: size of the old context, including terminating nul byte
+ * @lsm: new lsm name, nul terminated
+ * @new: new context, possibly nul terminated
+ * @newlen: maximum size of @new
+ *
+ * replace @ctx with a new compound context, appending @newlsm and @new
+ * to @ctx. On exit the new data replaces the old, which is freed.
+ * @ctxlen is set to the new size, which includes a trailing nul byte.
+ *
+ * Returns 0 on success, -ENOMEM if no memory is available.
+ */
+static int append_ctx(char **ctx, int *ctxlen, const char *lsm, char *new,
+		      int newlen)
+{
+	char *final;
+	int llen;
+
+	llen = strlen(lsm) + 1;
+	newlen = strnlen(new, newlen) + 1;
+
+	final = kzalloc(*ctxlen + llen + newlen, GFP_KERNEL);
+	if (final == NULL)
+		return -ENOMEM;
+	if (*ctxlen)
+		memcpy(final, *ctx, *ctxlen);
+	memcpy(final + *ctxlen, lsm, llen);
+	memcpy(final + *ctxlen + llen, new, newlen);
+	kfree(*ctx);
+	*ctx = final;
+	*ctxlen = *ctxlen + llen + newlen;
+	return 0;
+}
+
 /*
  * Hook list operation macros.
  *
@@ -2083,12 +2119,8 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 	struct security_hook_list *hp;
 	char *final = NULL;
 	char *cp;
-	char *tp;
 	int rc = 0;
 	int finallen = 0;
-	int llen;
-	int clen;
-	int tlen;
 	int display = lsm_task_display(current);
 	int slot = 0;
 
@@ -2116,26 +2148,12 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 				kfree(final);
 				return rc;
 			}
-			llen = strlen(hp->lsmid->lsm) + 1;
-			clen = strlen(cp) + 1;
-			tlen = llen + clen;
-			if (final)
-				tlen += finallen;
-			tp = kzalloc(tlen, GFP_KERNEL);
-			if (tp == NULL) {
-				kfree(cp);
+			rc = append_ctx(&final, &finallen, hp->lsmid->lsm,
+					cp, rc);
+			if (rc < 0) {
 				kfree(final);
-				return -ENOMEM;
+				return rc;
 			}
-			if (final)
-				memcpy(tp, final, finallen);
-			memcpy(tp + finallen, hp->lsmid->lsm, llen);
-			memcpy(tp + finallen + llen, cp, clen);
-			kfree(cp);
-			if (final)
-				kfree(final);
-			final = tp;
-			finallen = tlen;
 		}
 		if (final == NULL)
 			return -EINVAL;
@@ -2210,13 +2228,22 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb)
 	return call_int_hook(netlink_send, 0, sk, skb);
 }
 
+/**
+ * security_ismaclabel - Does @name identify a MAC attribute
+ * @name: attribute name in question
+ *
+ * If @name is the name of a Mandatory Access Control (MAC) attribute
+ * that the first module on the list recognizes return 1. Don't look
+ * beyond the first module, as this is only used by NFS and NFS can't
+ * differentiate which module to use.
+ */
 int security_ismaclabel(const char *name)
 {
 	struct security_hook_list *hp;
 
 	hlist_for_each_entry(hp, &security_hook_heads.ismaclabel, list)
-		if (hp->hook.ismaclabel(name) != 0)
-			return 1;
+		return hp->hook.ismaclabel(name);
+
 	return 0;
 }
 EXPORT_SYMBOL(security_ismaclabel);
@@ -2284,6 +2311,15 @@ void security_release_secctx(struct lsmcontext *cp)
 	struct security_hook_list *hp;
 	bool found = false;
 
+	if (cp->slot == LSMBLOB_INVALID)
+		return;
+
+	if (cp->slot == LSMBLOB_COMPOUND) {
+		kfree(cp->context);
+		found = true;
+		goto clear_out;
+	}
+
 	hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list)
 		if (cp->slot == hp->lsmid->slot) {
 			hp->hook.release_secctx(cp->context, cp->len);
@@ -2291,6 +2327,7 @@ void security_release_secctx(struct lsmcontext *cp)
 			break;
 		}
 
+clear_out:
 	memset(cp, 0, sizeof(*cp));
 
 	if (!found)
@@ -2305,30 +2342,82 @@ void security_inode_invalidate_secctx(struct inode *inode)
 }
 EXPORT_SYMBOL(security_inode_invalidate_secctx);
 
-int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+int security_inode_notifysecctx(struct inode *inode, struct lsmcontext *cp)
 {
-	return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen);
+	struct security_hook_list *hp;
+	char *raw = cp->context;
+	char *ctx;
+	int llen;
+	int clen;
+	int rc;
+
+	if (cp->slot == LSMBLOB_COMPOUND) {
+		hlist_for_each_entry(hp,
+		    &security_hook_heads.inode_notifysecctx, list) {
+			llen = strlen(raw) + 1;
+			ctx = raw + llen;
+			clen = strlen(ctx) + 1;
+			if (!strcmp(hp->lsmid->lsm, raw)) {
+				rc = hp->hook.inode_notifysecctx(inode, ctx,
+								 clen);
+				if (WARN_ON(rc != 0))
+					return rc;
+			}
+			raw = ctx + clen;
+		}
+		return 0;
+	}
+
+	hlist_for_each_entry(hp, &security_hook_heads.inode_notifysecctx, list)
+		if (cp->slot == LSMBLOB_FIRST || cp->slot == hp->lsmid->slot)
+			return hp->hook.inode_notifysecctx(inode, cp->context,
+							   cp->len);
+	return 0;
 }
 EXPORT_SYMBOL(security_inode_notifysecctx);
 
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+int security_inode_setsecctx(struct dentry *dentry, struct lsmcontext *cp)
 {
-	return call_int_hook(inode_setsecctx, 0, dentry, ctx, ctxlen);
+	struct security_hook_list *hp;
+
+	if (WARN_ON(cp->slot != LSMBLOB_FIRST))
+		return -EINVAL;
+
+	hlist_for_each_entry(hp, &security_hook_heads.inode_setsecctx, list)
+		return hp->hook.inode_setsecctx(dentry, cp->context, cp->len);
+	return 0;
 }
 EXPORT_SYMBOL(security_inode_setsecctx);
 
 int security_inode_getsecctx(struct inode *inode, struct lsmcontext *cp)
 {
 	struct security_hook_list *hp;
+	char *finalctx = NULL;
+	int rc = -EOPNOTSUPP;
+	int finallen = 0;
 
 	memset(cp, 0, sizeof(*cp));
 
 	hlist_for_each_entry(hp, &security_hook_heads.inode_getsecctx, list) {
+		rc = hp->hook.inode_getsecctx(inode, (void **)&cp->context,
+					      &cp->len);
+		if (rc) {
+			kfree(finalctx);
+			return rc;
+		}
 		cp->slot = hp->lsmid->slot;
-		return hp->hook.inode_getsecctx(inode, (void **)&cp->context,
-						&cp->len);
+		rc = append_ctx(&finalctx, &finallen, hp->lsmid->lsm,
+				cp->context, cp->len);
+		security_release_secctx(cp);
+		if (rc) {
+			kfree(finalctx);
+			return rc;
+		}
 	}
-	return -EOPNOTSUPP;
+	cp->slot = LSMBLOB_COMPOUND;
+	cp->context = finalctx;
+	cp->len = finallen;
+	return 0;
 }
 EXPORT_SYMBOL(security_inode_getsecctx);
 
@@ -2433,12 +2522,9 @@ int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 	struct security_hook_list *hp;
 	char *final = NULL;
 	char *cp;
-	char *tp;
 	int rc = 0;
 	unsigned finallen = 0;
-	unsigned llen;
 	unsigned clen = 0;
-	unsigned tlen;
 
 	switch (display) {
 	case LSMBLOB_DISPLAY:
@@ -2471,29 +2557,8 @@ int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				kfree(final);
 				return rc;
 			}
-			/*
-			 * Don't propogate trailing nul bytes.
-			 */
-			clen = strnlen(cp, clen) + 1;
-			llen = strlen(hp->lsmid->lsm) + 1;
-			tlen = llen + clen;
-			if (final)
-				tlen += finallen;
-			tp = kzalloc(tlen, GFP_KERNEL);
-			if (tp == NULL) {
-				kfree(cp);
-				kfree(final);
-				return -ENOMEM;
-			}
-			if (final)
-				memcpy(tp, final, finallen);
-			memcpy(tp + finallen, hp->lsmid->lsm, llen);
-			memcpy(tp + finallen + llen, cp, clen);
-			kfree(cp);
-			if (final)
-				kfree(final);
-			final = tp;
-			finallen = tlen;
+			rc = append_ctx(&final, &finallen, hp->lsmid->lsm,
+					cp, clen);
 		}
 		if (final == NULL)
 			return -EINVAL;
