I said that I was going to start writing these regularly, so I'm going to
stick to it, even when the results are rather underwhelming. One of the
goals is to make the time for more free software work, and I do better at
doing things that I record.
The only piece of free software work for September was that I made
rra-c-util compile cleanly with the
Clang static analyzer. This was fairly tedious work that mostly involved
unconfusing the compiler or converting (semi-intentional) crashes into
explicit asserts, but it unblocks using the Clang static analyzer as part
of the automated test suite of my other projects that are downstream of
rra-c-util.
One of the semantic changes I made was that the vector utilities in
rra-c-util (which maintain a resizable array of strings) now always
allocate room for at least one string pointer. This wastes a small amount
of memory for empty vectors that are never used, but ensures that the
strings struct member is always valid. This isn't, strictly
speaking, a correctness fix, since all the checks were correct, but after
some thought, I decided that humans might have the same problem that the
static analyzer had. It's a lot easier to reason about a field that's
never NULL. Similarly, the replacement function for a missing
reallocarray now does an allocation of size 1 if given a size of 0,
just to avoid edge case behavior. (I'm sure the behavior of a realloc
with size 0 is defined somewhere in the C standard, but if I have to look
it up, I'd rather not make a human reason about it.)
I started on, but didn't finish, making rra-c-util compile without Clang
warnings (at least for a chosen set of warnings). By far the hardest
problem here are the Clang warnings for comparisons between unsigned and
signed integers. In theory, I like this warning, since it's the cause of
a lot of very obscure bugs. In practice, gah does C ever do this all over
the place, and it's incredibly painful to avoid. (One of the biggest
offenders is
write, which returns a
ssize_t that you almost
always want to compare against a
size_t.) I did a bunch of
mechanical work, but I now have a lot of bits of code like:
if (status < 0)
return;
written = (size_t) status;
if (written < avail)
buffer->left += written;
which is ugly and unsatisfying. And I also have a ton of casts, such as
with:
buffer_resize(buffer, (size_t) st.st_size + used);
since
st.st_size is an
off_t, which may be signed. This is
all deeply unsatisfying and ugly, and I think it makes the code moderately
harder to read, but I do think the warning will potentially catch bugs and
even security issues.
I'm still torn. Maybe I can find some nice macros or programming styles
to avoid the worst of this problem. It definitely requires more thought,
rather than just committing this huge mechanical change with lots of ugly
code.
Mostly, this kind of nonsense makes me want to stop working on C code and
go finish learning Rust....
Anyway, apart from work, the biggest thing I managed to do last month that
was vaguely related to free software was upgrading my personal servers to
stretch (finally). That mostly went okay; only a few things made it
unnecessarily exciting.
The first was that one of my systems had a very tiny / partition that was
too small to hold the downloaded debs for the upgrade, so I had to resize
it (VM disk, partition, and file system), and that was a bit exciting
because it has an old-style DOS partition table that isn't aligned (hmmm,
which is probably why disk I/O is so slow on those VMs), so I had to use
the obsolete
fdisk -c=dos mode because I wasn't up for replacing
the partition right then.
The second was that my first try at an upgrade died with a segfault during
the libc6 postinst and then every executable segfaulted. A mild panic and
a rescue disk later (and thirty minutes and a lot of swearing), I tracked
the problem down to libc6-xen. Nothing in the dependency structure
between jessie and stretch forces libc6-xen to be upgraded in lockstep or
removed, but it's earlier in the search path. So ld.so gets upgraded, and
then finds the old libc6 from the libc6-xen package, and the mismatch
causes immediate segfaults. A
chroot dpkg --purge from the rescue
disk solved the problem as soon as I knew what was going on, but that was
a stressful half-hour.
The third problem was something I should have known was going to be an
issue: an old Perl program that does some internal stuff for one of the
services I ran had a
defined @array test that has been warning for
eons and that I never fixed. That became a full syntax error with the
most recent Perl, and then I fixed it incorrectly the first time and had a
bunch of trouble tracking down what I'd broken. All sorted out now, and
everything is happily running stretch. (ejabberd, which other folks had
mentioned was a problem, went completely smoothly, although I suspect I
now have too many of the plugin packages installed and should do a
purging.)