diff mbox series

[v36,18/33] LSM: Use lsmcontext in security_dentry_init_security

Message ID 20220609230146.319210-19-casey@schaufler-ca.com (mailing list archive)
State Changes Requested
Delegated to: Paul Moore
Headers show
Series [v36,01/33] integrity: disassociate ima_filter_rule from security_audit_rule | expand

Commit Message

Casey Schaufler June 9, 2022, 11:01 p.m. UTC
Replace the (secctx,seclen) pointer pair with a single
lsmcontext pointer to allow return of the LSM identifier
along with the context and context length. This allows
security_release_secctx() to know how to release the
context. Callers have been modified to use or save the
returned data from the new structure.

Special care is taken in the NFS code, which uses the
same data structure for its own copied labels as it does
for the data which comes from security_dentry_init_security().
In the case of copied labels the data has to be freed, not
released.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 fs/ceph/super.h          |  3 +--
 fs/ceph/xattr.c          | 19 ++++++-------------
 fs/fuse/dir.c            | 24 +++++++++++++-----------
 fs/nfs/dir.c             |  2 +-
 fs/nfs/inode.c           | 17 ++++++++++-------
 fs/nfs/internal.h        |  8 +++++---
 fs/nfs/nfs4proc.c        | 20 ++++++++------------
 fs/nfs/nfs4xdr.c         | 22 ++++++++++++----------
 include/linux/nfs4.h     |  8 ++++----
 include/linux/nfs_fs.h   |  2 +-
 include/linux/security.h |  7 +++----
 security/security.c      | 12 ++++++++----
 12 files changed, 72 insertions(+), 72 deletions(-)

Comments

kernel test robot June 10, 2022, 3:05 a.m. UTC | #1
Hi Casey,

I love your patch! Perhaps something to improve:

[auto build test WARNING on pcmoore-audit/next]
[also build test WARNING on pcmoore-selinux/next linus/master]
[cannot apply to jmorris-security/next-testing]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Casey-Schaufler/integrity-disassociate-ima_filter_rule-from-security_audit_rule/20220610-080129
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit.git next
config: s390-randconfig-r044-20220608 (https://download.01.org/0day-ci/archive/20220610/202206101053.7FemxCmO-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/86d33e271bed739fe32367e703b054ea253bb471
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Casey-Schaufler/integrity-disassociate-ima_filter_rule-from-security_audit_rule/20220610-080129
        git checkout 86d33e271bed739fe32367e703b054ea253bb471
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash fs/fuse/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/build_bug.h:5,
                    from include/linux/container_of.h:5,
                    from include/linux/list.h:5,
                    from include/linux/wait.h:7,
                    from include/linux/wait_bit.h:8,
                    from include/linux/fs.h:6,
                    from fs/fuse/fuse_i.h:17,
                    from fs/fuse/dir.c:9:
   fs/fuse/dir.c: In function 'get_security_context.constprop':
>> include/linux/compiler.h:70:46: warning: 'lsmctx.len' is used uninitialized [-Wuninitialized]
      70 |                 (__if_trace.miss_hit[1]++,1) :          \
         |                                              ^
   fs/fuse/dir.c:467:27: note: 'lsmctx.len' was declared here
     467 |         struct lsmcontext lsmctx;
         |                           ^~~~~~


vim +70 include/linux/compiler.h

a15fd609ad53a6 Linus Torvalds 2019-03-20  59  
a15fd609ad53a6 Linus Torvalds 2019-03-20  60  #define __trace_if_value(cond) ({			\
2bcd521a684cc9 Steven Rostedt 2008-11-21  61  	static struct ftrace_branch_data		\
e04462fb82f8dd Miguel Ojeda   2018-09-03  62  		__aligned(4)				\
33def8498fdde1 Joe Perches    2020-10-21  63  		__section("_ftrace_branch")		\
a15fd609ad53a6 Linus Torvalds 2019-03-20  64  		__if_trace = {				\
2bcd521a684cc9 Steven Rostedt 2008-11-21  65  			.func = __func__,		\
2bcd521a684cc9 Steven Rostedt 2008-11-21  66  			.file = __FILE__,		\
2bcd521a684cc9 Steven Rostedt 2008-11-21  67  			.line = __LINE__,		\
2bcd521a684cc9 Steven Rostedt 2008-11-21  68  		};					\
a15fd609ad53a6 Linus Torvalds 2019-03-20  69  	(cond) ?					\
a15fd609ad53a6 Linus Torvalds 2019-03-20 @70  		(__if_trace.miss_hit[1]++,1) :		\
a15fd609ad53a6 Linus Torvalds 2019-03-20  71  		(__if_trace.miss_hit[0]++,0);		\
a15fd609ad53a6 Linus Torvalds 2019-03-20  72  })
a15fd609ad53a6 Linus Torvalds 2019-03-20  73
Dan Carpenter June 23, 2022, 7:09 a.m. UTC | #2
Hi Casey,

