DF-0509 / harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | /* * DF-0509 analysis: ng_ksocket_sockaddr_unparse stack overflow via negative pathlen * * Source: sys/netgraph/ksocket/ng_ksocket.c:323-339 (v1 โ loadable) * sys/netgraph7/ksocket/ng_ksocket.c:323-339 (ng7 โ dead code) * * The bug in the code: * ng_ksocket_sockaddr_unparse: * const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); // = 2 * const int pathlen = sun->sun_len - pathoff; // underflows if sun_len < 2 * char pathbuf[SOCK_MAXADDRLEN + 1]; * bcopy(sun->sun_path, pathbuf, pathlen); // pathlen negative -> huge size_t * * The ng_ksocket BIND/CONNECT sanity checks (lines 655, 717): * if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len) * do NOT check sa->sa_len >= SADATA_OFFSET, so a message with sa_len=0/1 passes. * * HOWEVER: defense-in-depth in unp_bind (sys/kern/uipc_usrreq.c:1141-1143): * namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path); * if (namelen <= 0) * return EINVAL; * This rejects short sockaddrs BEFORE they are stored, so a ksocket bound * via PF_LOCAL can never have a stored sockaddr with sa_len < 3. * * CONCLUSION: The unparse function has a latent integer-underflow vulnerability, * but the only data path that feeds it (sobind -> unp_bind) validates the * length. The bug is NOT triggerable to panic/RCE on DragonFly. * * CLASSIFICATION: FALSE POSITIVE (defense-in-depth in unp_bind prevents * exploitation). The ng_ksocket sanity check should still be hardened. * * VERDICT: The finding's claim that "sobind accepts short sockaddr" is * incorrect โ unp_bind at uipc_usrreq.c:1141-1143 rejects it. * * This program documents the analysis by demonstrating: * 1. The underflow in pathlen computation * 2. The unp_bind validation that blocks it */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stddef.h> /* Simplified sockaddr_un */ struct sockaddr_un { uint8_t sun_len; uint8_t sun_family; char sun_path[104]; }; #define SADATA_OFFSET offsetof(struct sockaddr_un, sun_path) /* = 2 */ /* Replicates ng_ksocket_sockaddr_unparse pathlen computation */ static int compute_pathlen(uint8_t sun_len) { const int pathoff = SADATA_OFFSET; const int pathlen = sun_len - pathoff; /* underflow if sun_len < 2 */ return pathlen; } /* Replicates unp_bind validation (uipc_usrreq.c:1141-1143) */ static int unp_bind_validate(uint8_t sun_len) { int namelen = sun_len - SADATA_OFFSET; if (namelen <= 0) return -1; /* EINVAL โ rejected */ return 0; /* OK */ } /* Replicates ng_ksocket BIND sanity check (ng_ksocket.c:655-656) */ static int ng_ksocket_sanity(int arglen, uint8_t sa_len) { if (arglen < SADATA_OFFSET || arglen < sa_len) return -1; /* EINVAL */ return 0; /* passes โ does NOT check sa_len >= SADATA_OFFSET */ } int main(void) { printf("=== DF-0509: ng_ksocket_sockaddr_unparse pathlen underflow ===\n\n"); printf("SADATA_OFFSET = offsetof(sockaddr_un, sun_path) = %ld\n\n", (long)SADATA_OFFSET); uint8_t test_lens[] = {0, 1, 2, 3, 5, 10}; for (int i = 0; i < (int)(sizeof(test_lens)/sizeof(test_lens[0])); i++) { uint8_t sl = test_lens[i]; int pathlen = compute_pathlen(sl); int ng_sanity = ng_ksocket_sanity(SADATA_OFFSET, sl); /* arglen=2 */ int unp = unp_bind_validate(sl); printf("sun_len=%d:\n", sl); printf(" ng_ksocket sanity (arglen=2): %s\n", ng_sanity == 0 ? "PASSES (weak)" : "FAILS"); printf(" pathlen = %d - %ld = %d\n", sl, (long)SADATA_OFFSET, pathlen); if (pathlen < 0) printf(" -> bcopy size = (size_t)%d = 0x%lx -> MASSIVE overflow!\n", pathlen, (unsigned long)(size_t)pathlen); printf(" unp_bind validation: %s\n\n", unp == 0 ? "ACCEPTS" : "REJECTS (EINVAL)"); } printf("=== CONCLUSION ===\n"); printf("For sun_len < 2:\n"); printf(" ng_ksocket BIND sanity PASSES (bug)\n"); printf(" BUT unp_bind REJECTS with EINVAL (defense-in-depth)\n"); printf(" -> sockaddr never stored -> unparse never sees it\n"); printf(" -> NOT triggerable on DragonFly\n\n"); printf("RECOMMENDATION: Harden ng_ksocket sanity check anyway (fix.diff).\n"); printf("CLASSIFICATION: FALSE POSITIVE (latent bug, blocked by unp_bind).\n"); return 0; } |