Hi Matt, i finally found some time to look at your diff and it looks pretty good to me so far. I have a few question about the SIOCGWG ioctl.
> +void > +wg_status(void) > +{ > + size_t i, j, size = 0; > + struct timespec now; > + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; > + char key[WG_BASE64_KEY_LEN + 1]; > + struct wg_data_io wgdata; > + struct wg_interface_io *iface; > + > + strlcpy(wgdata.wgd_name, ifname, sizeof(wgdata.wgd_name)); > + wgdata.wgd_size = 0; > + wgdata.wgd_mem = NULL; > + > + if (ioctl(sock, SIOCGWG, (caddr_t)&wgdata) == -1 && > + (errno == ENOTTY || errno == EPERM)) > + return; > + > + while (size < wgdata.wgd_size) { > + size = wgdata.wgd_size; > + wgdata.wgd_mem = realloc(wgdata.wgd_mem, size); > + if (ioctl(sock, SIOCGWG, (caddr_t)&wgdata) == -1) > + err(1, "SIOCGWG"); > + } As I understand it you call the IOCTL at least twice. The first time you pass in 'wgd_size == 0'. The IOCTL handler calculates the required size and returns it in wgd_size. Then you pass in a realloced wgdata until the ioctl returns 0 and the size has not changed. > + > + iface = wgdata.wgd_mem; > +int > +wg_ioctl_get(struct wg_softc *sc, struct wg_data_io *data) > +{ > + struct wg_interface_io *iface_p, iface_o; > + struct wg_peer_io *peer_p, peer_o; > + struct wg_aip_io *aip_p, aip_o; > + > + struct wg_peer *peer; > + struct wg_aip *aip; > + > + size_t size, i; > + void *mem; > + int ret = 0; > + > + rw_enter_read(&sc->sc_lock); > + > + size = sizeof(struct wg_interface_io); > + if (((SIZE_MAX - size) / sizeof(struct wg_peer_io)) < sc->sc_peer_num) > + goto error; > + size += sizeof(struct wg_peer_io) * sc->sc_peer_num; > + if (((SIZE_MAX - size) / sizeof(struct wg_aip_io)) < sc->sc_aip_num) > + goto error; > + size += sizeof(struct wg_aip_io) * sc->sc_aip_num; > + > + if (data->wgd_size < size) > + goto error; The (data->wgd_size < size) is for the loop in ifconfig. If the input size was too small you 'goto error', which is an unfortunate name because it is not actually an error but sets 'wgd_size' to the required 'size' and returns 0. Maybe 'cleanup' would be a better name. What I don't understand is why the two previous checks that make sure the result fits into SIZE_MAX 'goto error' without setting ret != 0 (thus breaking the loop in ifconfig without data actually being written). It looks a bit like the error got lost in refactoring. ERANGE would probably be a good choice: 34 ERANGE Result too large. A result of the function was too large to fit in the available space (perhaps exceeded precision). > + > +error: > + rw_exit_read(&sc->sc_lock); > + explicit_bzero(&iface_o, sizeof(iface_o)); > + explicit_bzero(&peer_o, sizeof(peer_o)); > + data->wgd_size = size; > + return ret; > +} > +