DF-3315 / fix.diff
--- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -2456,19 +2456,42 @@ LIST_INSERT_HEAD(&pcbinfo->pcblisthead, marker, inp_list); i = 0; while ((inp = LIST_NEXT(marker, inp_list)) != NULL && i < n) { + int skip = 0; + + /* + * The marker advance (read next, unlink marker, + * relink marker after next) is a non-atomic + * two-step sequence. For pcbinfo's without an + * infotoken (e.g. ripcbinfo) nothing else + * serializes us against a same-cpu netisr tearing + * down the pcb we just stepped to: relinking the + * marker after an unlinked (and possibly freed and + * recycled) inpcb corrupts the list and writes + * into freed memory. The critical section makes + * the advance + snapshot atomic against same-cpu + * preemption; the marker itself keeps the walk + * coherent across the (sleeping) SYSCTL_OUT below. + */ + crit_enter(); LIST_REMOVE(marker, inp_list); LIST_INSERT_AFTER(inp, marker, inp_list); - if (inp->inp_flags & INP_PLACEMARKER) - continue; - if (prison_xinpcb(req->td, inp)) - continue; + if (inp->inp_flags & INP_PLACEMARKER) { + skip = 1; + } else if (prison_xinpcb(req->td, inp)) { + skip = 1; + } else { + bzero(&xi, sizeof xi); + xi.xi_len = sizeof xi; + bcopy(inp, &xi.xi_inp, sizeof *inp); + if (inp->inp_socket) + sotoxsocket(inp->inp_socket, + &xi.xi_socket); + } + crit_exit(); - bzero(&xi, sizeof xi); - xi.xi_len = sizeof xi; - bcopy(inp, &xi.xi_inp, sizeof *inp); - if (inp->inp_socket) - sotoxsocket(inp->inp_socket, &xi.xi_socket); + if (skip) + continue; if ((error = SYSCTL_OUT(req, &xi, sizeof xi)) != 0) break; ++i; |