@@ -423,6 +423,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
423423 return & profile -> base ;
424424}
425425
426+ /**
427+ * __create_missing_ancestors - create place holders for missing ancestores
428+ * @ns: namespace to lookup profile in (NOT NULL)
429+ * @hname: hierarchical profile name to find parent of (NOT NULL)
430+ * @gfp: type of allocation.
431+ *
432+ * Returns: NULL on error, parent profile on success
433+ *
434+ * Requires: ns mutex lock held
435+ *
436+ * Returns: unrefcounted parent policy or NULL if error creating
437+ * place holder profiles.
438+ */
439+ static struct aa_policy * __create_missing_ancestors (struct aa_ns * ns ,
440+ const char * hname ,
441+ gfp_t gfp )
442+ {
443+ struct aa_policy * policy ;
444+ struct aa_profile * parent , * profile = NULL ;
445+ char * split ;
446+
447+ AA_BUG (!ns );
448+ AA_BUG (!hname );
449+
450+ policy = & ns -> base ;
451+
452+ for (split = strstr (hname , "//" ); split ;) {
453+ parent = profile ;
454+ profile = __strn_find_child (& policy -> profiles , hname ,
455+ split - hname );
456+ if (!profile ) {
457+ const char * name = kstrndup (hname , split - hname ,
458+ gfp );
459+ if (!name )
460+ return NULL ;
461+ profile = aa_alloc_null (parent , name , gfp );
462+ kfree (name );
463+ if (!profile )
464+ return NULL ;
465+ if (!parent )
466+ profile -> ns = aa_get_ns (ns );
467+ }
468+ policy = & profile -> base ;
469+ hname = split + 2 ;
470+ split = strstr (hname , "//" );
471+ }
472+ if (!profile )
473+ return & ns -> base ;
474+ return & profile -> base ;
475+ }
476+
426477/**
427478 * __lookupn_profile - lookup the profile matching @hname
428479 * @base: base list to start looking up profile name from (NOT NULL)
@@ -1032,6 +1083,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
10321083 /* setup parent and ns info */
10331084 list_for_each_entry (ent , & lh , list ) {
10341085 struct aa_policy * policy ;
1086+ struct aa_profile * p ;
10351087
10361088 if (aa_g_export_binary )
10371089 ent -> new -> rawdata = aa_get_loaddata (udata );
@@ -1056,21 +1108,38 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
10561108 continue ;
10571109
10581110 /* no ref on policy only use inside lock */
1111+ p = NULL ;
10591112 policy = __lookup_parent (ns , ent -> new -> base .hname );
10601113 if (!policy ) {
1061- struct aa_profile * p ;
1114+ /* first check for parent in the load set */
10621115 p = __list_lookup_parent (& lh , ent -> new );
10631116 if (!p ) {
1064- error = - ENOENT ;
1065- info = "parent does not exist" ;
1066- goto fail_lock ;
1117+ /*
1118+ * fill in missing parent with null
1119+ * profile that doesn't have
1120+ * permissions. This allows for
1121+ * individual profile loading where
1122+ * the child is loaded before the
1123+ * parent, and outside of the current
1124+ * atomic set. This unfortunately can
1125+ * happen with some userspaces. The
1126+ * null profile will be replaced once
1127+ * the parent is loaded.
1128+ */
1129+ policy = __create_missing_ancestors (ns ,
1130+ ent -> new -> base .hname ,
1131+ GFP_KERNEL );
1132+ if (!policy ) {
1133+ error = - ENOENT ;
1134+ info = "parent does not exist" ;
1135+ goto fail_lock ;
1136+ }
10671137 }
1068- rcu_assign_pointer (ent -> new -> parent , aa_get_profile (p ));
1069- } else if (policy != & ns -> base ) {
1070- /* released on profile replacement or free_profile */
1071- struct aa_profile * p = (struct aa_profile * ) policy ;
1072- rcu_assign_pointer (ent -> new -> parent , aa_get_profile (p ));
10731138 }
1139+ if (!p && policy != & ns -> base )
1140+ /* released on profile replacement or free_profile */
1141+ p = (struct aa_profile * ) policy ;
1142+ rcu_assign_pointer (ent -> new -> parent , aa_get_profile (p ));
10741143 }
10751144
10761145 /* create new fs entries for introspection if needed */
0 commit comments