Re: [PATCH 2/2] selinux: implement mmap on /selinux/policy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 2010-07-27 at 16:24 -0400, Eric Paris wrote:
> On Tue, 2010-07-27 at 15:28 -0400, Stephen Smalley wrote:
> > On Tue, 2010-07-27 at 15:24 -0400, Eric Paris wrote:
> > > On Tue, 2010-07-27 at 15:20 -0400, Stephen Smalley wrote:
> > > > On Tue, 2010-07-27 at 15:16 -0400, Eric Paris wrote:
> > > > > On Tue, 2010-07-27 at 15:09 -0400, Stephen Smalley wrote:
> > > > > > On Tue, 2010-07-27 at 14:51 -0400, Eric Paris wrote:
> > > > > 
> > > > > > > My fear was:
> > > > > > > 
> > > > > > > Process A			Process B
> > > > > > > ------------------------	-------------------------
> > > > > > > open("/selinux/policy");
> > > > > > >   plm->data = p1
> > > > > > >   plm->len = l1
> > > > > > >   i_size = l1
> > > > > > > 				load_policy
> > > > > > > 				open("/selinux/policy");
> > > > > > > 				  plm->data = p2;
> > > > > > > 				  plm->len = l2;
> > > > > > > 				  i_size = l2;
> > > > > > > 				stat("/selinux/policy");
> > > > > > > 				  we get i_size=l2
> > > > > > > stat("/selinux/policy");
> > > > > > >   we get i_size=l2  WHOOPS.
> > > > > > 
> > > > > > How is that different from corresponding situation for a regular file?
> > > > > > Doesn't seem like the sort of thing the kernel should worry about.
> > > > > > I thought the check was to prevent arbitrary allocation of kernel memory
> > > > > > by spinning in a loop opening /selinux/policy forever.  But that doesn't
> > > > > > require worrying about the inode size changing.
> > > > > 
> > > > > This is actually more the problem with files in /proc.  The difference
> > > > > is that the plm->data and plm->len are still p1 and l1.  It's just
> > > > > stat() that is going to lie to you.  So you will either mmap to much or
> > > > > too little space and you have no idea how long the data you are going to
> > > > > be reading is...
> > > > > 
> > > > > read() works ok, because it is going to go till the EOF, but you have no
> > > > > idea where that is with mmap.....
> > > > 
> > > > I see.  It appears that setools and coreutils use read() rather than
> > > > mmap() since I could run seinfo, sediff, and cp on /selinux/policy, so
> > > > maybe nothing other than checkpolicy -b would even use the mmap support?
> > > > And that can be worked around by first copying to a regular file.
> > > > checkpolicy -b isn't exactly a typical user command; most people don't
> > > > even know it exists.
> > > 
> > > Another horrific option would be to create a new inode for each open()
> > > so your fd would always have your i_size info....
> > 
> > That would indeed be horrific ;)
> > 
> > > I'm going to finish my second mmap implementation attempt just to learn,
> > > but maybe we want to just throw it all away?  If so, thoughts on
> > > addressing the memory usage issue?
> > 
> > I don't care about userspace getting confused by an interleaving of
> > policy load and policy read - that is a userspace problem IMO.  So I
> > wouldn't be opposed to having the mmap handler upstreamed, although I
> > don't feel qualified to review the implementation - might want some
> > -fsdevel or -kernel eyes on it.
> > 
> > Limiting /selinux/policy to one user at a time might be reasonable as a
> > way of limiting memory consumption.  But you don't need the i_size hacks
> > as part of that.
> 
> My new mmap implementation (a whole lot easier to review even by an
> idiot like me) gives:
> 
> # checkpolicy -M -b /selinux/policy
> checkpolicy:  loading policy configuration from /selinux/policy
> libsepol.policydb_index_others: security:  9 users, 13 roles, 3341 types, 170 bools
> libsepol.policydb_index_others: security: 1 sens, 1024 cats
> libsepol.policydb_index_others: security:  77 classes, 195841 rules, 236706 cond rules
> checkpolicy:  policy configuration loaded
> 
> -----
> 
> commit 7e9368a5d130e6caa9b914cc60da7bc3791e04a5
> Author: Eric Paris <eparis@xxxxxxxxxx>
> Date:   Tue Jul 27 16:22:33 2010 -0400
> 
>     selinux: implement mmap on /selinux/policy
>     
>     /selinux/policy allows a user to copy the policy back out of the kernel.
>     This patch allows userspace to actually mmap that file and use it directly.
>     
>     Signed-off-by: Eric Paris <eparis@xxxxxxxxxx>
> 
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 8f64eec..6562ecd 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -68,6 +68,8 @@ static int *bool_pending_values;
>  static struct dentry *class_dir;
>  static unsigned long last_class_ino;
>  
> +static char policy_opened;
> +
>  /* global data for policy capabilities */
>  static struct dentry *policycap_dir;
>  
> @@ -315,11 +317,23 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
>  	if (rc)
>  		goto err;
>  
> +	if (policy_opened) {
> +		mutex_unlock(&sel_mutex);
> +		return -EBUSY;
> +	}
> +	policy_opened = 1;

