Merge tag 'apparmor-pr-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "Another round of bug fixing and some code cleanups, there are no new
  features. The biggest thing to note is Georgia is being added to help
  co-maintain apparmor.

  Cleanups:
   - replace get_zeroed_page() with kzalloc()
   - remove unnecessary goto and associated label
   - change fn_label_build() to return err on failure instead of NULL or
     err
   - free rawdata as soon as possible
   - use explicit instead of implicit flex array in rawdata_f_data
   - use __label_make_stale in __aa_proxy_redirect
   - return correct error by propagate -ENOMEM correctly in unpack_table
   - aa_label_alloc use aa_label_free on alloc failure
   - add a conditional version of get_newest_label

  Bug Fixes:
   - mediate the implicit connect of TCP fast open sendmsg
   - fix C23ism of label immediately before a declaration
   - fix kernel-doc warnings
   - fix spelling mistakes
   - fix use-after-free in rawdata dedup loop
   - Fix inverted comparison in cache_hold_inc()
   - fix uninitialized pointer passed to audit_log_untrustedstring()
   - don't audit files pointing to aa_null.dentry
   - put secmark label after secid lookup
   - fix aa_getprocattr free procattr leak on format failure
   - release exe file resources on path failure
   - fail policy unpack on accept2 allocation failure
   - Fix return in ns_mkdir_op
   - remove or add symlinks to rawdata according to export_binary
   - fix NULL pointer dereference in unpack_pdb
   - fix potential UAF in aa_replace_profiles
   - grab ns lock and refresh when looking up changehat child profiles
   - enable differential encoding
   - check label build before no_new_privs test
   - conditionally compile get_loaddata_common_ref()
   - fix unix socket mediation cache update, and leak"

* tag 'apparmor-pr-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (35 commits)
  apparmor: advertise the tcp fast open fix is applied
  apparmor: mediate the implicit connect of TCP fast open sendmsg
  apparmor: fix label can not be immediately before a declaration
  apparmor: fix kernel-doc warnings
  apparmor: replace get_zeroed_page() with kzalloc()
  security: apparmor: fix two spelling mistakes
  apparmor: fix use-after-free in rawdata dedup loop
  apparmor: Fix inverted comparison in cache_hold_inc()
  apparmor: fix uninitialised pointer passed to audit_log_untrustedstring()
  apparmor: don't audit files pointing to aa_null.dentry
  apparmor: put secmark label after secid lookup
  apparmor: aa_getprocattr free procattr leak on format failure
  apparmor: remove unnecessary goto and associated label
  apparmor: release exe file resources on path failure
  apparmor: fail policy unpack on accept2 allocation failure
  apparmor: Fix return in ns_mkdir_op
  apparmor: remove or add symlinks to rawdata according to export_binary
  apparmor: fix NULL pointer dereference in unpack_pdb
  apparmor: make fn_label_build() capable of handling not supported
  apparmor: change fn_label_build() call to not return NULL
  ...
This commit is contained in:
Linus Torvalds
2026-06-24 12:33:40 -07:00
18 changed files with 365 additions and 150 deletions

View File

@@ -1987,6 +1987,7 @@ F: include/uapi/linux/apm_bios.h
APPARMOR SECURITY MODULE
M: John Johansen <john.johansen@canonical.com>
M: John Johansen <john@apparmor.net>
M: Georgia Garcia <georgia.garcia@canonical.com>
L: apparmor@lists.ubuntu.com (moderated for non-subscribers)
S: Supported
W: apparmor.net

View File

