[REGRESSION] apparmor: AF_UNIX datagram send slowdown after 6456ccbd2ff7

Chengfeng Lin lin2530632123 at gmail.com
Tue Sep 22 16:27:59 UTC 2026


Hi John,

I tested a small early-return prototype on v7.3-rc4 (93f51579e7df).
It reduced send syscall time by 12.84% in the original unconfined
socketpair benchmark. A separate two-process test found a 1.25% cost
when both peers were confined but allowed to communicate.

The attached patch returns early from aa_unix_peer_perm() only when the
whole label is unconfined. It skips peer-address and audit preparation.
The sender and receiver are still checked separately. The patch adds no
per-object fields and does not change label updates or locking.

For each prototype comparison, I used original -> patched -> original,
with a separate boot for each point. Both kernels used the same baseline,
GCC 15.2.0 and config, except for the kernel release suffix
(CONFIG_LOCALVERSION). The machine was the same bare-metal i7-12700KF
with 32 GiB RAM, full preemption, performance governor/EPP and Turbo off.

Each condition had three invocations per boot. Each invocation had
3 warm-up and 15 measured rounds of 65,536 messages, sent in batches of
32 messages of 128 bytes. Only sendmmsg() was timed; peer draining and
payload checks were outside the timed interval.

The original socketpair test ran on CPU 2. Results in ns/message were:

  original A       patched       original B
     416.073       359.448          408.719

The patch saved 52.95 ns/message, or 12.84%, against the original midpoint.
Maximum within-invocation CV was 0.114%; original-kernel drift was 1.78%.

The two-process test used connected abstract AF_UNIX datagram sockets,
with the sender on CPU 2 and receiver on CPU 4. Both ran as UID 1000.
The confined policies allowed communication. Results in ns/message were:

  peers                  original midpoint   patched      change
  both unconfined                  455.564     412.052      -9.55%
  sender confined only             569.902     561.786      -1.42%
  receiver confined only           578.244     562.807      -2.67%
  both confined                    741.385     750.620      +1.25%

The socketpair and two-process tests have different workload shapes.
Their absolute timings should not be compared directly.
The both-confined case added 9.24 ns/message, with maximum CV 0.51%
and original-kernel drift 0.16%. Across all four cases, these maxima
were 1.80% and 0.60%. These are send-only microbenchmark results,
not application timings.

I tested confined/unconfined peer combinations, stacked labels, datagram
and stream sockets, old and new policy ABIs, and serial policy replacement
followed by socket recreation. Allow and deny results matched expectations
on both the original and patched kernels. Fine-grained UNIX permission
tests used ABI 5.0 with network_v9.

Separate untimed probes confirmed that the outer checks still ran for
both sender and receiver. In the unconfined socketpair probe, internal
unix_peer_perm() calls were 2,112 -> 0 -> 2,112 across
original -> patched -> original. The two-process probes also confirmed
that only unconfined sides skipped this internal check.

The follow-up kept sockets open across policy and mode changes. Permission
results matched between the original and patched kernels. Each boot also
had two concurrent tests, each lasting two seconds with 16 policy
replacements during the send loop. A fixed sender deny remained effective
while the receiver policy changed.

One limit is worth noting: receiver-side deny updates did not revoke
access through the existing socket, on either kernel. That path uses the
socket's stored label, so matching the original behavior does not prove
immediate revocation. The live-update tests did not cover stacked labels
and were not exhaustive.

Separately, the original controlled source delta still reproduced a
13.61% slowdown. v7.3-rc4 was 20.35% slower than the same old baseline.
The latter includes other source and config changes; I do not attribute
that whole gap to 6456ccbd2ff7.

Results and test sources are at [1].

Would this whole-label early return be worth pursuing, given the small
cost on the both-confined path? This is a prototype for review.

Thanks,
Chengfeng

[1] https://github.com/lcf0399/linux-regression-evidence/tree/23b61ebab6aff0d8316334da7a988811082d204d/apparmor-af-unix-send-old-abi-6456cc/bare-metal/early-unconfined-20260922
-------------- next part --------------
--- a/security/apparmor/af_unix.c
+++ b/security/apparmor/af_unix.c
@@ -634,12 +634,17 @@
 	struct unix_sock *peeru = unix_sk(peer_sk);
 	struct unix_sock *u = unix_sk(sk);
 	int plen;
-	struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), &plen);
+	struct sockaddr_un *paddr;
 
 	AA_BUG(!label);
 	AA_BUG(!sk);
 	AA_BUG(!peer_sk);
 	AA_BUG(!peer_label);
+
+	if (unconfined(label))
+		return 0;
+
+	paddr = aa_sunaddr(unix_sk(peer_sk), &plen);
 
 	return unix_peer_perm(subj_cred, label, op, request, sk,
 			      is_unix_fs(sk) ? &u->path : NULL,
@@ -809,4 +814,3 @@
 
 	return error;
 }
-


More information about the Linux-security-module-archive mailing list