aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2013-07-11 00:07:43 -0400
committerJohn Johansen <john.johansen@canonical.com>2013-08-14 14:42:06 -0400
commit77b071b34045a0c65d0e1f85f3d47fd2b8b7a8a1 (patch)
treeb0b2c38b79969ac7b9439389888f6a1ac14a32a7 /security
parent01e2b670aa898a39259bc85c78e3d74820f4d3b6 (diff)
apparmor: change how profile replacement update is done
remove the use of replaced by chaining and move to profile invalidation and lookup to handle task replacement. Replacement chaining can result in large chains of profiles being pinned in memory when one profile in the chain is use. With implicit labeling this will be even more of a problem, so move to a direct lookup method. Signed-off-by: John Johansen <john.johansen@canonical.com>
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/context.c16
-rw-r--r--security/apparmor/domain.c4
-rw-r--r--security/apparmor/include/context.h15
-rw-r--r--security/apparmor/include/policy.h78
-rw-r--r--security/apparmor/lsm.c14
-rw-r--r--security/apparmor/policy.c85
6 files changed, 125 insertions, 87 deletions
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index d5af1d15f26d..3064c6ced87c 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile)
112 aa_clear_task_cxt_trans(cxt); 112 aa_clear_task_cxt_trans(cxt);
113 113
114 /* be careful switching cxt->profile, when racing replacement it 114 /* be careful switching cxt->profile, when racing replacement it
115 * is possible that cxt->profile->replacedby is the reference keeping 115 * is possible that cxt->profile->replacedby->profile is the reference
116 * @profile valid, so make sure to get its reference before dropping 116 * keeping @profile valid, so make sure to get its reference before
117 * the reference on cxt->profile */ 117 * dropping the reference on cxt->profile */
118 aa_get_profile(profile); 118 aa_get_profile(profile);
119 aa_put_profile(cxt->profile); 119 aa_put_profile(cxt->profile);
120 cxt->profile = profile; 120 cxt->profile = profile;
@@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
175 abort_creds(new); 175 abort_creds(new);
176 return -EACCES; 176 return -EACCES;
177 } 177 }
178 cxt->profile = aa_get_profile(aa_newest_version(profile)); 178 cxt->profile = aa_get_newest_profile(profile);
179 /* clear exec on switching context */ 179 /* clear exec on switching context */
180 aa_put_profile(cxt->onexec); 180 aa_put_profile(cxt->onexec);
181 cxt->onexec = NULL; 181 cxt->onexec = NULL;
@@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token)
212 } 212 }
213 213
214 aa_put_profile(cxt->profile); 214 aa_put_profile(cxt->profile);
215 cxt->profile = aa_newest_version(cxt->previous); 215 cxt->profile = aa_get_newest_profile(cxt->previous);
216 BUG_ON(!cxt->profile); 216 BUG_ON(!cxt->profile);
217 if (unlikely(cxt->profile != cxt->previous)) {
218 aa_get_profile(cxt->profile);
219 aa_put_profile(cxt->previous);
220 }
221 /* ref has been transfered so avoid putting ref in clear_task_cxt */
222 cxt->previous = NULL;
223 /* clear exec && prev information when restoring to previous context */ 217 /* clear exec && prev information when restoring to previous context */
224 aa_clear_task_cxt_trans(cxt); 218 aa_clear_task_cxt_trans(cxt);
225 219
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 454bcd7f3452..5488d095af6f 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
359 cxt = cred_cxt(bprm->cred); 359 cxt = cred_cxt(bprm->cred);
360 BUG_ON(!cxt); 360 BUG_ON(!cxt);
361 361
362 profile = aa_get_profile(aa_newest_version(cxt->profile)); 362 profile = aa_get_newest_profile(cxt->profile);
363 /* 363 /*
364 * get the namespace from the replacement profile as replacement 364 * get the namespace from the replacement profile as replacement
365 * can change the namespace 365 * can change the namespace
@@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
417 417
418 if (!(cp.allow & AA_MAY_ONEXEC)) 418 if (!(cp.allow & AA_MAY_ONEXEC))
419 goto audit; 419 goto audit;
420 new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); 420 new_profile = aa_get_newest_profile(cxt->onexec);
421 goto apply; 421 goto apply;
422 } 422 }
423 423
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h
index d44ba5802e3d..6bf65798e5d1 100644
--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/context.h
@@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
98{ 98{
99 struct aa_task_cxt *cxt = cred_cxt(cred); 99 struct aa_task_cxt *cxt = cred_cxt(cred);
100 BUG_ON(!cxt || !cxt->profile); 100 BUG_ON(!cxt || !cxt->profile);
101 return aa_newest_version(cxt->profile); 101 return cxt->profile;
102} 102}
103 103
104/** 104/**
@@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void)
152 struct aa_profile *profile; 152 struct aa_profile *profile;
153 BUG_ON(!cxt || !cxt->profile); 153 BUG_ON(!cxt || !cxt->profile);
154 154
155 profile = aa_newest_version(cxt->profile); 155 if (PROFILE_INVALID(cxt->profile)) {
156 /* 156 profile = aa_get_newest_profile(cxt->profile);
157 * Whether or not replacement succeeds, use newest profile so
158 * there is no need to update it after replacement.
159 */
160 if (unlikely((cxt->profile != profile)))
161 aa_replace_current_profile(profile); 157 aa_replace_current_profile(profile);
158 aa_put_profile(profile);
159 cxt = current_cxt();
160 }
162 161
163 return profile; 162 return cxt->profile;
164} 163}
165 164
166/** 165/**
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 82487a853353..e9f2baf4467e 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -42,6 +42,8 @@ extern const char *const profile_mode_names[];
42 42
43#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) 43#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
44 44
45#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
46
45#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) 47#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
46 48
47/* 49/*
@@ -65,6 +67,7 @@ enum profile_flags {
65 PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ 67 PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
66 PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ 68 PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
67 PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ 69 PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */
70 PFLAG_INVALID = 0x200, /* profile replaced/removed */
68 71
69 /* These flags must correspond with PATH_flags */ 72 /* These flags must correspond with PATH_flags */
70 PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ 73 PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */
@@ -146,6 +149,12 @@ struct aa_policydb {
146 149
147}; 150};
148 151
152struct aa_replacedby {
153 struct kref count;
154 struct aa_profile __rcu *profile;
155};
156
157
149/* struct aa_profile - basic confinement data 158/* struct aa_profile - basic confinement data
150 * @base - base components of the profile (name, refcount, lists, lock ...) 159 * @base - base components of the profile (name, refcount, lists, lock ...)
151 * @parent: parent of profile 160 * @parent: parent of profile
@@ -169,8 +178,7 @@ struct aa_policydb {
169 * used to determine profile attachment against unconfined tasks. All other 178 * used to determine profile attachment against unconfined tasks. All other
170 * attachments are determined by profile X transition rules. 179 * attachments are determined by profile X transition rules.
171 * 180 *
172 * The @replacedby field is write protected by the profile lock. Reads 181 * The @replacedby struct is write protected by the profile lock.
173 * are assumed to be atomic.
174 * 182 *
175 * Profiles have a hierarchy where hats and children profiles keep 183 * Profiles have a hierarchy where hats and children profiles keep
176 * a reference to their parent. 184 * a reference to their parent.
@@ -184,14 +192,14 @@ struct aa_profile {
184 struct aa_profile __rcu *parent; 192 struct aa_profile __rcu *parent;
185 193
186 struct aa_namespace *ns; 194 struct aa_namespace *ns;
187 struct aa_profile *replacedby; 195 struct aa_replacedby *replacedby;
188 const char *rename; 196 const char *rename;
189 197
190 struct aa_dfa *xmatch; 198 struct aa_dfa *xmatch;
191 int xmatch_len; 199 int xmatch_len;
192 enum audit_mode audit; 200 enum audit_mode audit;
193 enum profile_mode mode; 201 enum profile_mode mode;
194 u32 flags; 202 long flags;
195 u32 path_flags; 203 u32 path_flags;
196 int size; 204 int size;
197 205
@@ -250,6 +258,7 @@ static inline void aa_put_namespace(struct aa_namespace *ns)
250 kref_put(&ns->base.count, aa_free_namespace_kref); 258 kref_put(&ns->base.count, aa_free_namespace_kref);
251} 259}
252 260
261void aa_free_replacedby_kref(struct kref *kref);
253struct aa_profile *aa_alloc_profile(const char *name); 262struct aa_profile *aa_alloc_profile(const char *name);
254struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); 263struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
255void aa_free_profile_kref(struct kref *kref); 264void aa_free_profile_kref(struct kref *kref);
@@ -265,24 +274,6 @@ ssize_t aa_remove_profiles(char *name, size_t size);
265 274
266#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) 275#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED)
267 276
268/**
269 * aa_newest_version - find the newest version of @profile
270 * @profile: the profile to check for newer versions of (NOT NULL)
271 *
272 * Returns: newest version of @profile, if @profile is the newest version
273 * return @profile.
274 *
275 * NOTE: the profile returned is not refcounted, The refcount on @profile
276 * must be held until the caller decides what to do with the returned newest
277 * version.
278 */
279static inline struct aa_profile *aa_newest_version(struct aa_profile *profile)
280{
281 while (profile->replacedby)
282 profile = profile->replacedby;
283
284 return profile;
285}
286 277
287/** 278/**
288 * aa_get_profile - increment refcount on profile @p 279 * aa_get_profile - increment refcount on profile @p
@@ -335,6 +326,25 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
335} 326}
336 327
337/** 328/**
329 * aa_get_newest_profile - find the newest version of @profile
330 * @profile: the profile to check for newer versions of
331 *
332 * Returns: refcounted newest version of @profile taking into account
333 * replacement, renames and removals
334 * return @profile.
335 */
336static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
337{
338 if (!p)
339 return NULL;
340
341 if (PROFILE_INVALID(p))
342 return aa_get_profile_rcu(&p->replacedby->profile);
343
344 return aa_get_profile(p);
345}
346
347/**
338 * aa_put_profile - decrement refcount on profile @p 348 * aa_put_profile - decrement refcount on profile @p
339 * @p: profile (MAYBE NULL) 349 * @p: profile (MAYBE NULL)
340 */ 350 */
@@ -344,6 +354,30 @@ static inline void aa_put_profile(struct aa_profile *p)
344 kref_put(&p->base.count, aa_free_profile_kref); 354 kref_put(&p->base.count, aa_free_profile_kref);
345} 355}
346 356
357static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
358{
359 if (p)
360 kref_get(&(p->count));
361
362 return p;
363}
364
365static inline void aa_put_replacedby(struct aa_replacedby *p)
366{
367 if (p)
368 kref_put(&p->count, aa_free_replacedby_kref);
369}
370
371/* requires profile list write lock held */
372static inline void __aa_update_replacedby(struct aa_profile *orig,
373 struct aa_profile *new)
374{
375 struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile);
376 rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
377 orig->flags |= PFLAG_INVALID;
378 aa_put_profile(tmp);
379}
380
347static inline int AUDIT_MODE(struct aa_profile *profile) 381static inline int AUDIT_MODE(struct aa_profile *profile)
348{ 382{
349 if (aa_g_audit != AUDIT_NORMAL) 383 if (aa_g_audit != AUDIT_NORMAL)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 96506dfe51ec..c8c148a738f7 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
508 /* released below */ 508 /* released below */
509 const struct cred *cred = get_task_cred(task); 509 const struct cred *cred = get_task_cred(task);
510 struct aa_task_cxt *cxt = cred_cxt(cred); 510 struct aa_task_cxt *cxt = cred_cxt(cred);
511 struct aa_profile *profile = NULL;
511 512
512 if (strcmp(name, "current") == 0) 513 if (strcmp(name, "current") == 0)
513 error = aa_getprocattr(aa_newest_version(cxt->profile), 514 profile = aa_get_newest_profile(cxt->profile);
514 value);
515 else if (strcmp(name, "prev") == 0 && cxt->previous) 515 else if (strcmp(name, "prev") == 0 && cxt->previous)
516 error = aa_getprocattr(aa_newest_version(cxt->previous), 516 profile = aa_get_newest_profile(cxt->previous);
517 value);
518 else if (strcmp(name, "exec") == 0 && cxt->onexec) 517 else if (strcmp(name, "exec") == 0 && cxt->onexec)
519 error = aa_getprocattr(aa_newest_version(cxt->onexec), 518 profile = aa_get_newest_profile(cxt->onexec);
520 value);
521 else 519 else
522 error = -EINVAL; 520 error = -EINVAL;
523 521
522 if (profile)
523 error = aa_getprocattr(profile, value);
524
525 aa_put_profile(profile);
524 put_cred(cred); 526 put_cred(cred);
525 527
526 return error; 528 return error;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 25bbbb482bb6..41b8f275c626 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -469,7 +469,7 @@ static void __remove_profile(struct aa_profile *profile)
469 /* release any children lists first */ 469 /* release any children lists first */
470 __profile_list_release(&profile->base.profiles); 470 __profile_list_release(&profile->base.profiles);
471 /* released by free_profile */ 471 /* released by free_profile */
472 profile->replacedby = aa_get_profile(profile->ns->unconfined); 472 __aa_update_replacedby(profile, profile->ns->unconfined);
473 __list_remove_profile(profile); 473 __list_remove_profile(profile);
474} 474}
475 475
@@ -588,6 +588,23 @@ void __init aa_free_root_ns(void)
588 aa_put_namespace(ns); 588 aa_put_namespace(ns);
589} 589}
590 590
591
592static void free_replacedby(struct aa_replacedby *r)
593{
594 if (r) {
595 aa_put_profile(rcu_dereference(r->profile));
596 kzfree(r);
597 }
598}
599
600
601void aa_free_replacedby_kref(struct kref *kref)
602{
603 struct aa_replacedby *r = container_of(kref, struct aa_replacedby,
604 count);
605 free_replacedby(r);
606}
607
591/** 608/**
592 * free_profile - free a profile 609 * free_profile - free a profile
593 * @profile: the profile to free (MAYBE NULL) 610 * @profile: the profile to free (MAYBE NULL)
@@ -600,8 +617,6 @@ void __init aa_free_root_ns(void)
600 */ 617 */
601static void free_profile(struct aa_profile *profile) 618static void free_profile(struct aa_profile *profile)
602{ 619{
603 struct aa_profile *p;
604
605 AA_DEBUG("%s(%p)\n", __func__, profile); 620 AA_DEBUG("%s(%p)\n", __func__, profile);
606 621
607 if (!profile) 622 if (!profile)
@@ -620,28 +635,7 @@ static void free_profile(struct aa_profile *profile)
620 635
621 aa_put_dfa(profile->xmatch); 636 aa_put_dfa(profile->xmatch);
622 aa_put_dfa(profile->policy.dfa); 637 aa_put_dfa(profile->policy.dfa);
623 638 aa_put_replacedby(profile->replacedby);
624 /* put the profile reference for replacedby, but not via
625 * put_profile(kref_put).
626 * replacedby can form a long chain that can result in cascading
627 * frees that blows the stack because kref_put makes a nested fn
628 * call (it looks like recursion, with free_profile calling
629 * free_profile) for each profile in the chain lp#1056078.
630 */
631 for (p = profile->replacedby; p; ) {
632 if (atomic_dec_and_test(&p->base.count.refcount)) {
633 /* no more refs on p, grab its replacedby */
634 struct aa_profile *next = p->replacedby;
635 /* break the chain */
636 p->replacedby = NULL;
637 /* now free p, chain is broken */
638 free_profile(p);
639
640 /* follow up with next profile in the chain */
641 p = next;
642 } else
643 break;
644 }
645 639
646 kzfree(profile); 640 kzfree(profile);
647} 641}
@@ -682,13 +676,22 @@ struct aa_profile *aa_alloc_profile(const char *hname)
682 if (!profile) 676 if (!profile)
683 return NULL; 677 return NULL;
684 678
685 if (!policy_init(&profile->base, NULL, hname)) { 679 profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL);
686 kzfree(profile); 680 if (!profile->replacedby)
687 return NULL; 681 goto fail;
688 } 682 kref_init(&profile->replacedby->count);
683
684 if (!policy_init(&profile->base, NULL, hname))
685 goto fail;
689 686
690 /* refcount released by caller */ 687 /* refcount released by caller */
691 return profile; 688 return profile;
689
690fail:
691 kzfree(profile->replacedby);
692 kzfree(profile);
693
694 return NULL;
692} 695}
693 696
694/** 697/**
@@ -985,6 +988,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
985 * __replace_profile - replace @old with @new on a list 988 * __replace_profile - replace @old with @new on a list
986 * @old: profile to be replaced (NOT NULL) 989 * @old: profile to be replaced (NOT NULL)
987 * @new: profile to replace @old with (NOT NULL) 990 * @new: profile to replace @old with (NOT NULL)
991 * @share_replacedby: transfer @old->replacedby to @new
988 * 992 *
989 * Will duplicate and refcount elements that @new inherits from @old 993 * Will duplicate and refcount elements that @new inherits from @old
990 * and will inherit @old children. 994 * and will inherit @old children.
@@ -993,7 +997,8 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh,
993 * 997 *
994 * Requires: namespace list lock be held, or list not be shared 998 * Requires: namespace list lock be held, or list not be shared
995 */ 999 */
996static void __replace_profile(struct aa_profile *old, struct aa_profile *new) 1000static void __replace_profile(struct aa_profile *old, struct aa_profile *new,
1001 bool share_replacedby)
997{ 1002{
998 struct aa_profile *child, *tmp; 1003 struct aa_profile *child, *tmp;
999 1004
@@ -1008,7 +1013,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
1008 p = __find_child(&new->base.profiles, child->base.name); 1013 p = __find_child(&new->base.profiles, child->base.name);
1009 if (p) { 1014 if (p) {
1010 /* @p replaces @child */ 1015 /* @p replaces @child */
1011 __replace_profile(child, p); 1016 __replace_profile(child, p, share_replacedby);
1012 continue; 1017 continue;
1013 } 1018 }
1014 1019
@@ -1027,8 +1032,11 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
1027 struct aa_profile *parent = rcu_dereference(old->parent); 1032 struct aa_profile *parent = rcu_dereference(old->parent);
1028 rcu_assign_pointer(new->parent, aa_get_profile(parent)); 1033 rcu_assign_pointer(new->parent, aa_get_profile(parent));
1029 } 1034 }
1030 /* released by free_profile */ 1035 __aa_update_replacedby(old, new);
1031 old->replacedby = aa_get_profile(new); 1036 if (share_replacedby) {
1037 aa_put_replacedby(new->replacedby);
1038 new->replacedby = aa_get_replacedby(old->replacedby);
1039 }
1032 1040
1033 if (list_empty(&new->base.list)) { 1041 if (list_empty(&new->base.list)) {
1034 /* new is not on a list already */ 1042 /* new is not on a list already */
@@ -1152,23 +1160,24 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
1152 audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); 1160 audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error);
1153 1161
1154 if (ent->old) { 1162 if (ent->old) {
1155 __replace_profile(ent->old, ent->new); 1163 __replace_profile(ent->old, ent->new, 1);
1156 if (ent->rename) 1164 if (ent->rename)
1157 __replace_profile(ent->rename, ent->new); 1165 __replace_profile(ent->rename, ent->new, 0);
1158 } else if (ent->rename) { 1166 } else if (ent->rename) {
1159 __replace_profile(ent->rename, ent->new); 1167 __replace_profile(ent->rename, ent->new, 0);
1160 } else if (ent->new->parent) { 1168 } else if (ent->new->parent) {
1161 struct aa_profile *parent, *newest; 1169 struct aa_profile *parent, *newest;
1162 parent = rcu_dereference_protected(ent->new->parent, 1170 parent = rcu_dereference_protected(ent->new->parent,
1163 mutex_is_locked(&ns->lock)); 1171 mutex_is_locked(&ns->lock));
1164 newest = aa_newest_version(parent); 1172 newest = aa_get_newest_profile(parent);
1165 1173
1166 /* parent replaced in this atomic set? */ 1174 /* parent replaced in this atomic set? */
1167 if (newest != parent) { 1175 if (newest != parent) {
1168 aa_get_profile(newest); 1176 aa_get_profile(newest);
1169 aa_put_profile(parent); 1177 aa_put_profile(parent);
1170 rcu_assign_pointer(ent->new->parent, newest); 1178 rcu_assign_pointer(ent->new->parent, newest);
1171 } 1179 } else
1180 aa_put_profile(newest);
1172 __list_add_profile(&parent->base.profiles, ent->new); 1181 __list_add_profile(&parent->base.profiles, ent->new);
1173 } else 1182 } else
1174 __list_add_profile(&ns->base.profiles, ent->new); 1183 __list_add_profile(&ns->base.profiles, ent->new);