@@ -615,7 +615,7 @@ static int unix_peer_perm(const struct cred *subj_cred,
peer_label, &ad));
}
/**
/*
*
* Requires: lock held on both @sk and @peer_sk
* called by unix_stream_connect, unix_may_send
@@ -674,9 +674,11 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label,
old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock));
if (old == plabel) {
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
rcu_assign_pointer(ctx->peer_lastupdate,
aa_get_label(plabel));
} else if (aa_label_is_subset(plabel, old)) {
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
rcu_assign_pointer(ctx->peer_lastupdate,
aa_get_label(plabel));
rcu_assign_pointer(ctx->peer, aa_get_label(plabel));
aa_put_label(old);
} /* else race or a subset - don't update */
@@ -748,42 +750,47 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
if (!peer_sk)
goto out;
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
if (!is_sk_fs) {
bool is_peer_fs = is_unix_fs(peer_sk);
struct path peer_path;
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
if (is_peer_fs) {
struct path peer_path;
peer_path = unix_sk(peer_sk)->path;
if (!is_sk_fs && is_unix_fs(peer_sk)) {
last_error(error,
unix_fs_perm(op, request, subj_cred, label,
is_unix_fs(peer_sk) ? &peer_path : NULL));
} else if (!is_sk_fs) {
struct aa_label *plabel;
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
unix_state_lock(peer_sk);
peer_path = unix_sk(peer_sk)->path;
if (peer_path.dentry)
path_get(&peer_path);
unix_state_unlock(peer_sk);
rcu_read_lock();
plabel = aa_get_label_rcu(&pctx->label);
rcu_read_unlock();
/* no fs check of aa_unix_peer_perm because conditions above
* ensure they will never be done
*/
last_error(error,
xcheck(unix_peer_perm(subj_cred, label, op,
last_error(error,
unix_fs_perm(op, request, subj_cred, label,
&peer_path));
if (peer_path.dentry)
path_put(&peer_path);
} else {
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
rcu_read_lock();
plabel = aa_get_newest_label(pctx->label);
rcu_read_unlock();
/* no fs check of aa_unix_peer_perm because conditions
* above ensure they will never be done
*/
last_error(error,
xcheck(unix_peer_perm(subj_cred, label, op,
MAY_READ | MAY_WRITE, sock->sk,
is_sk_fs ? &path : NULL,
peer_addr, peer_addrlen,
is_unix_fs(peer_sk) ?
&peer_path : NULL,
plabel),
unix_peer_perm(file->f_cred, plabel, op,
NULL, plabel),
unix_peer_perm(file->f_cred, plabel, op,
MAY_READ | MAY_WRITE, peer_sk,
is_unix_fs(peer_sk) ?
&peer_path : NULL,
addr, addrlen,
NULL, addr, addrlen,
is_sk_fs ? &path : NULL,
label)));
if (!error && !__aa_subj_label_is_cached(plabel, label))
update_peer_ctx(peer_sk, pctx, label);
if (!error && !__aa_subj_label_is_cached(plabel, label))
update_peer_ctx(peer_sk, pctx, label);
}
}
sock_put(peer_sk);

View File

@@ -9,6 +9,7 @@
*/
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
@@ -71,10 +72,10 @@
struct rawdata_f_data {
struct aa_loaddata *loaddata;
DECLARE_FLEX_ARRAY(char, data);
};
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
static void rawdata_f_data_free(struct rawdata_f_data *private)
{
@@ -174,6 +175,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref)
return NULL;
}
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
{
if (ref)
@@ -181,6 +183,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
count));
return NULL;
}
#endif
static void aa_put_common_ref(struct aa_common_ref *ref)
{
@@ -904,7 +907,7 @@ static void multi_transaction_kref(struct kref *kref)
struct multi_transaction *t;
t = container_of(kref, struct multi_transaction, count);
free_page((unsigned long) t);
kfree(t);
}
static struct multi_transaction *
@@ -947,7 +950,7 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
if (size > MULTI_TRANSACTION_LIMIT - 1)
return ERR_PTR(-EFBIG);
t = (struct multi_transaction *)get_zeroed_page(GFP_KERNEL);
t = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!t)
return ERR_PTR(-ENOMEM);
kref_init(&t->count);
@@ -1434,7 +1437,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
struct rawdata_f_data *private = file->private_data;
return simple_read_from_buffer(buf, size, ppos,
RAWDATA_F_DATA_BUF(private),
private->data,
private->loaddata->size);
}
@@ -1467,8 +1470,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata;
error = decompress_zstd(loaddata->data, loaddata->compressed_size,
RAWDATA_F_DATA_BUF(private),
loaddata->size);
private->data, loaddata->size);
if (error)
goto fail_decompress;
@@ -1756,6 +1758,80 @@ static const struct inode_operations rawdata_link_abi_iops = {
static const struct inode_operations rawdata_link_data_iops = {
.get_link = rawdata_get_link_data,
};
/*
* Requires: @profile->ns->lock held
*/
void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
{
aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]);
profile->dents[AAFS_PROF_RAW_HASH] = NULL;
aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]);
profile->dents[AAFS_PROF_RAW_ABI] = NULL;
aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]);
profile->dents[AAFS_PROF_RAW_DATA] = NULL;
}
static inline int create_symlink_dent(struct aa_profile *profile,
const char *name,
enum aafs_prof_type type,
const struct inode_operations *iops)
{
struct dentry *dent = NULL;
struct dentry *dir = prof_dir(profile);
if (profile->dents[type])
return 0;
dent = aafs_create(name, S_IFLNK | 0444, dir,
&profile->label.proxy->count, NULL, NULL, iops);
if (IS_ERR(dent))
return PTR_ERR(dent);
profile->dents[type] = dent;
return 0;
}
/*
* Requires: @profile->ns->lock held
*/
int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
{
int error;
if (!profile ||
(profile->dents[AAFS_PROF_RAW_HASH] &&
profile->dents[AAFS_PROF_RAW_ABI] &&
profile->dents[AAFS_PROF_RAW_DATA]))
return 0;
if (!profile->rawdata)
return 0;
if (aa_g_hash_policy) {
error = create_symlink_dent(profile, "raw_sha256",
AAFS_PROF_RAW_HASH,
&rawdata_link_sha256_iops);
if (error)
return error;
}
error = create_symlink_dent(profile, "raw_abi",
AAFS_PROF_RAW_ABI,
&rawdata_link_abi_iops);
if (error)
return error;
error = create_symlink_dent(profile, "raw_data",
AAFS_PROF_RAW_DATA,
&rawdata_link_data_iops);
if (error)
return error;
return 0;
}
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
/*
@@ -1831,31 +1907,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile->dents[AAFS_PROF_HASH] = dent;
}
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
if (profile->rawdata) {
if (aa_g_hash_policy) {
dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir,
&profile->label.proxy->count, NULL,
NULL, &rawdata_link_sha256_iops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_HASH] = dent;
}
dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
&profile->label.proxy->count, NULL, NULL,
&rawdata_link_abi_iops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_ABI] = dent;
dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
&profile->label.proxy->count, NULL, NULL,
&rawdata_link_data_iops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_DATA] = dent;
}
#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
error = __aa_create_rawdata_symlink_dents(profile);
if (error)
goto fail2;
list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aafs_profile_mkdir(child, prof_child_dir(profile));
@@ -1922,7 +1976,7 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
mutex_unlock(&parent->lock);
aa_put_ns(parent);
return ERR_PTR(error);
return error ? ERR_PTR(error) : NULL;
}
static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
@@ -2422,6 +2476,7 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = {
static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_DIR("versions", aa_sfs_entry_versions),
AA_SFS_FILE_BOOLEAN("set_load", 1),
AA_SFS_FILE_BOOLEAN("diff-encode", 1),
/* number of out of band transitions supported */
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
AA_SFS_FILE_U64("permstable32_version", 3),