I would take the policy_opened flag to the first patch to prevent
arbitrary kernel memory allocation just by opening /selinux/policy
repeatedly.  Note however my other comments on the first patch as well.
 
> +
>  	rc = -ENOMEM;
>  	plm = kzalloc(sizeof(*plm), GFP_KERNEL);
>  	if (!plm)
>  		goto err;
>  
> +	if (i_size_read(inode) != security_policydb_len()) {
> +		mutex_lock(&inode->i_mutex);
> +		i_size_write(inode, security_policydb_len());
> +		mutex_unlock(&inode->i_mutex);
> +	}

Do you really think this is necessary?  Does any other filesystem do
this?  I'd drop it otherwise.

> +
>  	rc = security_read_policy(&plm->data, &plm->len);
>  	if (rc)
>  		goto err;
> @@ -344,6 +358,8 @@ static int sel_release_policy(struct inode *inode, struct file *filp)
>  
>  	BUG_ON(!plm);
>  
> +	policy_opened = 0;
> +
>  	vfree(plm->data);
>  	kfree(plm);
>  
> @@ -380,9 +396,44 @@ out:
>  	return ret;
>  }
>  
> +static int sel_mmap_policy_fault(struct vm_area_struct *vma,
> +				 struct vm_fault *vmf)
> +{
> +	struct policy_load_memory *plm = vma->vm_file->private_data;
> +	unsigned long offset;
> +	struct page *page;
> +
> +	if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE))
> +		return VM_FAULT_SIGBUS;
> +
> +	offset = vmf->pgoff << PAGE_SHIFT;
> +	if (offset >= roundup(plm->len, PAGE_SIZE))
> +		return VM_FAULT_SIGBUS;
> +
> +	page = vmalloc_to_page(plm->data + offset);
> +	get_page(page);
> +
> +	vmf->page = page;
> +
> +	return 0;
> +}

This looks fine to me but I can't say that I've written one of these
handlers before.  Did you derive this from some example in an existing
filesystem?

> +
> +static struct vm_operations_struct sel_mmap_policy_ops = {
> +	.fault = sel_mmap_policy_fault,
> +};
> +
> +int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
> +{
> +	vma->vm_flags |= VM_RESERVED;
> +	vma->vm_ops = &sel_mmap_policy_ops;
> +
> +	return 0;
> +}
> +
>  static const struct file_operations sel_policy_ops = {
>  	.open		= sel_open_policy,
>  	.read		= sel_read_policy,
> +	.mmap		= sel_mmap_policy,
>  	.release	= sel_release_policy,
>  };
>  
> 
> 
> 
> --
> This message was distributed to subscribers of the selinux mailing list.
> If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
> the words "unsubscribe selinux" without quotes as the message.

-- 
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 majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.


[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux