[PATCH] security_ops locking

From: James Morris (jmorrisat_private)
Date: Wed Jul 24 2002 - 09:23:48 PDT

  • Next message: Emily Ratliff: "Re: Why hooks in sys_iopl and sys_ioperm?"

    The patch below implements locking of for the security module registration 
    interfaces, covering writing & testing of the security_ops pointer.
    
    It is possible for races to exist when there are multiple callers of any 
    of register_security(),  unregister_security(), mod_reg_security() and 
    mod_unreg_security() during testing and manipulation of the security_ops 
    pointer.  For example, you could have two callers succeeding in a call to 
    register_security(), with only one succeeding in actually having its 
    security ops pointer recorded with the framework (the other would be 
    overwritten),
    
    Typically this will not be encountered, as module load/unload occurs under 
    locks, but it is possible for these interfaces to be called from other 
    contexts.  I'm not sure if this is too paranoid, but it should at least be 
    safe and correct under all conditions.
    
    David raised an issue regarding atomic pointer assignments -- if this is 
    indeed a problem on any arch, the new spinlock will impose appropriate 
    memory ordering for security_ops pointer assignments.
    
    Comments welcome.
    
    - James
    -- 
    James Morris
    <jmorrisat_private>
    
    diff -burN -X dontdiff lsm-2.5/security/security.c lsm-2.5.w2/security/security.c
    --- lsm-2.5/security/security.c	Tue Jul 23 11:24:17 2002
    +++ lsm-2.5.w2/security/security.c	Thu Jul 25 02:12:47 2002
    @@ -16,13 +16,18 @@
     #include <linux/init.h>
     #include <linux/kernel.h>
     #include <linux/sched.h>
    +#include <linux/spinlock.h>
     #include <linux/security.h>
     
     #define SECURITY_SCAFFOLD_VERSION	"1.0.0"
     
     extern struct security_operations dummy_security_ops;	/* lives in dummy.c */
     
    +/* The security_ops pointer should only be set or tested while security_ops_lock
    + * is held (except during scaffolding startup).
    + */
     struct security_operations *security_ops;	/* Initialized to NULL */
    +static spinlock_t security_ops_lock = SPIN_LOCK_UNLOCKED;
     
     /* This macro checks that all pointers in a struct are non-NULL.  It 
      * can be fooled by struct padding for object tile alignment and when
    @@ -106,13 +111,18 @@
      */
     int register_security (struct security_operations *ops)
     {
    +	unsigned long flags;
     
     	if (verify (ops)) {
     		printk (KERN_INFO __FUNCTION__ " could not verify "
     			"security_operations structure.\n");
     		return -EINVAL;
     	}
    +	
    +	spin_lock_irqsave (&security_ops_lock, flags);
    +	
     	if (security_ops != &dummy_security_ops) {
    +		spin_unlock_irqrestore (&security_ops_lock, flags);
     		printk (KERN_INFO "There is already a security "
     			"framework initialized, " __FUNCTION__ " failed.\n");
     		return -EINVAL;
    @@ -120,6 +130,8 @@
     
     	security_ops = ops;
     
    +	spin_unlock_irqrestore (&security_ops_lock, flags);
    +	
     	return 0;
     }
     
    @@ -136,7 +148,12 @@
      */
     int unregister_security (struct security_operations *ops)
     {
    +	unsigned long flags;
    +
    +	spin_lock_irqsave (&security_ops_lock, flags);
    +	
     	if (ops != security_ops) {
    +		spin_unlock_irqrestore (&security_ops_lock, flags);
     		printk (KERN_INFO __FUNCTION__ ": trying to unregister "
     			"a security_opts structure that is not "
     			"registered, failing.\n");
    @@ -145,6 +162,8 @@
     
     	security_ops = &dummy_security_ops;
     
    +	spin_unlock_irqrestore (&security_ops_lock, flags);
    +	
     	return 0;
     }
     
    @@ -162,18 +181,25 @@
      */
     int mod_reg_security (const char *name, struct security_operations *ops)
     {
    +        unsigned long flags;
    +        
     	if (verify (ops)) {
     		printk (KERN_INFO __FUNCTION__ " could not verify "
     			"security operations.\n");
     		return -EINVAL;
     	}
     
    +	spin_lock_irqsave (&security_ops_lock, flags);
    +	
     	if (ops == security_ops) {
    +		spin_unlock_irqrestore (&security_ops_lock, flags);
     		printk (KERN_INFO __FUNCTION__ " security operations "
     			"already registered.\n");
     		return -EINVAL;
     	}
     
    +	spin_unlock_irqrestore (&security_ops_lock, flags);
    +	
     	return security_ops->register_security (name, ops);
     }
     
    @@ -192,12 +218,19 @@
      */
     int mod_unreg_security (const char *name, struct security_operations *ops)
     {
    +	unsigned long flags;
    +	
    +	spin_lock_irqsave (&security_ops_lock, flags);
    +	
     	if (ops == security_ops) {
    +		spin_unlock_irqrestore (&security_ops_lock, flags);
     		printk (KERN_INFO __FUNCTION__ " invalid attempt to unregister "
     			" primary security ops.\n");
     		return -EINVAL;
     	}
     
    +	spin_unlock_irqrestore (&security_ops_lock, flags);
    +	
     	return security_ops->unregister_security (name, ops);
     }
     
    
    _______________________________________________
    linux-security-module mailing list
    linux-security-moduleat_private
    http://mail.wirex.com/mailman/listinfo/linux-security-module
    



    This archive was generated by hypermail 2b30 : Wed Jul 24 2002 - 09:24:44 PDT