DF-3346 / df3346.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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | /* * DF-3346 harness -- SIOCSLAGG sets sc_proto BEFORE ti_attach; when * the attach fails (its kmalloc(M_NOWAIT) in lacp_attach fails, or any * other failure return), sc_proto stays set while sc_psc is NULL. * The NEXT SIOCSLAGG runs sc->sc_detach(sc) = lagg_lacp_detach -> * lacp_port_destroy(lp) = LACP_PORT(lgp) = lgp->lp_psc == NULL -> * NULL dereference. Fault-injection harness with the verbatim * SIOCSLAGG ordering (if_lagg.c:1086-1117) and lacp path shapes. */ #include "guard.h" #include <stdio.h> #include <string.h> #include <stdlib.h> struct lagg_port { const char *ifname; void *lp_psc; /* NULL when create failed */ int lp_entries; }; struct lacp_port { int dummy; }; struct lagg_softc { int sc_proto; int (*sc_detach)(struct lagg_softc *); void (*sc_port_destroy)(struct lagg_port *); struct lagg_port ports[4]; int nports; }; #define LAGG_PROTO_NONE 0 #define LAGG_PROTO_LACP 1 #define LAGG_PROTO_FAILOVER 2 static int lacp_attach_fail; /* fault knob: M_NOWAIT failure */ static void *kmalloc_or_null(size_t sz) { return lacp_attach_fail ? NULL : calloc(1, sz); } /* verbatim lagg_lacp_detach -> lacp_port_destroy deref shape */ static void lacp_port_destroy(struct lagg_port *lgp) { struct lacp_port *lp = lgp->lp_psc; /* LACP_PORT(lgp) */ lp->dummy = 1; /* <- NULL deref when unset */ } static int lagg_lacp_detach(struct lagg_softc *sc) { int i; for (i = 0; i < sc->nports; i++) lacp_port_destroy(&sc->ports[i]); /* return ignored */ return 0; } /* verbatim lacp_attach (ieee8023ad_lacp.c:795-804): */ static int lacp_attach(struct lagg_softc *sc) { if (kmalloc_or_null(sizeof(struct lacp_port) * 8) == NULL) return 12; /* ENOMEM */ return 0; } static int lagg_lacp_attach(struct lagg_softc *sc) { sc->sc_detach = lagg_lacp_detach; /* callbacks set FIRST */ return lacp_attach(sc); /* ... and can fail after */ } /* fixed SIOCSLAGG ordering: publish sc_proto only after attach succeeds */ static int siocslagg_fixed(struct lagg_softc *sc, int newproto) { int error = 0; if (sc->sc_proto != LAGG_PROTO_NONE) { sc->sc_proto = LAGG_PROTO_NONE; error = sc->sc_detach(sc); } if (error != 0) return error; if (newproto == LAGG_PROTO_LACP) { error = lagg_lacp_attach(sc); /* attach FIRST */ if (error == 0) { sc->sc_proto = LAGG_PROTO_LACP; /* publish on success */ } else { /* roll back: never leave a half-attached protocol */ sc->sc_detach = NULL; sc->sc_proto = LAGG_PROTO_NONE; } } return error; } /* verbatim SIOCSLAGG ordering (if_lagg.c:1086-1117) */ static int siocslagg(struct lagg_softc *sc, int newproto) { int error = 0; if (sc->sc_proto != LAGG_PROTO_NONE) { sc->sc_proto = LAGG_PROTO_NONE; /* reset FIRST */ error = sc->sc_detach(sc); /* detach old (unlocks!) */ /* callbacks nulled */ } if (error != 0) return error; if (newproto == LAGG_PROTO_LACP) { sc->sc_proto = LAGG_PROTO_LACP; /* set BEFORE attach */ error = lagg_lacp_attach(sc); /* attach may FAIL here */ } return error; } static struct lagg_softc sc; static struct lagg_softc sc; int main(int argc, char **argv) { int poison = (argc > 1 && argv[1][0] == 'p'); printf("DF-3346 (sc_proto set before ti_attach; failed attach" " poisons the next SIOCSLAGG)\n"); memset(&sc, 0, sizeof(sc)); sc.ports[sc.nports++].ifname = "tap0"; lacp_attach_fail = poison ? 1 : 0; #ifdef FIXED int e1 = siocslagg_fixed(&sc, LAGG_PROTO_LACP); #else int e1 = siocslagg(&sc, LAGG_PROTO_LACP); #endif printf(" attach %s: error=%d, sc_proto=%d, ports lp_psc=NULL\n", poison ? "FAILED (fault injection)" : "succeeded", e1, sc.sc_proto); if (!poison) { printf(" control: attach ok, next switch is safe\n"); siocslagg(&sc, LAGG_PROTO_FAILOVER); printf(" ran (balanced)\n"); return 0; } printf(" next SIOCSLAGG -> lacp_port_destroy derefs NULL lp_psc:\n"); fflush(stdout); #ifdef FIXED /* DF-3346 fix: only publish sc_proto after a successful attach */ { int e2 = siocslagg_fixed(&sc, LAGG_PROTO_FAILOVER); printf("FIXED: next SIOCSLAGG returned %d (no NULL deref)\n", e2); return 0; } #endif siocslagg(&sc, LAGG_PROTO_FAILOVER); /* NULL deref -> SEGV */ printf(" ran?!\n"); return 0; } |