Skip to contentSkip to conversation

selinux: Improving SELinux read/write performance

RFC In review Yuichi Nakamura 2 participants 7 messages Sep 6, 2007 – Sep 14, 2007

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 selinux: Improving SELinux read/write performance

Hello.

As I posted before in selinux list,
I found big overhead of SELinux in read/write on some CPUs, and trying tuning.
There were discussion in previous threads.
Part 1:
http://marc.info/?t=118845343400001&r=1&w=2
Part 2:
http://marc.info/?t=118880749800004&r=1&w=2

I would like to RFC again about this topic.

1. Background
Look at benchmark result below.
lmbench simple read/write.
Big overhead exists especially on SH(SuperH) arch.

1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.24     12.3
Simple write    1.00  1.14     14.0

* Base: kernel compiled without SELinux support

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    5.49      130.5
Simple write    2.07    5.10      146.6
# This result is a little different from previous threads, 
# because I changed some kernel configs.

Overhead more than 100%
I also found about 70-90% overhead in ARM.

2. About patch
I found a overhead in selinux_file_permission function. This is a function that is called in read/write calls, and does SELinux permission check.
SELinux checks permission both in open and read/write time. Stephen Smalley sugessted that we can usually skip permission check in selinux_file_permission.
By this patch,
permission check in selinux_file_permssion is done only when
- sid of task has changed after file open
- sid of inode has changed after file open
- policy load or boolean change happen after file open

To detect these changes,
I added entries in file_security struct and saving these values at file open.

And to save sid of inode at the time of file open, I had to add new LSM hook in __dentry_open function.

3. Benchmark after applying this patch
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.12     1.6(Before 12.3)
Simple write    1.00  1.03     3.6(Before 14.0)

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    2.65      11.1(Before 130.5)
Simple write    2.07    2.28      10.5(Before 146.6)

Performance has improved a lot.
I want comments from community.

---
 fs/open.c                         |    5 +++
 include/linux/security.h          |   11 +++++++
 security/selinux/avc.c            |    5 ++-
 security/selinux/hooks.c          |   54 +++++++++++++++++++++++++++++++++++++-
 security/selinux/include/objsec.h |    3 ++
 5 files changed, 76 insertions(+), 2 deletions(-)
security/selinux/avc.c +4 -1
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/avc.c linux-2.6.22/security/selinux/avc.c
@@ -123,6 +123,7 @@ DEFINE_PER_CPU(struct avc_cache_stats, a
 #endif
 
 static struct avc_cache avc_cache;
+u32 policy_seqno = 0;
 static struct avc_callback_node *avc_callbacks;
 static struct kmem_cache *avc_node_cachep;
 
@@ -431,8 +432,10 @@ static int avc_latest_notif_update(int s
 			ret = -EAGAIN;
 		}
 	} else {
-		if (seqno > avc_cache.latest_notif)
+		if (seqno > avc_cache.latest_notif) {
 			avc_cache.latest_notif = seqno;
+			policy_seqno = seqno;
+		}
 	}
 	spin_unlock_irqrestore(&notif_lock, flag);
 
security/selinux/hooks.c +53 -1

90 lines not on this page · raw message

