AA_STACK_PROFILE(2)
π AA_STACK_PROFILE(2) β the AppArmor one where you stack profiles like pancakes π₯
okay bestie so this is from section 2 of the manual (the syscalls/libc one, iykyk) and it's about aa_stack_profile and aa_stack_onexec, the two functions that let you combine multiple profiles to confine a task. yes. stacking. like a stack of cute little security pancakes. periodt. π¦
β¨ NAME β¨
aa_stack_profile, aa_stack_onexec β combine multiple profiles to confine a task
π SYNOPSIS π
#include <sys/apparmor.h>
int aa_stack_profile(const char *profile);
int aa_stack_onexec(const char *profile);
Link with -lapparmor when compiling. don't forget it, she's shy and won't link herself π
πΈ DESCRIPTION πΈ
AppArmor supports stacking two or more profiles when confining a task. The result is an intersection of all the profiles which are stacked. not a union, not a vibe merge β an intersection, as in all of them have to agree. iconic behavior honestly π
Stacking profiles together is desirable when you want to ensure that confinement will never become more permissive. like, ever. and here's the tea: when you change between two profiles, as performed with aa_change_profile(2), there is always the possibility that the new profile is more permissive than the old profile. but that possibility is eliminated when you use aa_stack_profile(). no cap. π₯
To stack a profile with the current confinement context, a task can use the aa_stack_profile() function. The profile parameter is a NUL-terminated string indicating a profile name that should be stacked with the current confinement.
So, the receipts π:
- Calling
aa_stack_profile("profile_a")while unconfined is equivalent to callingaa_change_profile("profile_a")β because the intersection of unconfined and"profile_a"is"profile_a". math is mathing β¨ - Calling
aa_stack_profile("profile_b")while confined by"profile_a"results in the task's confinement being the intersection of"profile_a"and"profile_b". and the resulting confinement context will be represented as"profile_a//&profile_b"in audit log messages, the return value ofaa_getcon(2), etc. that//&is the little tell, the signature move π
Confined programs wanting to use aa_stack_profile() need to have rules permitting stacking the named profile. See apparmor.d(8) for details.
β οΈ okay important bestie: open file descriptors may not be remediated after a call toaa_stack_profile(), so the calling program mustclose(2)open file descriptors to ensure they are not available after callingaa_stack_profile(). like, the fd you opened before the stack? still open. still that old permissive you. close it. do it for me π
The aa_stack_onexec() function is like the aa_stack_profile() function except it specifies that the stacking should take place on the next exec instead of immediately. The delayed profile change takes precedence over any exec transition rules within the confining profile. and delaying the stacking boundary has a couple of advantages:
- it removes the need for stub transition profiles (so much boilerplate, gone π«Ά)
- the exec boundary is a natural security layer where potentially sensitive memory is unmapped π
π¦ RETURN VALUE π¦
On success zero is returned. On error, -1 is returned, and errno(3) is set appropriately. classic. π
π ERRORS π
EINVALβ AppArmor is not loaded, neither a profile nor a namespace was specified, or the communication via the/proc/*/attr/currentfile did not conform to protocol.ENOMEMβ Insufficient kernel memory was available.ENOENTβ The specified profile does not exist, or is not visible from the current namespace.
β NOTES β
Using aa_stack_profile() and related libapparmor functions are the only way to ensure compatibility between varying kernel versions. however (and this is the lowkey caveat), there may be some situations where libapparmor is not available and directly interacting with the AppArmor filesystem is required to stack a profile.
To immediately stack a profile named "profile_a", as performed with aa_stack_profile("profile_a"), the equivalent of this shell command can be used:
$ echo -n "stack profile_a" > /proc/self/attr/current
To stack a profile named "profile_a" at the next exec, as performed with aa_stack_onexec("profile_a"), the equivalent of this shell command can be used:
$ echo -n "stack profile_a" > /proc/self/attr/exec
π¨ These raw AppArmor filesystem operations must only be used when using libapparmor is not a viable option. like, it's the emergency backup outfit. don't make it your whole wardrobe π
π§ EXAMPLE π§
The following example shows a simple, if contrived, use of aa_stack_profile(). (contrived but cute, we love a pedagogical demo π)
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
static void read_passwd()
{
int fd;
char buf[10];
if ((fd=open("/etc/passwd", O_RDONLY)) < 0) {
perror("Failure opening /etc/passwd");
_exit(1);
}
/* Verify that we can read /etc/passwd */
memset(&buf, 0, 10);
if (read(fd, &buf, 10) == -1) {
perror("Failure reading /etc/passwd");
_exit(1);
}
buf[9] = '\0';
printf("/etc/passwd: %s\n", buf);
close(fd);
}
int main(int argc, char * argv[])
{
printf("Before aa_stack_profile():\n");
read_passwd();
/* stack the "i_cant_be_trusted_anymore" profile, which
* should not have read access to /etc/passwd. */
if (aa_stack_profile("i_cant_be_trusted_anymore") < 0) {
perror("Failure changing profile -- aborting");
_exit(1);
}
printf("After aa_stack_profile():\n");
read_passwd();
_exit(0);
}
This code example requires a profile similar to the following to be loaded with apparmor_parser(8):
# Confine stack_p to be able to read /etc/passwd and aa_stack_profile()
# to the 'i_cant_be_trusted_anymore' profile.
/tmp/stack_p {
/etc/ld.so.cache mr,
/lib/ld-*.so* mrix,
/lib/libc*.so* mr,
/etc/passwd r,
# Needed for aa_stack_profile()
change-profile -> &i_cant_be_trusted_anymore,
/usr/lib/libapparmor*.so* mr,
/proc/[0-9]*/attr/current w,
}
As well as the profile to stack:
profile i_cant_be_trusted_anymore {
/etc/ld.so.cache mr,
/lib/ld-*.so* mrix,
/lib/libc*.so* mr,
}
The output when run:
$ /tmp/stack_p
Before aa_stack_profile():
/etc/passwd: root:x:0:
After aa_stack_profile():
Failure opening /etc/passwd: Permission denied
$
look at that glow-up β from reading /etc/passwd to Permission denied in one function call. she ate and left absolutely zero crumbs ππ₯
π BUGS π
None known. If you find any, please report them at <https://gitlab.com/apparmor/apparmor/-/issues>.
π Note that usingaa_stack_profile(2)withoutexecve(2)provides no memory barriers between different areas of a program; if address space separation is required, then separate processes should be used. like, actually separate processes. not separate vibes. π«Ά
π SEE ALSO π
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2), aa_getcon(2) and <https://wiki.apparmor.net>.
okay that's the whole page bestie, go stack something responsibly πβ¨
this little masterpiece is brought to you by AppArmor 4.1.8, dated 2026-10-18 β mwah, stan the maintainers, and see you in the group chat ππ¦
π¦ where this page comes from
- arch rolling: apparmor 4.1.8-2 Β· GPL-2.0-only AND LGPL-2.0-only AND LGPL-2.1-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms