Somewhat interesting bug in Stationfall

After seeing a bug report about a crash in Stationfall (github.com/garglk/garglk/issues/284), I did some digging to see what was going on.

It looks like Stationfall has a bug in the very beginning of the game, in the form-reading slot. Inserting a non-form item into the slot will either trigger an “item too large” message, or “the slot spits the item back”. However, the way the game determines whether an item is too large is totally invalid. Some Z-machine disassembly follows…

Routine e282, 0 locals ()

 e283:  41 92 37 01 a7          JE              G82,#37 [FALSE] e42d
 e288:  41 d8 fe 01 a2          JE              Gc8,#fe [FALSE] e42d
 e28d:  41 77 88 67             JE              G67,#88 [FALSE] e2b6
 e291:  b3 ...                  PRINT_RET       "The form's crumpledness
prevents it from sliding into the slot."
 e2b6:  41 77 62 78             JE              G67,#62 [FALSE] e2f0
...

Routine e282 looks like the handler for insertion of items into the slot. At the point it’s called, global variable G67 contains the object number of the item being inserted. #37 is the object “crumpled form”, #62 is “Class Three Spacecraft Activation Form HB-56-V”, etc, over all the forms. But later on:

 e3fe:  31 0c 77 00             GET_PROP        "Level Three",G67 -> -(SP)
 e402:  42 00 03 61             JL              (SP)+,#03 [FALSE] e425
 e406:  b2 ...                  PRINT           "The slot swallows"
 e411:  e0 3f 2b 46 00          CALL            568c -> -(SP)
 e416:  b3 ...                  PRINT_RET       " and then spits it back."
 e425:  e0 0f 7a 79 d5 b4 00    CALL            f4f2 (S100) -> -(SP)
 e42c:  b8                      RET_POPPED

Here’s the bug. At e3fe, the property stored in G67 is looked up for the “Level Three” object. If that value is less than 3, the slot spits the item back, otherwise it calls the routine at f4f2, which just prints out a “too large” message.

But the problem here is that in a version 3 game, there can’t be a property larger than 64; however, object IDs can be larger than 64 (and right at the start of the game, the uniform is object 229, and the ID is object 81). So these invalid properties are looked up and treated as the size (I assume, anyway, that’s the goal here) of the item.

Most interpreters wind up looking for these properties in the property defaults table, which has only 64 entries, so the result is reading a word outside the bounds of the table, which contains a junk value. This value is then compared to 3 to determine which message to print.

This seems totally harmless, since memory is large enough so that even the largest object will at least not break the bounds of the entire story file, and in both cases, the item can’t be put in the slot, so in the worst case, the error message is simply nonsensical (e.g. a huge object being spit out, but a small object being rejected for not fitting).

And I have no idea why the “Level Three” would be the source of item sizes, even if the lookup code made sense. It seems that something just went wrong in the source, but playtesting wouldn’t have discovered it: in the beginning, at least, by happenstance, the two smaller objects (watch and ID card) get spit back, but the uniform is considered too large. I think that’s coincidence, but it would have been enough, it seems, to have passed playtesting if anybody tried it.

Nothing earth-shattering but I found it a bit interesting.

The “Level Three” object is object 12. What they intended to do was check property 12 (which is evidently SIZE) of the object in G67 (surely PRSO), instead of property G67 of object 12.
I don’t remember the syntax for testing properties in ZIL, but presumably it’s an easy mistake to make.

Great catch; that definitely looks to be the culprit. The syntax for ZIL is:

<GETP ,PRSO ,P?SIZE>

which presumably was mistyped as

<GETP ,P?SIZE, PRSO>

Looking at the object table (e.g. via infodump) shows that many objects have a property 12 which appears to correspond with relative sizes. I modified the Stationfall data file to swap the arguments to the relevant @get_prop and it works as expected: the ID card fits in the slot, and the watch and uniform don’t.

2 Likes

I just discovered this evening that exactly the same sort of bug exists in all versions of Beyond Zork, when you try to put something in the jar of bubble liquid.
I wonder how many others there are.