In this article I describe one of the most interesting bugs I've had to fix.
This bug surfaced while I was extending Snowdrop OS's BASIC interpreter. Like all other Snowdrop OS software, I was writing in assembly language.
The bug occurs when the program assigns an empty string to a variable, via LET variable = "". Any subsequent variable assignments cause undefined behaviour.
A program exhibiting this issue would look like this:
LET variable = "";
LET otherVar = 3;
This bug is an unfortunate combination of two different issues, as well as a zero byte found by chance in a specific place.
- First issue: during the execution of LET, a variable must be looked up by name. Erroneously, a pointer to the variable value instead of the variable name is used. In our case of LET variable = "", this effectively looks up a variable with an empty name.
- Second issue: variable lookup-by-name routine has an off by one error (x86 opcode ja instead of jae), causing it to look at an extra, false variable slot immediately past the end of the variable storage area.
- Unfortunate zero: executable code lies immediately past the end of the variable storage area. By chance, the first byte of the would-be variable name string was zero. This means that the respective chunk of executable code was regarded as a variable with an empty name.
|var slot |var slot |var slot |last var slot | ... executable code
The executable code is now seen as a variable slot with a matching name string. Upon finding this false variable slot, LET modifies it, effectively corrupting executable code with data.
Then, subsequent LET instructions will execute the corrupted executable code, causing undefined behaviour.