I'll make a comment that I am quite unhappy about this ioctl
methodology.  I don't like void *'s and varying sizes.

I would be much happier if this was a fixed structure, filled with
known objects.

It looks fragile.



Tobias Heider <tobias.hei...@stusta.de> wrote:

> 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;
> > +}
> > +
> 

Reply via email to