url:    https://github.com/intel-lab-lkp/linux/commits/Casey-Schaufler/integrity-disassociate-ima_filter_rule-from-security_audit_rule/20220610-080129
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit.git next
config: parisc-randconfig-m031-20220622 (https://download.01.org/0day-ci/archive/20220623/202206230827.rGKbTxmu-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
fs/fuse/dir.c:484 get_security_context() error: uninitialized symbol 'name'.

Old smatch warnings:
fs/fuse/dir.c:503 get_security_context() warn: is 'ptr' large enough for 'struct fuse_secctx'? 0

vim +/name +484 fs/fuse/dir.c

3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  462  static int get_security_context(struct dentry *entry, umode_t mode,
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  463  				void **security_ctx, u32 *security_ctxlen)
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  464  {
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  465  	struct fuse_secctx *fctx;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  466  	struct fuse_secctx_header *header;
86d33e271bed73 Casey Schaufler 2022-06-09  467  	struct lsmcontext lsmctx;
                                                        ^^^^^^^^^^^^^^^^^^^^^^^^

86d33e271bed73 Casey Schaufler 2022-06-09  468  	void *ptr;
86d33e271bed73 Casey Schaufler 2022-06-09  469  	u32 total_len = sizeof(*header);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  470  	int err, nr_ctx = 0;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  471  	const char *name;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  472  	size_t namelen;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  473  
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  474  	err = security_dentry_init_security(entry, mode, &entry->d_name,
86d33e271bed73 Casey Schaufler 2022-06-09  475  					    &name, &lsmctx);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  476  	if (err) {
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  477  		if (err != -EOPNOTSUPP)

Imagine "err == -EOPNOTSUPP".

3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  478  			goto out_err;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  479  		/* No LSM is supporting this security hook. Ignore error */
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  480  	}
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  481  
86d33e271bed73 Casey Schaufler 2022-06-09  482  	if (lsmctx.len) {

Then actually "lsmctx.len" is uninitialized.  Everything breaks after
that.

3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  483  		nr_ctx = 1;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11 @484  		namelen = strlen(name) + 1;
                                                                                 ^^^^
Warning.

3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  485  		err = -EIO;
86d33e271bed73 Casey Schaufler 2022-06-09  486  		if (WARN_ON(namelen > XATTR_NAME_MAX + 1 ||
86d33e271bed73 Casey Schaufler 2022-06-09  487  		    lsmctx.len > S32_MAX))
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  488  			goto out_err;
86d33e271bed73 Casey Schaufler 2022-06-09  489  		total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen +
86d33e271bed73 Casey Schaufler 2022-06-09  490  					    lsmctx.len);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  491  	}
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  492  
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  493  	err = -ENOMEM;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  494  	header = ptr = kzalloc(total_len, GFP_KERNEL);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  495  	if (!ptr)
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  496  		goto out_err;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  497  
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  498  	header->nr_secctx = nr_ctx;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  499  	header->size = total_len;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  500  	ptr += sizeof(*header);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  501  	if (nr_ctx) {
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  502  		fctx = ptr;
86d33e271bed73 Casey Schaufler 2022-06-09  503  		fctx->size = lsmctx.len;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  504  		ptr += sizeof(*fctx);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  505  
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  506  		strcpy(ptr, name);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  507  		ptr += namelen;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  508  
86d33e271bed73 Casey Schaufler 2022-06-09  509  		memcpy(ptr, lsmctx.context, lsmctx.len);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  510  	}
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  511  	*security_ctxlen = total_len;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  512  	*security_ctx = header;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  513  	err = 0;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  514  out_err:
86d33e271bed73 Casey Schaufler 2022-06-09  515  	if (nr_ctx)
86d33e271bed73 Casey Schaufler 2022-06-09  516  		security_release_secctx(&lsmctx);
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  517  	return err;
3e2b6fdbdc9ab5 Vivek Goyal     2021-11-11  518  }
diff mbox series