security/selinux/include/objsec.h +3 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h
@@ -53,6 +53,9 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 tsid;             /* SID of task at the time of file open*/
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */
 };
 
 struct superblock_security_struct {
fs/open.c +5 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c
@@ -698,6 +698,11 @@ static struct file *__dentry_open(struct
 
 	if (!open && f->f_op)
 		open = f->f_op->open;
+
+	error = security_dentry_open(f, flags);
+	if (error)
+		goto cleanup_all;
+
 	if (open) {
 		error = open(inode, f);
 		if (error)
include/linux/security.h +11 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file, int flags);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return security_ops->dentry_open (file, flags);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);

Regards,

Signature · 10 lines
Yuichi Nakamura
Hitachi Software Engineering Co., Ltd.
Japan SELinux Users Group(JSELUG): http://www.selinux.gr.jp/
SELinux Policy Editor: http://seedit.sourceforge.net/


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#1 lore raw
Quoted 11 lines · Yuichi Nakamura

Hello.

As I posted before in selinux list,
I found big overhead of SELinux in read/write on some CPUs, and trying tuning.
There were discussion in previous threads.
Part 1:
http://marc.info/?t=118845343400001&r=1&w=2
Part 2:
http://marc.info/?t=118880749800004&r=1&w=2

Quoted 1 line

I would like to RFC again about this topic.

Thanks for your work on this, as this is clearly an important area to improve.

Quoted 74 lines

1. Background
Look at benchmark result below.
lmbench simple read/write.
Big overhead exists especially on SH(SuperH) arch.

1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.24     12.3
Simple write    1.00  1.14     14.0

* Base: kernel compiled without SELinux support

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    5.49      130.5
Simple write    2.07    5.10      146.6
# This result is a little different from previous threads, 
# because I changed some kernel configs.

Overhead more than 100%
I also found about 70-90% overhead in ARM.

2. About patch
I found a overhead in selinux_file_permission function. This is a function that is called in read/write calls, and does SELinux permission check.
SELinux checks permission both in open and read/write time. Stephen Smalley sugessted that we can usually skip permission check in selinux_file_permission.
By this patch,
permission check in selinux_file_permssion is done only when
- sid of task has changed after file open
- sid of inode has changed after file open
- policy load or boolean change happen after file open

To detect these changes,
I added entries in file_security struct and saving these values at file open.

And to save sid of inode at the time of file open, I had to add new LSM hook in __dentry_open function.

3. Benchmark after applying this patch
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.12     1.6(Before 12.3)
Simple write    1.00  1.03     3.6(Before 14.0)

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    2.65      11.1(Before 130.5)
Simple write    2.07    2.28      10.5(Before 146.6)

Performance has improved a lot.
I want comments from community.

---
 fs/open.c                         |    5 +++
 include/linux/security.h          |   11 +++++++
 security/selinux/avc.c            |    5 ++-
 security/selinux/hooks.c          |   54 +++++++++++++++++++++++++++++++++++++-
 security/selinux/include/objsec.h |    3 ++
 5 files changed, 76 insertions(+), 2 deletions(-)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/avc.c linux-2.6.22/security/selinux/avc.c

security/selinux/avc.c +1 -0
@@ -123,6 +123,7 @@ DEFINE_PER_CPU(struct avc_cache_stats, a
 #endif
 
 static struct avc_cache avc_cache;
+u32 policy_seqno = 0;
 static struct avc_callback_node *avc_callbacks;
 static struct kmem_cache *avc_node_cachep;
 

security/selinux/avc.c static int avc_latest_notif_update(int s · line 438

Quoted 8 lines
security/selinux/avc.c +3 -1
@@ -431,8 +432,10 @@ static int avc_latest_notif_update(int s
 			ret = -EAGAIN;
 		}
 	} else {
-		if (seqno > avc_cache.latest_notif)
+		if (seqno > avc_cache.latest_notif) {
 			avc_cache.latest_notif = seqno;
+			policy_seqno = seqno;
+		}

I would have just provided an avc interface for obtaining the seqno, e.g.
u32 avc_policy_seqno(void)

{
	return avc_cache.latest_notif;
}

security/selinux/hooks.c line 87

Quoted 11 lines
security/selinux/avc.c +0 -0
 	}
 	spin_unlock_irqrestore(&notif_lock, flag);
 

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c

security/selinux/hooks.c +1 -0
@@ -84,6 +84,7 @@
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern int selinux_compat_net;
+extern u32 policy_seqno;

I think that they frown upon extern declarations in .c files (versus in .h files), so we don't want to add more - we ultimately should clean up what we presently have.

security/selinux/hooks.c static int file_alloc_security(struct fi · line 224

Quoted 8 lines
security/selinux/hooks.c +1 -0
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
@@ -220,6 +221,8 @@ static int file_alloc_security(struct fi
 
 	fsec->file = file;
 	fsec->sid = tsec->sid;
+	fsec->tsid = tsec->sid;

I'm not sure why we need the separate field here, as fsec->sid already holds the allocating task SID and doesn't change.

security/selinux/hooks.c static int file_alloc_security(struct fi · line 225

Quoted 1 line
security/selinux/hooks.c +1 -0
+	fsec->pseqno = policy_seqno;

Not sure that you want to set the seqno here versus from your new hook, as it could conceivably change in between.

security/selinux/hooks.c static int selinux_inode_listsecurity(st · line 2464

Quoted 9 lines
security/selinux/hooks.c +1 -1
 	fsec->fown_sid = tsec->sid;
 	file->f_security = fsec;
 
@@ -2458,7 +2461,7 @@ static int selinux_inode_listsecurity(st
 
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int do_selinux_file_permission(struct file *file, int mask)

Might want to rename for clarity, e.g.
selinux_revalidate_file_permission or
selinux_file_permission_slow (slow path) or
something similar.

Quoted 25 lines
security/selinux/hooks.c +18 -0
 {
 	int rc;
 	struct inode *inode = file->f_path.dentry->d_inode;
@@ -2480,6 +2483,43 @@ static int selinux_file_permission(struc
 	return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct task_security_struct *tsec = current->security;
+	struct file_security_struct *fsec = file->f_security;
+	struct inode_security_struct *isec = inode->i_security;
+
+	if (!mask) {
+		/* No permission to check.  Existence test. */
+		return 0;
+	}
+
+	if (tsec->sid != fsec->tsid) {
+		if (tsec->sid != fsec->sid) {
+			struct vfsmount *mnt = file->f_path.mnt;
+			struct dentry *dentry = file->f_path.dentry;
+			struct avc_audit_data ad;
+			int rc;

security/selinux/hooks.c static int selinux_file_permission(struc · line 2513

Quoted 10 lines
security/selinux/hooks.c +10 -0
+			AVC_AUDIT_DATA_INIT(&ad, FS);
+			ad.u.fs.mnt = mnt;
+			ad.u.fs.dentry = dentry;
+			rc = avc_has_perm(tsec->sid, fsec->sid,
+					  SECCLASS_FD,
+					  FD__USE,
+					  &ad);
+			if (rc)
+				return rc;
+		}

Why inline the FD_USE check here given that you are falling back to the full function call anyway? I also don't see why you separate this from the rest of the comparison - I'd just make it something like:

	if (tsec->sid == fsec->sid && isec->sid == fsec->isid &&
	    avc_seqno() == fsec->seqno)
		return selinux_netlbl_inode_permission(inode, mask);
	return selinux_revalidate_file_permission(file, mask);
Quoted 4 lines
security/selinux/hooks.c +0 -0
 static int selinux_file_alloc_security(struct file *file)
 {
 	return file_alloc_security(file);

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2764

Quoted 10 lines
security/selinux/hooks.c +7 -0
@@ -2715,6 +2755,16 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file, int flags)
+{
+	struct file_security_struct *fsec;
+	struct inode_security_struct *isec;
+	fsec = file->f_security;
+	isec = file->f_path.dentry->d_inode->i_security;
+	fsec->isid = isec->sid;

Set the seqno here too. Ideally, it would come straight from the AVC entry that was used for the open-time check, but that is a bit more invasive and there will always be a small window there.

security/selinux/include/objsec.h struct file_security_struct { · line 58

Quoted 10 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h

security/selinux/include/objsec.h +3 -0
@@ -53,6 +53,9 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 tsid;             /* SID of task at the time of file open*/
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */

No need for tsid above I think.

Quoted 25 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h

include/linux/security.h +6 -0
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file, int flags);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);

include/linux/security.h static inline int security_file_receive · line 1870

Quoted 10 lines
include/linux/security.h +5 -0
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return security_ops->dentry_open (file, flags);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);

Need to also provide the stub definition in the #else case (SECURITY=n) and a stub function for the dummy security module.

Thanks.

Signature · 8 lines
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#2 lore raw

On Thu, 06 Sep 2007 09:47:15 -0400
Stephen Smalley wrote:
<snip>

Quoted 14 lines

Review static int avc_latest_notif_update(int s · line 438

Quoted 10 lines
(quoted diff) +3 -1
@@ -431,8 +432,10 @@ static int avc_latest_notif_update(int s
 			ret = -EAGAIN;
 		}
 	} else {
-		if (seqno > avc_cache.latest_notif)
+		if (seqno > avc_cache.latest_notif) {
 			avc_cache.latest_notif = seqno;
+			policy_seqno = seqno;
+		}

I would have just provided an avc interface for obtaining the seqno, e.g.
u32 avc_policy_seqno(void)

Quoted 3 lines
{
	return avc_cache.latest_notif;
}

Fixed.

Quoted 4 lines
Quoted 4 lines
(quoted diff) +0 -0
 	}
 	spin_unlock_irqrestore(&notif_lock, flag);
 
Quoted 12 lines

security/selinux/hooks.c line 87

Quoted 8 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c

security/selinux/hooks.c +1 -0
@@ -84,6 +84,7 @@
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern int selinux_compat_net;
+extern u32 policy_seqno;

I think that they frown upon extern declarations in .c files (versus in .h files), so we don't want to add more - we ultimately should clean up what we presently have.

Removed policy_seqno.

Quoted 6 lines
Quoted 6 lines
security/selinux/hooks.c +0 -0
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
@@ -220,6 +221,8 @@ static int file_alloc_security(struct fi
 
Quoted 6 lines

security/selinux/hooks.c static int file_alloc_security(struct fi · line 224

Quoted 3 lines
security/selinux/hooks.c +1 -0
 	fsec->file = file;
 	fsec->sid = tsec->sid;
+	fsec->tsid = tsec->sid;

I'm not sure why we need the separate field here, as fsec->sid already holds the allocating task SID and doesn't change.

I see.
removed fsec->tsid.

Quoted 4 lines

security/selinux/hooks.c static int file_alloc_security(struct fi · line 225

Quoted 1 line
security/selinux/hooks.c +1 -0
+	fsec->pseqno = policy_seqno;

Not sure that you want to set the seqno here versus from your new hook, as it could conceivably change in between.

Fixed, pseqno is set in selinux_dentry_open.

Quoted 7 lines
Quoted 7 lines
security/selinux/hooks.c +0 -0
 	fsec->fown_sid = tsec->sid;
 	file->f_security = fsec;
 
@@ -2458,7 +2461,7 @@ static int selinux_inode_listsecurity(st
 
 /* file security operations */
 
Quoted 7 lines

security/selinux/hooks.c static int selinux_inode_listsecurity(st · line 2464

Quoted 2 lines
security/selinux/hooks.c +1 -1
-static int selinux_file_permission(struct file *file, int mask)
+static int do_selinux_file_permission(struct file *file, int mask)

Might want to rename for clarity, e.g.
selinux_revalidate_file_permission or
selinux_file_permission_slow (slow path) or
something similar.

Renamed to selinux_revalidate_file_permission.

Quoted 40 lines
Quoted 26 lines
security/selinux/hooks.c +18 -0
 {
 	int rc;
 	struct inode *inode = file->f_path.dentry->d_inode;
@@ -2480,6 +2483,43 @@ static int selinux_file_permission(struc
 	return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct task_security_struct *tsec = current->security;
+	struct file_security_struct *fsec = file->f_security;
+	struct inode_security_struct *isec = inode->i_security;
+
+	if (!mask) {
+		/* No permission to check.  Existence test. */
+		return 0;
+	}
+
+	if (tsec->sid != fsec->tsid) {
+		if (tsec->sid != fsec->sid) {
+			struct vfsmount *mnt = file->f_path.mnt;
+			struct dentry *dentry = file->f_path.dentry;
+			struct avc_audit_data ad;
+			int rc;

security/selinux/hooks.c static int selinux_file_permission(struc · line 2513

Quoted 10 lines
security/selinux/hooks.c +10 -0
+			AVC_AUDIT_DATA_INIT(&ad, FS);
+			ad.u.fs.mnt = mnt;
+			ad.u.fs.dentry = dentry;
+			rc = avc_has_perm(tsec->sid, fsec->sid,
+					  SECCLASS_FD,
+					  FD__USE,
+					  &ad);
+			if (rc)
+				return rc;
+		}

Why inline the FD_USE check here given that you are falling back to the full function call anyway? I also don't see why you separate this from the rest of the comparison - I'd just make it something like:

Quoted 4 lines
	if (tsec->sid == fsec->sid && isec->sid == fsec->isid &&
	    avc_seqno() == fsec->seqno)
		return selinux_netlbl_inode_permission(inode, mask);
	return selinux_revalidate_file_permission(file, mask);

Thanks, I missed that.
FD_USE check is called in file_permission..
Fixed like you pointed out.

Quoted 7 lines
Quoted 7 lines
security/selinux/hooks.c +0 -0
 static int selinux_file_alloc_security(struct file *file)
 {
 	return file_alloc_security(file);
@@ -2715,6 +2755,16 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
Quoted 11 lines

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2764

Quoted 7 lines
security/selinux/hooks.c +7 -0
+static int selinux_dentry_open(struct file *file, int flags)
+{
+	struct file_security_struct *fsec;
+	struct inode_security_struct *isec;
+	fsec = file->f_security;
+	isec = file->f_path.dentry->d_inode->i_security;
+	fsec->isid = isec->sid;

Set the seqno here too. Ideally, it would come straight from the AVC entry that was used for the open-time check, but that is a bit more invasive and there will always be a small window there.

Fixed, setting pseqno now.

Quoted 4 lines
Quoted 4 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h

security/selinux/include/objsec.h +0 -0
@@ -53,6 +53,9 @@ struct file_security_struct {
Quoted 8 lines

security/selinux/include/objsec.h struct file_security_struct { · line 58

Quoted 6 lines
security/selinux/include/objsec.h +3 -0
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 tsid;             /* SID of task at the time of file open*/
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */

No need for tsid above I think.

Removed tsid.

Quoted 27 lines
Quoted 27 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h

include/linux/security.h +6 -0
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file, int flags);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);
 }
 
Quoted 11 lines

include/linux/security.h static inline int security_file_receive · line 1870

Quoted 8 lines
include/linux/security.h +5 -0
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return security_ops->dentry_open (file, flags);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);

Need to also provide the stub definition in the #else case (SECURITY=n) and a stub function for the dummy security module.

Fixed.

Quoted 6 lines

Thanks.

Signature · 2 lines
Stephen Smalley
National Security Agency

Next is updated patch.

---
 fs/open.c                         |    5 +++++
 include/linux/security.h          |   16 ++++++++++++++++
 security/selinux/avc.c            |    5 +++++
 security/selinux/hooks.c          |   36 +++++++++++++++++++++++++++++++++++-
 security/selinux/include/avc.h    |    2 ++
 security/selinux/include/objsec.h |    2 ++
 6 files changed, 65 insertions(+), 1 deletion(-)
security/selinux/avc.c +5 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/avc.c linux-2.6.22/security/selinux/avc.c
@@ -913,3 +913,8 @@ int avc_has_perm(u32 ssid, u32 tsid, u16
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	return rc;
 }
+
+u32 avc_policy_seqno(void)
+{
+	return avc_cache.latest_notif;
+}
security/selinux/hooks.c +35 -1
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c
@@ -14,6 +14,8 @@
  *                          <[email protected]>
  *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
  *                     Paul Moore, <[email protected]>
+ *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ *                     Yuichi Nakamura <[email protected]>
  *
  *	This program is free software; you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License version 2,
@@ -2458,7 +2460,7 @@ static int selinux_inode_listsecurity(st
 
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_revalidate_file_permission(struct file *file, int mask)
 {
 	int rc;
 	struct inode *inode = file->f_path.dentry->d_inode;
@@ -2480,6 +2482,25 @@ static int selinux_file_permission(struc
 	return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct task_security_struct *tsec = current->security;
+	struct file_security_struct *fsec = file->f_security;
+	struct inode_security_struct *isec = inode->i_security;
+
+	if (!mask) {
+		/* No permission to check.  Existence test. */
+		return 0;
+	}
+
+	if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+	    && fsec->pseqno == avc_policy_seqno())
+		return selinux_netlbl_inode_permission(inode, mask);
+
+	return selinux_revalidate_file_permission(file, mask);
+}
+
 static int selinux_file_alloc_security(struct file *file)
 {
 	return file_alloc_security(file);
@@ -2715,6 +2736,17 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file, int flags)
+{
+	struct file_security_struct *fsec;
+	struct inode_security_struct *isec;
+	fsec = file->f_security;
+	isec = file->f_path.dentry->d_inode->i_security;
+	fsec->isid = isec->sid;
+	fsec->pseqno = avc_policy_seqno();
+	return 0;
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -4780,6 +4812,8 @@ static struct security_operations selinu
 	.file_send_sigiotask =		selinux_file_send_sigiotask,
 	.file_receive =			selinux_file_receive,
 
+	.dentry_open =                  selinux_dentry_open,
+
 	.task_create =			selinux_task_create,
 	.task_alloc_security =		selinux_task_alloc_security,
 	.task_free_security =		selinux_task_free_security,
security/selinux/include/avc.h +2 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/avc.h linux-2.6.22/security/selinux/include/avc.h
@@ -110,6 +110,8 @@ int avc_has_perm(u32 ssid, u32 tsid,
                  u16 tclass, u32 requested,
                  struct avc_audit_data *auditdata);
 
+u32 avc_policy_seqno(void);
+
 #define AVC_CALLBACK_GRANT		1
 #define AVC_CALLBACK_TRY_REVOKE		2
 #define AVC_CALLBACK_REVOKE		4
security/selinux/include/objsec.h +2 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h
@@ -53,6 +53,8 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */
 };
 
 struct superblock_security_struct {
fs/open.c +5 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c
@@ -698,6 +698,11 @@ static struct file *__dentry_open(struct
 
 	if (!open && f->f_op)
 		open = f->f_op->open;
+
+	error = security_dentry_open(f, flags);
+	if (error)
+		goto cleanup_all;
+
 	if (open) {
 		error = open(inode, f);
 		if (error)
include/linux/security.h +16 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file, int flags);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return security_ops->dentry_open (file, flags);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);
@@ -2529,6 +2540,11 @@ static inline int security_file_receive 
 	return 0;
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return 0;
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return 0;

Regards,

Signature · 10 lines
Yuichi Nakamura
Hitachi Software Engineering Co., Ltd.
Japan SELinux Users Group(JSELUG): http://www.selinux.gr.jp/
SELinux Policy Editor: http://seedit.sourceforge.net/


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#3 lore raw
Quoted 1 line · Yuichi Nakamura

Next is updated patch.

Thanks.
Please include the short description of the patch though when re-submitting.

Quoted 9 lines
---
 fs/open.c                         |    5 +++++
 include/linux/security.h          |   16 ++++++++++++++++
 security/selinux/avc.c            |    5 +++++
 security/selinux/hooks.c          |   36 +++++++++++++++++++++++++++++++++++-
 security/selinux/include/avc.h    |    2 ++
 security/selinux/include/objsec.h |    2 ++
 6 files changed, 65 insertions(+), 1 deletion(-)

Still missing the necessary changes to security/dummy.c (add dummy_dentry_open() and update security_fixup_ops()). For CONFIG_SECURITY=y but SELinux disabled.

Also, have you re-run your benchmarks with this version of the patch?

Quoted 174 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/avc.c linux-2.6.22/security/selinux/avc.c

security/selinux/avc.c +5 -0
@@ -913,3 +913,8 @@ int avc_has_perm(u32 ssid, u32 tsid, u16
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	return rc;
 }
+
+u32 avc_policy_seqno(void)
+{
+	return avc_cache.latest_notif;
+}

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c

security/selinux/hooks.c +35 -1
@@ -14,6 +14,8 @@
  *                          <[email protected]>
  *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
  *                     Paul Moore, <[email protected]>
+ *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ *                     Yuichi Nakamura <[email protected]>
  *
  *	This program is free software; you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License version 2,
@@ -2458,7 +2460,7 @@ static int selinux_inode_listsecurity(st
 
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_revalidate_file_permission(struct file *file, int mask)
 {
 	int rc;
 	struct inode *inode = file->f_path.dentry->d_inode;
@@ -2480,6 +2482,25 @@ static int selinux_file_permission(struc
 	return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct task_security_struct *tsec = current->security;
+	struct file_security_struct *fsec = file->f_security;
+	struct inode_security_struct *isec = inode->i_security;
+
+	if (!mask) {
+		/* No permission to check.  Existence test. */
+		return 0;
+	}
+
+	if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+	    && fsec->pseqno == avc_policy_seqno())
+		return selinux_netlbl_inode_permission(inode, mask);
+
+	return selinux_revalidate_file_permission(file, mask);
+}
+
 static int selinux_file_alloc_security(struct file *file)
 {
 	return file_alloc_security(file);
@@ -2715,6 +2736,17 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file, int flags)
+{
+	struct file_security_struct *fsec;
+	struct inode_security_struct *isec;
+	fsec = file->f_security;
+	isec = file->f_path.dentry->d_inode->i_security;
+	fsec->isid = isec->sid;
+	fsec->pseqno = avc_policy_seqno();
+	return 0;
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -4780,6 +4812,8 @@ static struct security_operations selinu
 	.file_send_sigiotask =		selinux_file_send_sigiotask,
 	.file_receive =			selinux_file_receive,
 
+	.dentry_open =                  selinux_dentry_open,
+
 	.task_create =			selinux_task_create,
 	.task_alloc_security =		selinux_task_alloc_security,
 	.task_free_security =		selinux_task_free_security,

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/avc.h linux-2.6.22/security/selinux/include/avc.h

security/selinux/include/avc.h +2 -0
@@ -110,6 +110,8 @@ int avc_has_perm(u32 ssid, u32 tsid,
                  u16 tclass, u32 requested,
                  struct avc_audit_data *auditdata);
 
+u32 avc_policy_seqno(void);
+
 #define AVC_CALLBACK_GRANT		1
 #define AVC_CALLBACK_TRY_REVOKE		2
 #define AVC_CALLBACK_REVOKE		4

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h

security/selinux/include/objsec.h +2 -0
@@ -53,6 +53,8 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */
 };
 
 struct superblock_security_struct {

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c

fs/open.c +5 -0
@@ -698,6 +698,11 @@ static struct file *__dentry_open(struct
 
 	if (!open && f->f_op)
 		open = f->f_op->open;
+
+	error = security_dentry_open(f, flags);
+	if (error)
+		goto cleanup_all;
+
 	if (open) {
 		error = open(inode, f);
 		if (error)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h

include/linux/security.h +16 -0
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file, int flags);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return security_ops->dentry_open (file, flags);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);
@@ -2529,6 +2540,11 @@ static inline int security_file_receive 
 	return 0;
 }
 
+static inline int security_dentry_open (struct file *file, int flags)
+{
+	return 0;
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return 0;

Regards,

Signature · 8 lines
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#4 lore raw

Hi.

Stephen Smalley pointed out possibility of race condition in off-list discussion.

Quoted 8 lines · Stephen Smalley

One other observation about the patch: it presently leaves open a (small) race window in which the file could get relabeled or policy gets reloaded between the time of the normal permission check (from selinux_inode_permission) and the time you copy the inode SID and policy seqno to the file security struct. In which case you might end up never revalidating access upon read/write even though conditions changed since the open-time permission check. Not sure how to cleanly fix in a lock-free manner, and adding locks here will only make matters worse.

To fix that, permission has to be checked in selinux_dentry_open. Therefore, in open, number of permission checks increased. As shown in benchmark result below, it does not affect open/close performance so much.

Following is benchmark result.
* Benchmark
lmbench simple read,write,open/close.

* Before tuning
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.24     12.3
Simple write    1.02  1.14     14.0
open/close      5.97  7.45     24.9

* Base: kernel compiled without SELinux support

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    5.49      130.5
Simple write    2.07    5.10      146.6
open/close      32.6    62.8      93.0

* After tuning
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.13     2.3(Before 12.3)
Simple write    1.02  1.024    0.6(Before 14.0)
open/close      5.97  7.48     25.3(Before 24.9)

* Base: kernel compiled without SELinux support

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    2.63      10.4(Before 130.5)
Simple write    2.07    2.34      13.1(Before 146.6)
open/close      32.6    58.7      80.2(before 93.0)

Next is a patch.

* Description of patch
This patch improves performance of read/write in SELinux. It improves performance by skipping permission check in selinux_file_permission. Permission is only checked when sid change or policy load is detected after file open. To detect sid change, new LSM hook securiy_dentry_open is added.

---
 fs/open.c                         |    5 ++++
 include/linux/security.h          |   16 ++++++++++++++
 security/dummy.c                  |    6 +++++
 security/selinux/avc.c            |    5 ++++
 security/selinux/hooks.c          |   43 +++++++++++++++++++++++++++++++++++++-
 security/selinux/include/avc.h    |    2 +
 security/selinux/include/objsec.h |    2 +
 7 files changed, 78 insertions(+), 1 deletion(-)
security/selinux/avc.c +5 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/avc.c linux-2.6.22/security/selinux/avc.c
@@ -913,3 +913,8 @@ int avc_has_perm(u32 ssid, u32 tsid, u16
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	return rc;
 }
+
+u32 avc_policy_seqno(void)
+{
+	return avc_cache.latest_notif;
+}
security/selinux/hooks.c +42 -1

79 lines not on this page · raw message

security/selinux/include/avc.h +2 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/avc.h linux-2.6.22/security/selinux/include/avc.h
@@ -110,6 +110,8 @@ int avc_has_perm(u32 ssid, u32 tsid,
                  u16 tclass, u32 requested,
                  struct avc_audit_data *auditdata);
 
+u32 avc_policy_seqno(void);
+
 #define AVC_CALLBACK_GRANT		1
 #define AVC_CALLBACK_TRY_REVOKE		2
 #define AVC_CALLBACK_REVOKE		4
security/selinux/include/objsec.h +2 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/include/objsec.h linux-2.6.22/security/selinux/include/objsec.h
@@ -53,6 +53,8 @@ struct file_security_struct {
 	struct file *file;              /* back pointer to file object */
 	u32 sid;              /* SID of open file description */
 	u32 fown_sid;         /* SID of file owner (for SIGIO) */
+	u32 isid;             /* SID of inode at the time of file open */
+	u32 pseqno;           /* Policy seqno at the time of file open */
 };
 
 struct superblock_security_struct {
fs/open.c +5 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c
@@ -696,8 +696,13 @@ static struct file *__dentry_open(struct
 	f->f_op = fops_get(inode->i_fop);
 	file_move(f, &inode->i_sb->s_files);
 
+	error = security_dentry_open(f);
+	if (error)
+		goto cleanup_all;
+
 	if (!open && f->f_op)
 		open = f->f_op->open;
+
 	if (open) {
 		error = open(inode, f);
 		if (error)
include/linux/security.h +16 -0
diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *
  * Security hooks for task operations.
  *
  * @task_create:
@@ -1253,6 +1258,7 @@ struct security_operations {
 	int (*file_send_sigiotask) (struct task_struct * tsk,
 				    struct fown_struct * fown, int sig);
 	int (*file_receive) (struct file * file);
+	int (*dentry_open)  (struct file *file);
 
 	int (*task_create) (unsigned long clone_flags);
 	int (*task_alloc_security) (struct task_struct * p);
@@ -1854,6 +1860,11 @@ static inline int security_file_receive 
 	return security_ops->file_receive (file);
 }
 
+static inline int security_dentry_open (struct file *file)
+{
+	return security_ops->dentry_open (file);
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return security_ops->task_create (clone_flags);
@@ -2529,6 +2540,11 @@ static inline int security_file_receive 
 	return 0;
 }
 
+static inline int security_dentry_open (struct file *file)
+{
+	return 0;
+}
+
 static inline int security_task_create (unsigned long clone_flags)
 {
 	return 0;
security/dummy.c +6 -0
@@ -459,6 +459,11 @@ static int dummy_file_receive (struct fi
 	return 0;
 }
 
+static int dummy_dentry_open (struct file *file)
+{
+	return 0;
+}
+
 static int dummy_task_create (unsigned long clone_flags)
 {
 	return 0;
@@ -1029,6 +1034,7 @@ void security_fixup_ops (struct security
 	set_to_dummy_if_null(ops, file_set_fowner);
 	set_to_dummy_if_null(ops, file_send_sigiotask);
 	set_to_dummy_if_null(ops, file_receive);
+	set_to_dummy_if_null(ops, dentry_open);
 	set_to_dummy_if_null(ops, task_create);
 	set_to_dummy_if_null(ops, task_alloc_security);
 	set_to_dummy_if_null(ops, task_free_security);

Regards,

Signature · 10 lines
Yuichi Nakamura
Hitachi Software Engineering Co., Ltd.
Japan SELinux Users Group(JSELUG): http://www.selinux.gr.jp/
SELinux Policy Editor: http://seedit.sourceforge.net/


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#5 lore raw
Quoted 45 lines · Yuichi Nakamura

Hi.

Stephen Smalley pointed out possibility of race condition in off-list discussion.

Quoted 8 lines · Stephen Smalley

One other observation about the patch: it presently leaves open a (small) race window in which the file could get relabeled or policy gets reloaded between the time of the normal permission check (from selinux_inode_permission) and the time you copy the inode SID and policy seqno to the file security struct. In which case you might end up never revalidating access upon read/write even though conditions changed since the open-time permission check. Not sure how to cleanly fix in a lock-free manner, and adding locks here will only make matters worse.

To fix that, permission has to be checked in selinux_dentry_open. Therefore, in open, number of permission checks increased. As shown in benchmark result below, it does not affect open/close performance so much.

Following is benchmark result.
* Benchmark
lmbench simple read,write,open/close.

* Before tuning
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.24     12.3
Simple write    1.02  1.14     14.0
open/close      5.97  7.45     24.9

* Base: kernel compiled without SELinux support

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    5.49      130.5
Simple write    2.07    5.10      146.6
open/close      32.6    62.8      93.0

* After tuning
1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22

                Base  SELinux  Overhead(%)
Simple read     1.10  1.13     2.3(Before 12.3)
Simple write    1.02  1.024    0.6(Before 14.0)
open/close      5.97  7.48     25.3(Before 24.9)

* Base: kernel compiled without SELinux support

Quoted 7 lines

2) Result for SH(SH4, SH7751R), kernel 2.6.22

                Base    SELinux   Overhead(%)
Simple read     2.39    2.63      10.4(Before 130.5)
Simple write    2.07    2.34      13.1(Before 146.6)
open/close      32.6    58.7      80.2(before 93.0)

Next is a patch.

Thanks, a few comments below.

Quoted 7 lines

* Description of patch
This patch improves performance of read/write in SELinux. It improves performance by skipping permission check in selinux_file_permission. Permission is only checked when sid change or policy load is detected after file open. To detect sid change, new LSM hook securiy_dentry_open is added.

I think I'd reword this a little, e.g.

It reduces the selinux overhead on read/write by only revalidating permissions in selinux_file_permission if the task or inode labels have changed or the policy has changed since the open-time check. A new LSM hook, security_dentry_open, is added to capture the necessary state at open time to allow this optimization.

Quoted 11 lines
---
 fs/open.c                         |    5 ++++
 include/linux/security.h          |   16 ++++++++++++++
 security/dummy.c                  |    6 +++++
 security/selinux/avc.c            |    5 ++++
 security/selinux/hooks.c          |   43 +++++++++++++++++++++++++++++++++++++-
 security/selinux/include/avc.h    |    2 +
 security/selinux/include/objsec.h |    2 +
 7 files changed, 78 insertions(+), 1 deletion(-)

<snip>

security/selinux/hooks.c line 85

Quoted 8 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c

security/selinux/hooks.c +1 -0
@@ -80,6 +82,7 @@
 
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])

Leftover from prior version of the patch, no longer needed.

<snip>

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2747

Quoted 12 lines
security/selinux/hooks.c +8 -0
@@ -2715,6 +2737,23 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file)
+{
+	struct file_security_struct *fsec;
+	struct inode *inode;
+	struct inode_security_struct *isec;
+	inode = file->f_path.dentry->d_inode;
+	fsec = file->f_security;
+	isec = inode->i_security;

I'd add a comment here, e.g.
/*
* Save inode label and policy sequence number
* at open-time so that selinux_file_permission
* can determine whether revalidation is necessary.
* Task label is already saved in the file security
* struct as its SID.
*/

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2753

Quoted 6 lines
security/selinux/hooks.c +6 -0
+	fsec->isid = isec->sid;
+	fsec->pseqno = avc_policy_seqno();
+
+	/*Permission has to be rechecked here.
+	  Policy load of inode sid can happen between
+	  may_open and selinux_dentry_open.*/

Typo in the comment (s/of/or/), coding style isn't right for a multi-line comment, and likely needs clarification, e.g.
/*
* Since the inode label or policy seqno may have changed
* between the selinux_inode_permission check and the saving
* of state above, recheck that access is still permitted.
* Otherwise, access might never be revalidated against the
* new inode label or new policy.
* This check is not redundant - do not remove. */

Quoted 11 lines
security/selinux/hooks.c +3 -0
+	return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c

fs/open.c static struct file *__dentry_open(struct · line 705

Quoted 10 lines
fs/open.c +5 -0
@@ -696,8 +696,13 @@ static struct file *__dentry_open(struct
 	f->f_op = fops_get(inode->i_fop);
 	file_move(f, &inode->i_sb->s_files);
 
+	error = security_dentry_open(f);
+	if (error)
+		goto cleanup_all;
+
 	if (!open && f->f_op)
 		open = f->f_op->open;
+

Extraneous whitespace leftover from prior version of the patch.

Quoted 7 lines
fs/open.c +0 -0
 	if (open) {
 		error = open(inode, f);
 		if (error)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h

include/linux/security.h struct request_sock; · line 510

Quoted 8 lines
include/linux/security.h +5 -0
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *

More precisely, "Save open-time permission checking state for later use upon file_permission, and recheck access if anything has changed since inode_permission."

Signature · 8 lines
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.
#6 lore raw
Quoted 1 line · On Thu, 13 Sep 2007 08:58:32 -0400 Stephen Smalley wrote:

On Wed, 2007-09-12 at 17:51 +0900, Yuichi Nakamura wrote:

<snip>

Quoted 1 line

Thanks, a few comments below.

Thanks for comments!

Quoted 8 lines
Quoted 7 lines

* Description of patch
This patch improves performance of read/write in SELinux. It improves performance by skipping permission check in selinux_file_permission. Permission is only checked when sid change or policy load is detected after file open. To detect sid change, new LSM hook securiy_dentry_open is added.

Quoted 7 lines

I think I'd reword this a little, e.g.

It reduces the selinux overhead on read/write by only revalidating permissions in selinux_file_permission if the task or inode labels have changed or the policy has changed since the open-time check. A new LSM hook, security_dentry_open, is added to capture the necessary state at open time to allow this optimization.

I see, I will use that.

Quoted 17 lines
Quoted 11 lines
---
 fs/open.c                         |    5 ++++
 include/linux/security.h          |   16 ++++++++++++++
 security/dummy.c                  |    6 +++++
 security/selinux/avc.c            |    5 ++++
 security/selinux/hooks.c          |   43 +++++++++++++++++++++++++++++++++++++-
 security/selinux/include/avc.h    |    2 +
 security/selinux/include/objsec.h |    2 +
 7 files changed, 78 insertions(+), 1 deletion(-)

<snip>

Quoted 5 lines

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/security/selinux/hooks.c linux-2.6.22/security/selinux/hooks.c

security/selinux/hooks.c +0 -0
@@ -80,6 +82,7 @@
 
Quoted 5 lines

security/selinux/hooks.c line 85

Quoted 3 lines
security/selinux/hooks.c +1 -0
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])

Leftover from prior version of the patch, no longer needed.

Fixed.

Quoted 15 lines

<snip>

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2747

Quoted 12 lines
security/selinux/hooks.c +8 -0
@@ -2715,6 +2737,23 @@ static int selinux_file_receive(struct f
 	return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file)
+{
+	struct file_security_struct *fsec;
+	struct inode *inode;
+	struct inode_security_struct *isec;
+	inode = file->f_path.dentry->d_inode;
+	fsec = file->f_security;
+	isec = inode->i_security;
Quoted 8 lines

I'd add a comment here, e.g.
/*
* Save inode label and policy sequence number
* at open-time so that selinux_file_permission
* can determine whether revalidation is necessary.
* Task label is already saved in the file security
* struct as its SID.
*/

Fixed.

Quoted 10 lines

security/selinux/hooks.c static int selinux_file_receive(struct f · line 2753

Quoted 7 lines
security/selinux/hooks.c +6 -0
+	fsec->isid = isec->sid;
+	fsec->pseqno = avc_policy_seqno();
+
+	/*Permission has to be rechecked here.
+	  Policy load of inode sid can happen between
+	  may_open and selinux_dentry_open.*/

Typo in the comment (s/of/or/), coding style isn't right for a multi-line comment, and likely needs clarification, e.g.

Quoted 8 lines

/*
* Since the inode label or policy seqno may have changed
* between the selinux_inode_permission check and the saving
* of state above, recheck that access is still permitted.
* Otherwise, access might never be revalidated against the
* new inode label or new policy.
* This check is not redundant - do not remove. */

Fixed.

Quoted 15 lines
Quoted 15 lines
security/selinux/hooks.c +3 -0
+	return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/fs/open.c linux-2.6.22/fs/open.c

fs/open.c +0 -0
@@ -696,8 +696,13 @@ static struct file *__dentry_open(struct
 	f->f_op = fops_get(inode->i_fop);
 	file_move(f, &inode->i_sb->s_files);
 
Quoted 9 lines

fs/open.c static struct file *__dentry_open(struct · line 705

Quoted 7 lines
fs/open.c +5 -0
+	error = security_dentry_open(f);
+	if (error)
+		goto cleanup_all;
+
 	if (!open && f->f_op)
 		open = f->f_op->open;
+

Extraneous whitespace leftover from prior version of the patch.

Fixed.

Quoted 12 lines
Quoted 12 lines
fs/open.c +0 -0
 	if (open) {
 		error = open(inode, f);
 		if (error)

diff -purN -X linux-2.6.22/Documentation/dontdiff linux-2.6.22.orig/include/linux/security.h linux-2.6.22/include/linux/security.h

include/linux/security.h +1 -0
@@ -503,6 +503,11 @@ struct request_sock;
  *	@file contains the file structure being received.
  *	Return 0 if permission is granted.
  *
+ * Security hook for dentry
Quoted 8 lines

include/linux/security.h struct request_sock; · line 510

Quoted 4 lines
include/linux/security.h +4 -0
+ *
+ * @dentry_open
+ *   Check permission or get additional information before opening dentry.
+ *

More precisely, "Save open-time permission checking state for later use upon file_permission, and recheck access if anything has changed since inode_permission."

Fixed.

Quoted 3 lines
Signature · 2 lines
Stephen Smalley
National Security Agency

I would like to send patch in next e-mail in new thread.

Regards,

Signature · 10 lines
Yuichi Nakamura
Hitachi Software Engineering Co., Ltd.
Japan SELinux Users Group(JSELUG): http://www.selinux.gr.jp/
SELinux Policy Editor: http://seedit.sourceforge.net/


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to [email protected] with
the words "unsubscribe selinux" without quotes as the message.