apparmor reference leak causes refcount_t overflow with af_alg_accept()
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
linux (Ubuntu) |
Fix Released
|
Medium
|
Mauricio Faria de Oliveira | ||
Bionic |
Fix Released
|
Medium
|
Mauricio Faria de Oliveira | ||
Eoan |
Fix Released
|
Medium
|
Mauricio Faria de Oliveira | ||
Focal |
Fix Released
|
Medium
|
Mauricio Faria de Oliveira | ||
Groovy |
Invalid
|
Undecided
|
Unassigned |
Bug Description
[Impact]
* Users of the Crypto (user-space) API (i.e., AF_ALG)
can trigger refcount errors in AppArmor under high
load (might lead to memory leak or use after free.)
* There is a reference leak in AppArmor when af_alg_accept()
calls security_
* Both acquire a reference to a label, to assign it to the
same pointer, but the latter does not release the former's
acquired reference (before overwriting the pointer value.)
* This reference leak builds up over time, and under high
load can eventually overflow/
depending on which value it has when a program hits that.
* The fix just checks if the pointer has an assigned label,
then releases its acquired reference.
[Test Case]
* See comment #1 for the test-case 'aa-refcnt-
* Exercise that code path indefinitely until it hits
the refcount_t overflow/
(or not, with the patch.) (see comment #4)
* It's possible to monitor refcount values with kprobes,
to confirm whether or not the problem is happening.
(see comments #2 and #3)
[Other Info]
* Patch applied upstream on v5.8-rc1 [1]
* Applied on Unstable (tag Ubuntu-
* Not required on Groovy (still 5.4; should sync from Unstable)
* Not required on Eoan (EOL date before SRU cycle release date)
* Required on Bionic and Focal.
CVE References
Changed in linux (Ubuntu): | |
importance: | Undecided → Medium |
assignee: | nobody → Mauricio Faria de Oliveira (mfo) |
Changed in linux (Ubuntu Groovy): | |
status: | New → Won't Fix |
Changed in linux (Ubuntu Eoan): | |
status: | New → Won't Fix |
Changed in linux (Ubuntu Bionic): | |
status: | New → In Progress |
Changed in linux (Ubuntu Focal): | |
status: | New → In Progress |
Changed in linux (Ubuntu Groovy): | |
importance: | Medium → Undecided |
assignee: | Mauricio Faria de Oliveira (mfo) → nobody |
Changed in linux (Ubuntu Focal): | |
importance: | Undecided → Medium |
assignee: | nobody → Mauricio Faria de Oliveira (mfo) |
Changed in linux (Ubuntu Bionic): | |
importance: | Undecided → Medium |
assignee: | nobody → Mauricio Faria de Oliveira (mfo) |
Changed in linux (Ubuntu): | |
status: | New → Fix Committed |
tags: | added: sts |
Changed in linux (Ubuntu Groovy): | |
status: | Won't Fix → Invalid |
Changed in linux (Ubuntu Eoan): | |
status: | In Progress → Fix Committed |
Changed in linux (Ubuntu Bionic): | |
status: | In Progress → Fix Committed |
Changed in linux (Ubuntu Focal): | |
status: | In Progress → Fix Committed |
Changed in linux (Ubuntu): | |
status: | Fix Committed → Fix Released |
Test Case:
---------
$ cat aa-refcnt-af_alg.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
int main() {
int sockfd;
struct sockaddr_alg sa;
/* Setup the crypto API socket */
perror( "socket" );
sockfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (sockfd < 0) {
return 1;
}
memset(&sa, 0, sizeof(sa));
sa.salg_family = AF_ALG;
strcpy((char *) sa.salg_type, "rng");
strcpy((char *) sa.salg_name, "stdrng");
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
perror( "bind") ;
return 1;
}
/* Accept a "connection" and close it; repeat. */ accept( sockfd, NULL, 0)));
while (!close(
return 0;
}
$ gcc -o aa-refcnt-af_alg aa-refcnt-af_alg.c
$ ./aa-refcnt-af_alg
<a few hours later>
[ 9928.475953] refcount_t overflow at apparmor_ sk_clone_ security+ 0x37/0x70 in aa-refcnt- af_alg[ 1322], uid/euid: 1000/1000 sk_clone_ security+ 0x37/0x70 sk_clone+ 0x33/0x50 accept+ 0x81/0x1c0 [af_alg] 0x15/0x20 [af_alg] 0xff/0x210 0x10/0x20 64+0x73/ 0x130 64_after_ hwframe+ 0x3d/0xa2
...
[ 9928.507443] RIP: 0010:apparmor_
...
[ 9928.514286] security_
[ 9928.514807] af_alg_
[ 9928.516091] alg_accept+
[ 9928.516682] SYSC_accept4+
[ 9928.519609] SyS_accept+
[ 9928.520190] do_syscall_
[ 9928.520808] entry_SYSCALL_
Note that other messages may be seen, not just overflow, depending on
the value being incremented by kref_get(); on another run:
[ 7273.182666] refcount_t: saturated; leaking memory.
...
[ 7273.185789] refcount_t: underflow; use-after-free.