Patch

diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index dd7dac0f984a..967bc060c7af 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -1063,8 +1063,7 @@  struct ceph_acl_sec_ctx {
 	void *acl;
 #endif
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
-	void *sec_ctx;
-	u32 sec_ctxlen;
+	struct lsmcontext lsmctx;
 #endif
 	struct ceph_pagelist *pagelist;
 };
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index b1c81e75f37f..e761c6a151c9 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -1328,8 +1328,7 @@  int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
 	int err;
 
 	err = security_dentry_init_security(dentry, mode, &dentry->d_name,
-					    &name, &as_ctx->sec_ctx,
-					    &as_ctx->sec_ctxlen);
+					    &name, &as_ctx->lsmctx);
 	if (err < 0) {
 		WARN_ON_ONCE(err != -EOPNOTSUPP);
 		err = 0; /* do nothing */
@@ -1354,7 +1353,7 @@  int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
 	 */
 	name_len = strlen(name);
 	err = ceph_pagelist_reserve(pagelist,
-				    4 * 2 + name_len + as_ctx->sec_ctxlen);
+				    4 * 2 + name_len + as_ctx->lsmctx.len);
 	if (err)
 		goto out;
 
@@ -1374,11 +1373,9 @@  int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
 		as_ctx->pagelist = pagelist;
 	}
 
-	ceph_pagelist_encode_32(pagelist, name_len);
-	ceph_pagelist_append(pagelist, name, name_len);
-
-	ceph_pagelist_encode_32(pagelist, as_ctx->sec_ctxlen);
-	ceph_pagelist_append(pagelist, as_ctx->sec_ctx, as_ctx->sec_ctxlen);
+	ceph_pagelist_encode_32(pagelist, as_ctx->lsmctx.len);
+	ceph_pagelist_append(pagelist, as_ctx->lsmctx.context,
+			     as_ctx->lsmctx.len);
 
 	err = 0;
 out:
