I've reviewed this diff with Patrick and I believe it is correct, can someone else have a look ?
On Tue, Sep 04, 2012 at 04:46:08PM +0200, Patrick Wildt wrote: > When sasyncd(8) dumps data from the kernel, it does a sysctl() for the size to > alloc, > and a second for the actual data. In between, new data can be added/deleted. > Therefore > the kernel might return less data, than actually told. The case that you get > less/more data > isn't handled properly. > > In case of less data, it might happen that the child is working on > uninitialized data. > This might lead to a segmentation fault. > In case of more data, the sasyncd(8) parent process handles it as he didn't > get any data. > > This diff rewrites that part, so that we try thrice to get the SAs and SPDs. > If getting the SAs > breaks thrice, we tell the child process, that there's no data. Same for the > SPDs. > > The case of less data is now handled properly. The case of more data is either > caught > by the for loop, or allocing twice the size of the data we should originally > get. > That might be helpful on bigger vpn setups, when the loop still doesn't catch > the race. > > Tested with 7080 SAs. (For real.) > > Index: monitor.c > =================================================================== > RCS file: /cvs/src/usr.sbin/sasyncd/monitor.c,v > retrieving revision 1.15 > diff -u -r1.15 monitor.c > --- monitor.c 2 Apr 2012 18:56:15 -0000 1.15 > +++ monitor.c 4 Sep 2012 13:06:37 -0000 > @@ -342,7 +342,7 @@ > { > u_int8_t *sadb_buf = 0, *spd_buf = 0; > size_t sadb_buflen = 0, spd_buflen = 0, sz; > - int mib[5]; > + int i, mib[5]; > u_int32_t v; > > mib[0] = CTL_NET; > @@ -352,59 +352,81 @@ > mib[4] = 0; /* Unspec SA type */ > > /* First, fetch SADB data */ > - if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 > - || sz == 0) { > - sadb_buflen = 0; > - goto try_spd; > - } > - > - sadb_buflen = sz; > - if ((sadb_buf = malloc(sadb_buflen)) == NULL) { > - log_err("m_priv_pfkey_snap: malloc"); > - sadb_buflen = 0; > - goto out; > - } > - > - if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, NULL, 0) > - == -1) { > - log_err("m_priv_pfkey_snap: sysctl"); > - sadb_buflen = 0; > - goto out; > + for (i = 0; i < 3; i++) { > + if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) > + == -1) > + break; > + > + if (!sz) > + break; > + > + /* Try to catch newly added data */ > + sz *= 2; > + > + if ((sadb_buf = malloc(sz)) == NULL) > + break; > + > + if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, > NULL, 0) > + == -1) { > + free(sadb_buf); > + sadb_buf = 0; > + /* > + * If new SAs were added meanwhile and the given buffer > is > + * too small, retry. > + */ > + if (errno == ENOMEM) > + continue; > + break; > + } > + > + sadb_buflen = sz; > + break; > } > > /* Next, fetch SPD data */ > - try_spd: > mib[3] = NET_KEY_SPD_DUMP; > > - if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 > - || sz == 0) { > - spd_buflen = 0; > - goto out; > - } > + for (i = 0; i < 3; i++) { > + if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) > + == -1) > + break; > > - spd_buflen = sz; > - if ((spd_buf = malloc(spd_buflen)) == NULL) { > - log_err("m_priv_pfkey_snap: malloc"); > - spd_buflen = 0; > - goto out; > - } > + if (!sz) > + break; > + > + /* Try to catch newly added data */ > + sz *= 2; > + > + if ((spd_buf = malloc(sz)) == NULL) > + break; > + > + if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, > 0) > + == -1) { > + free(spd_buf); > + spd_buf = 0; > + /* > + * If new SPDs were added meanwhile and the given > buffer is > + * too small, retry. > + */ > + if (errno == ENOMEM) > + continue; > + break; > + } > > - if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, 0) > - == -1) { > - log_err("m_priv_pfkey_snap: sysctl"); > - spd_buflen = 0; > - goto out; > + spd_buflen = sz; > + break; > } > > - out: > /* Return SADB data */ > v = (u_int32_t)sadb_buflen; > if (m_write(s, &v, sizeof v) == -1) { > log_err("m_priv_pfkey_snap: write"); > goto cleanup; > } > - if (m_write(s, sadb_buf, sadb_buflen) == -1) > + if (m_write(s, sadb_buf, sadb_buflen) == -1) { > log_err("m_priv_pfkey_snap: write"); > + goto cleanup; > + } > > /* Return SPD data */ > v = (u_int32_t)spd_buflen; > @@ -412,13 +434,20 @@ > log_err("m_priv_pfkey_snap: write"); > goto cleanup; > } > - if (m_write(s, spd_buf, spd_buflen) == -1) > + if (m_write(s, spd_buf, spd_buflen) == -1) { > log_err("m_priv_pfkey_snap: write"); > - cleanup: > - memset(sadb_buf, 0, sadb_buflen); > - free(sadb_buf); > - memset(spd_buf, 0, spd_buflen); > - free(spd_buf); > + goto cleanup; > + } > + > +cleanup: > + if (sadb_buf) { > + memset(sadb_buf, 0, sadb_buflen); > + free(sadb_buf); > + } > + if (spd_buf) { > + memset(spd_buf, 0, spd_buflen); > + free(spd_buf); > + } > } > > static int