@ -469,7 +469,7 @@ static void __remove_profile(struct aa_profile *profile)
/* release any children lists first */
__profile_list_release ( & profile - > base . profiles ) ;
/* released by free_profile */
profile - > replacedby = aa_get_profile ( profile - > ns - > unconfined ) ;
__aa_update_replacedby ( profile , profile - > ns - > unconfined ) ;
__list_remove_profile ( profile ) ;
}
@ -588,6 +588,23 @@ void __init aa_free_root_ns(void)
aa_put_namespace ( ns ) ;
}
static void free_replacedby ( struct aa_replacedby * r )
{
if ( r ) {
aa_put_profile ( rcu_dereference ( r - > profile ) ) ;
kzfree ( r ) ;
}
}
void aa_free_replacedby_kref ( struct kref * kref )
{
struct aa_replacedby * r = container_of ( kref , struct aa_replacedby ,
count ) ;
free_replacedby ( r ) ;
}
/**
* free_profile - free a profile
* @ profile : the profile to free ( MAYBE NULL )
@ -600,8 +617,6 @@ void __init aa_free_root_ns(void)
*/
static void free_profile ( struct aa_profile * profile )
{
struct aa_profile * p ;
AA_DEBUG ( " %s(%p) \n " , __func__ , profile ) ;
if ( ! profile )
@ -620,28 +635,7 @@ static void free_profile(struct aa_profile *profile)
aa_put_dfa ( profile - > xmatch ) ;
aa_put_dfa ( profile - > policy . dfa ) ;
/* put the profile reference for replacedby, but not via
* put_profile ( kref_put ) .
* replacedby can form a long chain that can result in cascading
* frees that blows the stack because kref_put makes a nested fn
* call ( it looks like recursion , with free_profile calling
* free_profile ) for each profile in the chain lp # 1056078.
*/
for ( p = profile - > replacedby ; p ; ) {
if ( atomic_dec_and_test ( & p - > base . count . refcount ) ) {
/* no more refs on p, grab its replacedby */
struct aa_profile * next = p - > replacedby ;
/* break the chain */
p - > replacedby = NULL ;
/* now free p, chain is broken */
free_profile ( p ) ;
/* follow up with next profile in the chain */
p = next ;
} else
break ;
}
aa_put_replacedby ( profile - > replacedby ) ;
kzfree ( profile ) ;
}
@ -682,13 +676,22 @@ struct aa_profile *aa_alloc_profile(const char *hname)
if ( ! profile )
return NULL ;
if ( ! policy_init ( & profile - > base , NULL , hname ) ) {
kzfree ( profile ) ;
return NULL ;
}
profile - > replacedby = kzalloc ( sizeof ( struct aa_replacedby ) , GFP_KERNEL ) ;
if ( ! profile - > replacedby )
goto fail ;
kref_init ( & profile - > replacedby - > count ) ;
if ( ! policy_init ( & profile - > base , NULL , hname ) )
goto fail ;
/* refcount released by caller */
return profile ;
fail :
kzfree ( profile - > replacedby ) ;
kzfree ( profile ) ;
return NULL ;
}
/**
@ -985,6 +988,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
* __replace_profile - replace @ old with @ new on a list
* @ old : profile to be replaced ( NOT NULL )
* @ new : profile to replace @ old with ( NOT NULL )
* @ share_replacedby : transfer @ old - > replacedby to @ new
*
* Will duplicate and refcount elements that @ new inherits from @ old
* and will inherit @ old children .
@ -993,7 +997,8 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
*
* Requires : namespace list lock be held , or list not be shared
*/
static void __replace_profile ( struct aa_profile * old , struct aa_profile * new )
static void __replace_profile ( struct aa_profile * old , struct aa_profile * new ,
bool share_replacedby )
{
struct aa_profile * child , * tmp ;
@ -1008,7 +1013,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
p = __find_child ( & new - > base . profiles , child - > base . name ) ;
if ( p ) {
/* @p replaces @child */
__replace_profile ( child , p ) ;
__replace_profile ( child , p , share_replacedby ) ;
continue ;
}
@ -1027,8 +1032,11 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
struct aa_profile * parent = rcu_dereference ( old - > parent ) ;
rcu_assign_pointer ( new - > parent , aa_get_profile ( parent ) ) ;
}
/* released by free_profile */
old - > replacedby = aa_get_profile ( new ) ;
__aa_update_replacedby ( old , new ) ;
if ( share_replacedby ) {
aa_put_replacedby ( new - > replacedby ) ;
new - > replacedby = aa_get_replacedby ( old - > replacedby ) ;
}
if ( list_empty ( & new - > base . list ) ) {
/* new is not on a list already */
@ -1152,23 +1160,24 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
audit_policy ( op , GFP_ATOMIC , ent - > new - > base . name , NULL , error ) ;
if ( ent - > old ) {
__replace_profile ( ent - > old , ent - > new ) ;
__replace_profile ( ent - > old , ent - > new , 1 ) ;
if ( ent - > rename )
__replace_profile ( ent - > rename , ent - > new ) ;
__replace_profile ( ent - > rename , ent - > new , 0 ) ;
} else if ( ent - > rename ) {
__replace_profile ( ent - > rename , ent - > new ) ;
__replace_profile ( ent - > rename , ent - > new , 0 ) ;
} else if ( ent - > new - > parent ) {
struct aa_profile * parent , * newest ;
parent = rcu_dereference_protected ( ent - > new - > parent ,
mutex_is_locked ( & ns - > lock ) ) ;
newest = aa_newest_version ( parent ) ;
newest = aa_get_newest_profile ( parent ) ;
/* parent replaced in this atomic set? */
if ( newest ! = parent ) {
aa_get_profile ( newest ) ;
aa_put_profile ( parent ) ;
rcu_assign_pointer ( ent - > new - > parent , newest ) ;
}
} else
aa_put_profile ( newest ) ;
__list_add_profile ( & parent - > base . profiles , ent - > new ) ;
} else
__list_add_profile ( & ns - > base . profiles , ent - > new ) ;