View File

@@ -12,6 +12,7 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/mutex.h>
#include <linux/syscalls.h>
#include <linux/personality.h>
#include <linux/xattr.h>
@@ -135,7 +136,7 @@ static int label_compound_match(struct aa_profile *profile,
struct label_it i;
struct path_cond cond = { };
/* find first subcomponent that is in view and going to be interated with */
/* find first subcomponent that is in view and going to be interacted with */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
@@ -863,6 +864,15 @@ static int profile_onexec(const struct cred *subj_cred,
}
/* ensure none ns domain transitions are correctly applied with onexec */
static struct aa_label *label_merge_wrap(struct aa_label *a, struct aa_label *b,
gfp_t gfp)
{
struct aa_label *label = aa_label_merge(a, b, gfp);
if (!label)
return ERR_PTR(-ENOMEM);
return label;
}
static struct aa_label *handle_onexec(const struct cred *subj_cred,
struct aa_label *label,
@@ -890,12 +900,13 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
return ERR_PTR(error);
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
stack ? aa_label_merge(&profile->label, onexec,
GFP_KERNEL)
stack ? label_merge_wrap(&profile->label, onexec,
GFP_KERNEL)
: aa_get_newest_label(onexec),
profile_transition(subj_cred, profile, bprm,
buffer, cond, unsafe));
if (new)
AA_BUG(!new);
if (!IS_ERR(new))
return new;
/* TODO: get rid of GLOBAL_ROOT_UID */
@@ -904,7 +915,8 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
OP_CHANGE_ONEXEC,
AA_MAY_ONEXEC, bprm->filename, NULL,
onexec, GLOBAL_ROOT_UID,
"failed to build target label", -ENOMEM));
"failed to build target label",
PTR_ERR(new)));
return ERR_PTR(error);
}
@@ -967,14 +979,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
profile_transition(subj_cred, profile, bprm,
buffer,
&cond, &unsafe));
AA_BUG(!new);
if (IS_ERR(new)) {
error = PTR_ERR(new);
goto done;
} else if (!new) {
error = -ENOMEM;
goto done;
}
/* Policy has specified a domain transitions. If no_new_privs and
@@ -1109,6 +1117,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
int count, int flags)
{
struct aa_profile *profile, *root, *hat = NULL;
struct aa_ns *ns, *new_ns;
struct aa_label *new;
struct label_it it;
bool sibling = false;
@@ -1119,6 +1128,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
AA_BUG(!hats);
AA_BUG(count < 1);
/*
* Acquire the newest label and then hold the lock until we choose a
* hat, so that profile replacement doesn't atomically truncate the
* list of potential hats. Because we are getting the namespaces from
* the profiles and label, we can rely on the namespaces being live
* and avoid incrementing their refcounts while grabbing the lock.
*/
label = aa_get_label(label);
ns = labels_ns(label);
retry:
mutex_lock_nested(&ns->lock, ns->level);
if (label_is_stale(label)) {
new = aa_get_newest_label(label);
new_ns = labels_ns(new);
if (new_ns != ns) {
aa_put_label(new);
mutex_unlock(&ns->lock);
ns = new_ns;
label = new;
goto retry;
}
aa_put_label(label);
label = new;
}
if (PROFILE_IS_HAT(labels_profile(label)))
sibling = true;
@@ -1127,7 +1162,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
name = hats[i];
label_for_each_in_scope(it, labels_ns(label), label, profile) {
if (sibling && PROFILE_IS_HAT(profile)) {
root = aa_get_profile_rcu(&profile->parent);
root = aa_get_profile(profile->parent);
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
root = aa_get_profile(profile);
} else { /* conflicting change type */
@@ -1187,6 +1222,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
GLOBAL_ROOT_UID, info, error);
}
}
mutex_unlock(&ns->lock);
return ERR_PTR(error);
build:
@@ -1194,11 +1230,9 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
build_change_hat(subj_cred, profile, name,
sibling),
aa_get_label(&profile->label));
if (!new) {
info = "label build failed";
error = -ENOMEM;
goto fail;
} /* else if (IS_ERR) build_change_hat has logged error so return new */
mutex_unlock(&ns->lock);
AA_BUG(!new);
/* return new label or error ptr */
return new;
}
@@ -1527,6 +1561,9 @@ int aa_change_profile(const char *fqname, int flags)
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
AA_BUG(!new);
if (IS_ERR(new))
goto build_fail;
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
@@ -1545,27 +1582,29 @@ int aa_change_profile(const char *fqname, int flags)
/* only transition profiles in the current ns */
if (stack)
new = aa_label_merge(label, target, GFP_KERNEL);
if (IS_ERR_OR_NULL(new)) {
info = "failed to build target label";
if (!new)
error = -ENOMEM;
else
error = PTR_ERR(new);
new = NULL;
perms.allow = 0;
goto audit;
}
if (IS_ERR_OR_NULL(new))
goto build_fail;
error = aa_replace_current_label(new);
} else {
if (new) {
aa_put_label(new);
new = NULL;
}
/* new will be recomputed so at exec time. So discard */
aa_put_label(new);
new = NULL;
/* full transition will be built in exec path */
aa_set_current_onexec(target, stack);
}
goto audit;
build_fail:
info = "failed to build target label";
if (!new)
error = -ENOMEM;
else
error = PTR_ERR(new);
new = NULL;
perms.allow = 0;
audit:
error = fn_for_each_in_scope(label, profile,
aa_audit_file(subj_cred,

View File

@@ -157,10 +157,10 @@ static int path_name(const char *op, const struct cred *subj_cred,
/* don't reaudit files closed during inheritance */
if (unlikely(path->dentry == aa_null.dentry))
error = -EACCES;
else
error = aa_path_name(path, flags, buffer, name, &info,
labels_profile(label)->disconnected);
return -EACCES;
error = aa_path_name(path, flags, buffer, name, &info,
labels_profile(label)->disconnected);
if (error) {
fn_for_each_confined(label, profile,
aa_audit_file(subj_cred,
@@ -250,7 +250,7 @@ static int profile_path_perm(const char *op, const struct cred *subj_cred,
struct path_cond *cond, int flags,
struct aa_perms *perms)
{
const char *name;
const char *name = NULL;
int error;
if (profile_unconfined(profile))
@@ -328,7 +328,7 @@ static int profile_path_link(const struct cred *subj_cred,
struct path_cond *cond)
{
struct aa_ruleset *rules = profile->label.rules[0];
const char *lname, *tname = NULL;
const char *lname = NULL, *tname = NULL;
struct aa_perms lperms = {}, perms;
const char *info = NULL;
u32 request = AA_MAY_LINK;

View File

@@ -120,6 +120,8 @@ struct aa_loaddata;
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile);
int __aa_create_rawdata_symlink_dents(struct aa_profile *profile);
#else
static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
{
@@ -131,6 +133,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns,
{
return 0;
}
static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
{
/* empty stub */
}
static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
{
return 0;
}
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
#endif /* __AA_APPARMORFS_H */

View 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)

View File

@@ -281,15 +281,15 @@ void aa_policy_destroy(struct aa_policy *policy);
* @FN: fn to call for each profile transition. @P is set to the profile
*
* Returns: new label on success
* NULL if all callbacks decline to specify a transition
* ERR_PTR if build @FN fails
* NULL if label_build fails due to low memory conditions
*
* @FN must return a label or ERR_PTR on failure. NULL is not allowed
* @FN must return a label or ERR_PTR on failure.
*/
#define fn_label_build(L, P, GFP, FN) \
({ \
__label__ __do_cleanup, __done; \
struct aa_label *__new_; \
struct aa_label *__new_ = NULL; \
\
if ((L)->size > 1) { \
/* TODO: add cache of transitions already done */ \
@@ -298,17 +298,21 @@ void aa_policy_destroy(struct aa_policy *policy);
DEFINE_VEC(label, __lvec); \
DEFINE_VEC(profile, __pvec); \
if (vec_setup(label, __lvec, (L)->size, (GFP))) { \
__new_ = NULL; \
__new_ = ERR_PTR(-ENOMEM); \
goto __done; \
} \
__j = 0; \
label_for_each(__i, (L), (P)) { \
__new_ = (FN); \
AA_BUG(!__new_); \
if (!__new_) \
continue; \
if (IS_ERR(__new_)) \
goto __do_cleanup; \
__lvec[__j++] = __new_; \
} \
if (__j == 0) \
/* no components adding to build */ \
goto __do_cleanup; \
for (__j = __count = 0; __j < (L)->size; __j++) \
__count += __lvec[__j]->size; \
if (!vec_setup(profile, __pvec, __count, (GFP))) { \
@@ -320,14 +324,13 @@ void aa_policy_destroy(struct aa_policy *policy);
if (__count > 1) { \
__new_ = aa_vec_find_or_create_label(__pvec,\
__count, (GFP)); \
/* only fails if out of Mem */ \
if (!__new_) \
__new_ = NULL; \
__new_ = ERR_PTR(-ENOMEM); \
} else \
__new_ = aa_get_label(&__pvec[0]->label); \
vec_cleanup(profile, __pvec, __count); \
} else \
__new_ = NULL; \
__new_ = ERR_PTR(-ENOMEM); \
__do_cleanup: \
vec_cleanup(label, __lvec, (L)->size); \
} else { \
@@ -335,7 +338,7 @@ __do_cleanup: \
__new_ = (FN); \
} \
__done: \
if (!__new_) \
if (PTR_ERR(__new_)) \
AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \
(__new_); \
})

View File

@@ -131,7 +131,7 @@ struct aa_loaddata {
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
/**
* aa_get_loaddata - get a reference count from a counted data reference
* aa_get_i_loaddata - get a reference count from a counted data reference
* @data: reference to get a count on
*
* Returns: pointer to reference
@@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data)
return data;
}
/**
* aa_get_profile_loaddata_not0 - get a profile reference count if not zero
* @data: reference to get a count on
*
* Like aa_get_profile_loaddata(), but safe to call on an entry that may
* be on a list (e.g. ns->rawdata_list) where the last pcount has already
* dropped and the deferred cleanup has not yet run.
*
* Returns: pointer to reference, or %NULL if @data is NULL or its
* profile refcount has already reached zero.
*/
static inline struct aa_loaddata *
aa_get_profile_loaddata_not0(struct aa_loaddata *data)
{
if (data && kref_get_unless_zero(&data->pcount))
return data;
return NULL;
}
void __aa_loaddata_update(struct aa_loaddata *data, long revision);
bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r);
void aa_loaddata_kref(struct kref *kref);

View File

@@ -83,7 +83,7 @@ void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
tmp = rcu_dereference_protected(orig->proxy->label,
&labels_ns(orig)->lock);
rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
orig->flags |= FLAG_STALE;
__label_make_stale(orig);
aa_put_label(tmp);
}
@@ -458,7 +458,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
return new;
fail:
kfree(new);
aa_label_free(new);
return NULL;
}
@@ -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;

View File

@@ -1422,7 +1422,21 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
static int apparmor_socket_sendmsg(struct socket *sock,
struct msghdr *msg, int size)
{
return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
if (error)
return error;
/* TCP fast open carries connect() semantics in sendmsg(); mediate
* the implicit connect so it cannot bypass the connect permission.
*/
if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name &&
(sk_is_tcp(sock->sk) ||
(sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM &&
sock->sk->sk_protocol == IPPROTO_MPTCP)))
error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
return error;
}
static int apparmor_socket_recvmsg(struct socket *sock,
@@ -1493,7 +1507,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
*
* Note: can not sleep may be called with locks held
*
* dont want protocol specific in __skb_recv_datagram()
* don't want protocol specific in __skb_recv_datagram()
* to deny an incoming connection socket_sock_rcv_skb()
*/
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
@@ -2129,7 +2143,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp)
*/
static void cache_hold_inc(unsigned int *hold)
{
if (*hold > MAX_HOLD_COUNT)
if (*hold < MAX_HOLD_COUNT)
(*hold)++;
}

View File

@@ -27,13 +27,13 @@
* @blob: data to unpack (NOT NULL)
* @bsize: size of blob
*
* Returns: pointer to table else NULL on failure
* Returns: pointer to table else ERR_PTR on failure
*
* NOTE: must be freed by kvfree (not kfree)
*/
static struct table_header *unpack_table(char *blob, size_t bsize)
{
struct table_header *table = NULL;
struct table_header *table = ERR_PTR(-EPROTO);
struct table_header th;
size_t tsize;
@@ -74,20 +74,21 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
else if (th.td_flags == YYTD_DATA32)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
u32, __be32, get_unaligned_be32);
else
goto fail;
else {
kvfree(table);
table = ERR_PTR(-EPROTO);
goto out;
}
/* if table was vmalloced make sure the page tables are synced
* before it is used, as it goes live to all cpus.
*/
if (is_vmalloc_addr(table))
vm_unmap_aliases();
}
} else
table = ERR_PTR(-ENOMEM);
out:
return table;
fail:
kvfree(table);
return NULL;
}
/**
@@ -359,8 +360,11 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
while (size > 0) {
table = unpack_table(data, size);
if (!table)
if (IS_ERR(table)) {
error = PTR_ERR(table);
table = NULL;
goto fail;
}
switch (table->td_id) {
case YYTD_ID_ACCEPT:

View File

@@ -735,17 +735,11 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
build_pivotroot(subj_cred, profile, new_path,
new_buffer,
old_path, old_buffer));
if (!target) {
info = "label build failed";
error = -ENOMEM;
goto fail;
} else if (!IS_ERR(target)) {
AA_BUG(!target);
if (!IS_ERR(target)) {
error = aa_replace_current_label(target);
if (error) {
/* TODO: audit target */
aa_put_label(target);
goto out;
}
if (error)
goto fail;
aa_put_label(target);
} else
/* already audited error */
@@ -763,7 +757,8 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
NULL /*new_name */,
NULL /* old_name */,
NULL, NULL,
0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
0, target->hname, AA_MAY_PIVOTROOT, &nullperms, info,
error));
aa_put_label(target);
goto out;
}

