]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
apparmor: add a conditional version of get_newest_label
authorJohn Johansen <john.johansen@canonical.com>
Fri, 17 Apr 2026 07:51:22 +0000 (00:51 -0700)
committerJohn Johansen <john.johansen@canonical.com>
Sun, 14 Jun 2026 03:14:06 +0000 (20:14 -0700)
get_newest_label() will always return a refcount, on the profile it
returns. However there are cases where we only need the refcount
if the label is stale and get_newest_label() will return a different
label.

Optimize this by making the get/put happen conditionally, by keeping
a flag indicating if the get was performed and a put is needed.

Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/include/label.h
security/apparmor/label.c

index 335f21930702aabdfaf56c0358fd22ed0d971a0a..b5a722a47fd2c864d03a227e4ab14c6ac4f467b0 100644 (file)
@@ -423,6 +423,38 @@ static inline struct aa_label *aa_get_newest_label(struct aa_label *l)
        return aa_get_label(l);
 }
 
+/**
+ * aa_get_newest_label_condref - find the newest version of @l
+ * @l: the label to check for newer versions of
+ * @needput: returns whether the reference needs put
+ *
+ * Returns: refcounted newest version of @l taking into account
+ *          replacement, renames and removals
+ *          return @l.
+ */
+static inline struct aa_label *aa_get_newest_label_condref(struct aa_label *l,
+                                                          bool *needput)
+{
+       if (l && unlikely(label_is_stale(l))) {
+               struct aa_label *tmp;
+
+               AA_BUG(!l->proxy);
+               AA_BUG(!l->proxy->label);
+               /* BUG: only way this can happen is @l ref count and its
+                * replacement count have gone to 0 and are on their way
+                * to destruction. ie. we have a refcounting error
+                */
+               tmp = aa_get_label_rcu(&l->proxy->label);
+               AA_BUG(!tmp);
+
+               *needput = true;
+               return tmp;
+       }
+
+       *needput = false;
+       return l;
+}
+
 static inline void aa_put_label(struct aa_label *l)
 {
        if (l)
index 3a721fdf18339fdcbc9861a22de8a50e60f87af4..a8850d118c9ccde413ec371dd0d86977e443924b 100644 (file)
@@ -1176,22 +1176,21 @@ static struct aa_label *__label_find_merge(struct aa_labelset *ls,
 struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
 {
        struct aa_labelset *ls;
-       struct aa_label *label, *ar = NULL, *br = NULL;
+       struct aa_label *label;
        unsigned long flags;
+       bool a_needput, b_needput;
 
        AA_BUG(!a);
        AA_BUG(!b);
 
-       if (label_is_stale(a))
-               a = ar = aa_get_newest_label(a);
-       if (label_is_stale(b))
-               b = br = aa_get_newest_label(b);
+       a = aa_get_newest_label_condref(a, &a_needput);
+       b = aa_get_newest_label_condref(b, &b_needput);
        ls = labelset_of_merge(a, b);
        read_lock_irqsave(&ls->lock, flags);
        label = __label_find_merge(ls, a, b);
        read_unlock_irqrestore(&ls->lock, flags);
-       aa_put_label(ar);
-       aa_put_label(br);
+       aa_put_label_condref(a, a_needput);
+       aa_put_label_condref(b, b_needput);
 
        return label;
 }
@@ -1228,9 +1227,10 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
 
        if (!label) {
                struct aa_label *new;
+               bool a_needput, b_needput;
 
-               a = aa_get_newest_label(a);
-               b = aa_get_newest_label(b);
+               a = aa_get_newest_label_condref(a, &a_needput);
+               b = aa_get_newest_label_condref(b, &b_needput);
 
                /* could use label_merge_len(a, b), but requires double
                 * comparison for small savings
@@ -1242,8 +1242,8 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
                label = label_merge_insert(new, a, b);
                label_free_or_put_new(label, new);
 out:
-               aa_put_label(a);
-               aa_put_label(b);
+               aa_put_label_condref(a, a_needput);
+               aa_put_label_condref(b, b_needput);
        }
 
        return label;