@@ -1391,16 +1388,12 @@  int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
 
 void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
 {
-#ifdef CONFIG_CEPH_FS_SECURITY_LABEL
-	struct lsmcontext scaff; /* scaffolding */
-#endif
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 	posix_acl_release(as_ctx->acl);
 	posix_acl_release(as_ctx->default_acl);
 #endif
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
-	lsmcontext_init(&scaff, as_ctx->sec_ctx, as_ctx->sec_ctxlen, 0);
-	security_release_secctx(&scaff);
+	security_release_secctx(&as_ctx->lsmctx);
 #endif
 	if (as_ctx->pagelist)
 		ceph_pagelist_release(as_ctx->pagelist);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 74303d6e987b..632dfc3fd65b 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -464,29 +464,30 @@  static int get_security_context(struct dentry *entry, umode_t mode,
 {
 	struct fuse_secctx *fctx;
 	struct fuse_secctx_header *header;
-	void *ctx = NULL, *ptr;
-	u32 ctxlen, total_len = sizeof(*header);
+	struct lsmcontext lsmctx;
+	void *ptr;
+	u32 total_len = sizeof(*header);
 	int err, nr_ctx = 0;
 	const char *name;
 	size_t namelen;
 
 	err = security_dentry_init_security(entry, mode, &entry->d_name,
-					    &name, &ctx, &ctxlen);
+					    &name, &lsmctx);
 	if (err) {
 		if (err != -EOPNOTSUPP)
 			goto out_err;
 		/* No LSM is supporting this security hook. Ignore error */
-		ctxlen = 0;
-		ctx = NULL;
 	}
 
-	if (ctxlen) {
+	if (lsmctx.len) {
 		nr_ctx = 1;
 		namelen = strlen(name) + 1;
 		err = -EIO;
-		if (WARN_ON(namelen > XATTR_NAME_MAX + 1 || ctxlen > S32_MAX))
+		if (WARN_ON(namelen > XATTR_NAME_MAX + 1 ||
+		    lsmctx.len > S32_MAX))
 			goto out_err;
-		total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen + ctxlen);
+		total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen +
+					    lsmctx.len);
 	}
 
 	err = -ENOMEM;
@@ -499,19 +500,20 @@  static int get_security_context(struct dentry *entry, umode_t mode,
 	ptr += sizeof(*header);
 	if (nr_ctx) {
 		fctx = ptr;
-		fctx->size = ctxlen;
+		fctx->size = lsmctx.len;
 		ptr += sizeof(*fctx);
 
 		strcpy(ptr, name);
 		ptr += namelen;
 
-		memcpy(ptr, ctx, ctxlen);
+		memcpy(ptr, lsmctx.context, lsmctx.len);
 	}
 	*security_ctxlen = total_len;
 	*security_ctx = header;
 	err = 0;
 out_err:
-	kfree(ctx);
+	if (nr_ctx)
+		security_release_secctx(&lsmctx);
 	return err;
 }
 
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index a8ecdd527662..bdde5bcd2fa8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -810,7 +810,7 @@  static int nfs_readdir_entry_decode(struct nfs_readdir_descriptor *desc,
 	int ret;
 
 	if (entry->fattr->label)
-		entry->fattr->label->len = NFS4_MAXLABELLEN;
+		entry->fattr->label->lsmctx.len = NFS4_MAXLABELLEN;
 	ret = xdr_decode(desc, entry, stream);
 	if (ret || !desc->plus)
 		return ret;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index b4e46b0ffa2d..d3132f4626d0 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -361,14 +361,15 @@  void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr)
 		return;
 
 	if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
-		error = security_inode_notifysecctx(inode, fattr->label->label,
-				fattr->label->len);
+		error = security_inode_notifysecctx(inode,
+						fattr->label->lsmctx.context,
+						fattr->label->lsmctx.len);
 		if (error)
 			printk(KERN_ERR "%s() %s %d "
 					"security_inode_notifysecctx() %d\n",
 					__func__,
-					(char *)fattr->label->label,
-					fattr->label->len, error);
+					(char *)fattr->label->lsmctx.context,
+					fattr->label->lsmctx.len, error);
 		nfs_clear_label_invalid(inode);
 	}
 }
@@ -384,12 +385,14 @@  struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
 	if (label == NULL)
 		return ERR_PTR(-ENOMEM);
 
-	label->label = kzalloc(NFS4_MAXLABELLEN, flags);
-	if (label->label == NULL) {
+	label->lsmctx.context = kzalloc(NFS4_MAXLABELLEN, flags);
+	if (label->lsmctx.context == NULL) {
 		kfree(label);
 		return ERR_PTR(-ENOMEM);
 	}
-	label->len = NFS4_MAXLABELLEN;
+	label->lsmctx.len = NFS4_MAXLABELLEN;
+	/* Use an invalid LSM slot as this should never be "released". */
+	label->lsmctx.slot = -1;
 
 	return label;
 }
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 8f8cd6e2d4db..b97b66b8b7d0 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -342,13 +342,15 @@  nfs4_label_copy(struct nfs4_label *dst, struct nfs4_label *src)
 	if (!dst || !src)
 		return NULL;
 
-	if (src->len > NFS4_MAXLABELLEN)
+	if (src->lsmctx.len > NFS4_MAXLABELLEN)
 		return NULL;
 
 	dst->lfs = src->lfs;
 	dst->pi = src->pi;