View File

@@ -22,12 +22,14 @@
struct aa_sfs_entry aa_sfs_entry_network[] = {
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
{ }
};
struct aa_sfs_entry aa_sfs_entry_networkv9[] = {
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
AA_SFS_FILE_BOOLEAN("af_unix", 1),
AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
{ }
};
@@ -354,6 +356,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark)
return PTR_ERR(label);
secmark->secid = label->secid;
aa_put_label(label);
return 0;
}

View File

@@ -232,6 +232,13 @@ static void __remove_profile(struct aa_profile *profile)
aa_label_remove(&profile->label);
__aafs_profile_rmdir(profile);
__list_remove_profile(profile);
/* rawdata is only ever referenced by fs lookup, that is no
* longer possible here, so put the reference to it. This will
* enable the rawdata to be freed if for some reason the profile
* is pinned and going to live for a while.
*/
aa_put_profile_loaddata(profile->rawdata);
profile->rawdata = NULL;
}
/**
@@ -1223,8 +1230,12 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
if (aa_rawdata_eq(rawdata_ent, udata)) {
struct aa_loaddata *tmp;
tmp = aa_get_profile_loaddata(rawdata_ent);
/* check we didn't fail the race */
/*
* Entries remain on rawdata_list with
* pcount == 0 until do_ploaddata_rmfs()
* runs; only take a live profile ref.
*/
tmp = aa_get_profile_loaddata_not0(rawdata_ent);
if (tmp) {
aa_put_profile_loaddata(udata);
udata = tmp;
@@ -1342,6 +1353,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
goto skip;
}
if (!aa_g_export_binary) {
if (ent->old && ent->old->rawdata &&
ent->old->dents[AAFS_LOADDATA_DIR]) {
/* remove rawdata symlinks because the symlink
* target will be removed
*/
__aa_remove_rawdata_symlink_dents(ent->old);
}
}
/*
* TODO: finer dedup based on profile range in data. Load set
* can differ but profile may remain unchanged
@@ -1352,6 +1373,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
if (ent->old) {
share_name(ent->old, ent->new);
__replace_profile(ent->old, ent->new);
if (aa_g_export_binary) {
/* recreate rawdata symlinks */
if (!ent->old->rawdata)
__aa_create_rawdata_symlink_dents(ent->new);
}
} else {
struct list_head *lh;
@@ -1372,12 +1398,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
out:
aa_put_ns(ns);
ssize_t udata_sz = udata->size;
aa_put_profile_loaddata(udata);
kfree(ns_name);
if (error)
return error;
return udata->size;
return udata_sz;
fail_lock:
mutex_unlock(&ns->lock);

View File

@@ -1045,7 +1045,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
}
/* accept2 is in some cases being allocated, even with perms */
if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
if (pdb->dfa && pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
/* add dfa flags table missing in v2 */
u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen;
u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags;
@@ -1054,7 +1054,8 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL);
if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
*info = "failed to alloc dfa flags table";
goto out;
error = -ENOMEM;
goto fail;
}
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents;
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags;
@@ -1079,7 +1080,6 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
* - move free of unneeded trans table here, has to be done
* after perm mapping.
*/
out:
*policy = pdb;
return 0;

View File

@@ -54,6 +54,8 @@ int aa_getprocattr(struct aa_label *label, char **string, bool newline)
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
FLAG_HIDDEN_UNCONFINED);
if (len < 0) {
kfree(*string);
*string = NULL;
aa_put_ns(current_ns);
return len;
}

View File

@@ -314,7 +314,7 @@ static const char *get_current_exe_path(char *buffer, int buffer_size)
path_get(&p);
if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
return ERR_PTR(-ENOMEM);
path_str = ERR_PTR(-ENOMEM);
fput(exe_file);
path_put(&p);