From dda61023f976d7ab3bc7c8b46d26d6d23424f890 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 May 2026 18:20:27 -0700 Subject: [PATCH] apparmor: fix alternate loaders ability to load compressed policy When policy is compressed it is given a header in userspace. The parser knows to strip this header, but alternate profile loaders like aa-load and systemd have not been updated so that they know to strip the header. This allows those loaders to just load the binary file without having to be updated. Fixes: 17b5758bf35c7 ("apparmor: Initial support for compressed policies") Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 514ec34975a4..2ae9ab94a5a9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -546,6 +547,15 @@ static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbu } +struct aa_user_hdr { + uint8_t version; + uint8_t compress_level; + uint8_t padding[6]; /* force 8-byte alignment */ +} __packed __aligned(8); + +#define aa_hdr_magic "\x04\x08\x00\x76\x65\x72\x73\x69\x6f\x6e\x00\x02" +#define aa_hdr_magic_size 12 + static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, loff_t *pos, struct aa_ns *ns, const struct cred *ocred) @@ -556,6 +566,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, char *compressed_data = NULL; __le32 magic_le; bool is_compressed; + u8 aahdr[aa_hdr_magic_size]; label = begin_current_label_crit_section(); @@ -576,11 +587,30 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, if (size >= sizeof(__le32) && !copy_from_user(&magic_le, buf, sizeof(magic_le)) && - le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) + le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) { is_compressed = true; - else + } else if (size >= sizeof(struct aa_user_hdr) + sizeof(__le32) && + !copy_from_user(&magic_le, + buf + sizeof(struct aa_user_hdr), + sizeof(magic_le)) && + le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) { + is_compressed = true; + /* skip the userspace header if present */ + buf += sizeof(struct aa_user_hdr); + size -= sizeof(struct aa_user_hdr); + } else if (size >= sizeof(struct aa_user_hdr) + + aa_hdr_magic_size && + !copy_from_user(&aahdr, + buf + sizeof(struct aa_user_hdr), + aa_hdr_magic_size) && + memcmp(&aahdr, aa_hdr_magic, aa_hdr_magic_size) == 0) { + /* uncompressed blob with user hdr */ + buf += sizeof(struct aa_user_hdr); + size -= sizeof(struct aa_user_hdr); is_compressed = false; - + } else { + is_compressed = false; + } if (is_compressed) { data = aa_get_data_from_compressed(buf, size, pos, &compressed_data); @@ -2575,6 +2605,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { AA_SFS_FILE_U64("state32", 1), AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined), AA_SFS_FILE_BOOLEAN("compressed_load", 1), + AA_SFS_FILE_BOOLEAN("extended_policy_header", 1), { } };