-	dst->len = src->len;
-	memcpy(dst->label, src->label, src->len);
+	/* Use an invalid LSM slot as lsmctx should never be "released" */
+	dst->lsmctx.slot = -1;
+	dst->lsmctx.len = src->lsmctx.len;
+	memcpy(dst->lsmctx.context, src->lsmctx.context, src->lsmctx.len);
 
 	return dst;
 }
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d6bdb0868729..dca0d5c84337 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -123,8 +123,7 @@  nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
 		return NULL;
 
 	err = security_dentry_init_security(dentry, sattr->ia_mode,
-				&dentry->d_name, NULL,
-				(void **)&label->label, &label->len);
+				&dentry->d_name, NULL, &label->lsmctx);
 	if (err == 0)
 		return label;
 
@@ -133,12 +132,8 @@  nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
 static inline void
 nfs4_label_release_security(struct nfs4_label *label)
 {
-	struct lsmcontext scaff; /* scaffolding */
-
-	if (label) {
-		lsmcontext_init(&scaff, label->label, label->len, 0);
-		security_release_secctx(&scaff);
-	}
+	if (label)
+		security_release_secctx(&label->lsmctx);
 }
 static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
 {
@@ -3800,7 +3795,7 @@  nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx,
 		int open_flags, struct iattr *attr, int *opened)
 {
 	struct nfs4_state *state;
-	struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;
+	struct nfs4_label l = { }, *label = NULL;
 
 	label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);
 
