1983. The year of the IBM PC XT, the Apple Lisa, Pioneer 10 leaving the solar system, and Hooters opening up shop in Florida. It’s also the birthyear of a 25 year old BSD bug, squashed only a few days ago.
A few days ago, Marc Balmer, OpenBSD developer, received an email from an OpenBSD user. The email claimed that SAMBA would crash when serving files off an MS-DOS filesystem. Balmer got into contact with a few SAMBA developers who claimed that SAMBA uses a special workaround in order to function properly on BSD systems: the code for reading directories in all BSDs was flawed.
Understandably, Balmer’s first reaction was disbelief. “Of course my first reaction was to blame Samba,” he writes. Despite his initial reaction, he decided to dig deeper into this case, and he uncovered a bug that had been sitting in the code of all BSDs (including Mac OS X), including a lot of old releases. He confirmed the bug was already in 4.2BSD, released in August of 1983.
The bug itself? Well, I’m no programmer so the actual code is kind of gibberish to me, but I think I get the gist of the problem.
This code will not work as expected when seeking to the second entry of a block where the first has been deleted: seekdir() calls readdir() which happily skips the first entry (it has inode set to zero), and advance to the second entry. When the user now calls readdir() to read the directory entry to which he just seekdir()ed, he does not get the second entry but the third.
Marshall Kirk McKusick, the original developer of the *dir() library, commented on the issue in a personal conversation with Balmer:
As the original author of the *dir() library, you probably fixed one of my bugs :-). Prior to the *dir() commands, programs just opened, read, and interpreted directories directly. I had to update a shocking 22 programs (a large percentage of the programs available on UNIX at the time) to replace their direct interpretation of directories with the *dir() library calls.
This little bug’s fix was actually fairly trivial (as is common with these sorts of long-standing bugs): “The fix is surprisingly simple, not to say trivial: _readdir_unlocked() must not skip directory entries with inode set to zero when it is called from __seekdir().”
“Sorry that it took us almost twenty-five years to fix it,” Balmer adds, jokingly.