@@ -6108,7 +6103,7 @@  static int _nfs4_get_security_label(struct inode *inode, void *buf,
 					size_t buflen)
 {
 	struct nfs_server *server = NFS_SERVER(inode);
-	struct nfs4_label label = {0, 0, buflen, buf};
+	struct nfs4_label label = {0, 0, {buf, buflen, -1} };
 
 	u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
 	struct nfs_fattr fattr = {
@@ -6136,7 +6131,7 @@  static int _nfs4_get_security_label(struct inode *inode, void *buf,
 		return ret;
 	if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
 		return -ENOENT;
-	return label.len;
+	return label.lsmctx.len;
 }
 
 static int nfs4_get_security_label(struct inode *inode, void *buf,
@@ -6213,7 +6208,8 @@  static int nfs4_do_set_security_label(struct inode *inode,
 static int
 nfs4_set_security_label(struct inode *inode, const void *buf, size_t buflen)
 {
-	struct nfs4_label ilabel = {0, 0, buflen, (char *)buf };
+	struct nfs4_label ilabel = {0, 0,
+				    {(char *)buf, buflen, -1}};
 	struct nfs_fattr *fattr;
 	int status;
 
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index acfe5f4bda48..9f1a376fb92c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1154,7 +1154,7 @@  static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 	}
 
 	if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
-		len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
+		len += 4 + 4 + 4 + (XDR_QUADLEN(label->lsmctx.len) << 2);
 		bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
 	}
 
@@ -1186,8 +1186,9 @@  static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 	if (label && (bmval[2] & FATTR4_WORD2_SECURITY_LABEL)) {
 		*p++ = cpu_to_be32(label->lfs);
 		*p++ = cpu_to_be32(label->pi);
-		*p++ = cpu_to_be32(label->len);
-		p = xdr_encode_opaque_fixed(p, label->label, label->len);
+		*p++ = cpu_to_be32(label->lsmctx.len);
+		p = xdr_encode_opaque_fixed(p, label->lsmctx.context,
+					    label->lsmctx.len);
 	}
 	if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
 		*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
@@ -4236,12 +4237,12 @@  static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,
 			return -EIO;
 		if (len < NFS4_MAXLABELLEN) {
 			if (label) {
-				if (label->len) {
-					if (label->len < len)
+				if (label->lsmctx.len) {
+					if (label->lsmctx.len < len)
 						return -ERANGE;
-					memcpy(label->label, p, len);
+					memcpy(label->lsmctx.context, p, len);
 				}
-				label->len = len;
+				label->lsmctx.len = len;
 				label->pi = pi;
 				label->lfs = lfs;
 				status = NFS_ATTR_FATTR_V4_SECURITY_LABEL;
@@ -4250,10 +4251,11 @@  static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,
 		} else
 			printk(KERN_WARNING "%s: label too long (%u)!\n",
 					__func__, len);
-		if (label && label->label)
+		if (label && label->lsmctx.context)
 			dprintk("%s: label=%.*s, len=%d, PI=%d, LFS=%d\n",
-				__func__, label->len, (char *)label->label,
-				label->len, label->pi, label->lfs);
+				__func__, label->lsmctx.len,
+				(char *)label->lsmctx.context,
+				label->lsmctx.len, label->pi, label->lfs);
 	}
 	return status;
 }
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 8d04b6a5964c..5c2d69cf609a 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -15,6 +15,7 @@ 
 
 #include <linux/list.h>
 #include <linux/uidgid.h>
+#include <linux/security.h>
 #include <uapi/linux/nfs4.h>
 #include <linux/sunrpc/msg_prot.h>
 
@@ -44,10 +45,9 @@  struct nfs4_acl {
 #define NFS4_MAXLABELLEN	2048
 
 struct nfs4_label {
-	uint32_t	lfs;
-	uint32_t	pi;
-	u32		len;
-	char	*label;
+	uint32_t		lfs;
+	uint32_t		pi;
+	struct lsmcontext	lsmctx;
 };
 
 typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index a17c337dbdf1..a838d4a45c1b 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -428,7 +428,7 @@  static inline void nfs4_label_free(struct nfs4_label *label)
 {
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 	if (label) {
-		kfree(label->label);
+		kfree(label->lsmctx.context);
 		kfree(label);
 	}
 #endif
diff --git a/include/linux/security.h b/include/linux/security.h
index ca2ed1909608..1884c45f3d84 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -470,8 +470,8 @@  int security_sb_clone_mnt_opts(const struct super_block *oldsb,
 int security_move_mount(const struct path *from_path, const struct path *to_path);
 int security_dentry_init_security(struct dentry *dentry, int mode,
 				  const struct qstr *name,
-				  const char **xattr_name, void **ctx,
-				  u32 *ctxlen);
+				  const char **xattr_name,
+				  struct lsmcontext *lsmcxt);
 int security_dentry_create_files_as(struct dentry *dentry, int mode,
 					struct qstr *name,
 					const struct cred *old,
@@ -888,8 +888,7 @@  static inline int security_dentry_init_security(struct dentry *dentry,
 						 int mode,
 						 const struct qstr *name,
 						 const char **xattr_name,
-						 void **ctx,
-						 u32 *ctxlen)
+						 struct lsmcontext *lsmcxt)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/security/security.c b/security/security.c
index 72df3d0cd233..a01967c66078 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1167,8 +1167,8 @@  void security_inode_free(struct inode *inode)
 
 int security_dentry_init_security(struct dentry *dentry, int mode,
 				  const struct qstr *name,
-				  const char **xattr_name, void **ctx,
-				  u32 *ctxlen)
+				  const char **xattr_name,
+				  struct lsmcontext *lsmctx)
 {
 	struct security_hook_list *hp;
 	int rc;
@@ -1176,9 +1176,13 @@  int security_dentry_init_security(struct dentry *dentry, int mode,
 	/*
 	 * Only one module will provide a security context.
 	 */
-	hlist_for_each_entry(hp, &security_hook_heads.dentry_init_security, list) {
+	hlist_for_each_entry(hp, &security_hook_heads.dentry_init_security,
+			     list) {
 		rc = hp->hook.dentry_init_security(dentry, mode, name,
-						   xattr_name, ctx, ctxlen);
+						   xattr_name,
+						   (void **)&lsmctx->context,
+						   &lsmctx->len);
+		lsmctx->slot = hp->lsmid->slot;
 		if (rc != LSM_RET_DEFAULT(dentry_init_security))
 			return rc;
 	}