Last night, DF hung on me. It became completely unresponsive and was using 100% of one core of my CPU.
There were no indications as to why it hung.
I attached a debugger and did some analysis.
It was stuck in a tight loop with no valid exit condition.
The routine involved is the only one that uses the strings " has been possessed!" and " withdraws from society..." and " is taken by a fey mood!", so it is associated with the strange mood code.
Here's my analysis:
.text:005F2760 83 CE FF or esi, 0FFFFFFFFh
.text:005F2763 89 74 24 14 mov [esp+13Ch+var_128], esi
Loop preamble. Set a local variable to -1. This variable will be
cached in register ESI.
.text:005F2767 E8 B4 F1 1B 00 call sub_7B1920
Start of the loop.
The call target appears to be the Mersenne Twister random() function.
.text:005F276C 8B C8 mov ecx, eax
.text:005F276E B8 03 00 00 00 mov eax, 3
.text:005F2773 F7 E1 mul ecx
.text:005F2775 8B C1 mov eax, ecx
.text:005F2777 2B C2 sub eax, edx
.text:005F2779 D1 E8 shr eax, 1
.text:005F277B 03 C2 add eax, edx
.text:005F277D C1 E8 1E shr eax, 30
.text:005F2780 69 C0 FF FF FF 7F imul eax, 7FFFFFFFh
.text:005F2786 2B C8 sub ecx, eax
.text:005F2788 B8 FF FF FF BF mov eax, 0BFFFFFFFh
.text:005F278D F7 E1 mul ecx
.text:005F278F C1 EA 1D shr edx, 29
The mess above is a divide by a constant, done by multiplying by the
(fixed-point) reciprocal. I don't know what the constant is, save
that it is likely a very small number. I'm going to guess that it
is 3.
.text:005F2792 83 EA 00 sub edx, 0
.text:005F2795 74 23 jz short loc_5F27BA
.text:005F2797 83 EA 01 sub edx, 1
.text:005F279A 74 10 jz short loc_5F27AC
.text:005F279C 83 EA 01 sub edx, 1
.text:005F279F 75 20 jnz short loc_5F27C1
Then we check for the special values 0, 1, 2. This implies to me that
we care about the modulus of the division.
If we didn't get 0, 1, or 2, we exit the switch.
.text:005F27A1 38 54 24 27 cmp [esp+13Ch+var_115], dl
; DL known to be 0 here.
.text:005F27A5 74 1A jz short loc_5F27C1
.text:005F27A7 8D 72 0F lea esi, [edx+15]
; EDX known to be 0 here.
.text:005F27AA EB BB jmp short loc_5F2767
case 2: compare a byte-size local stack variable against 0.
If 0, exit the switch.
Else, store the value 15 in cached register variable ESI and loop.
---
.text:005F27AC 80 7C 24 26 00 cmp [esp+13Ch+var_116], 0
.text:005F27B1 74 0E jz short loc_5F27C1
.text:005F27B3 BE 0E 00 00 00 mov esi, 14
.text:005F27B8 EB AD jmp short loc_5F2767
case 1: compare a different byte-size local stack variable against 0.
If 0, exit the switch.
Else, store the value 14 in cached register variable ESI and loop.
---
.text:005F27BA BE 0D 00 00 00 mov esi, 13
.text:005F27BF EB A6 jmp short loc_5F2767
case 0: store the value 13 in cached register variable ESI and loop.
---
.text:005F27C1 66 83 FE FF cmp si, 0FFFFh
.text:005F27C5 75 A0 jnz short loc_5F2767
Loop exit condition check: is the cached register variable == -1 ?
So:
some_variable = -1;
do {
switch (mt_rand() % 3) {
case 0:
some_variable = 13;
continue;
case 1:
some_variable = 14;
if (a_condition) continue;
case 2:
some_variable = 15;
if (a_different_condition) continue;
}
} while (some_variable != -1);
This loop can never exit! In two ways: if both of the conditions are nonzero (TRUE), the switch will loop forever. (This occured in my case, and may be related to whatever triggers this loop.) Even if not, the do/while will loop forever.
The 13, 14, 15 magic numbers are probably enums or symbolic constants.
Also, the condition variables may be two members of a local strucure or object.
This code will be found about 2/3 of the distance between the start and end of the function.
I forced the loop to exit (by setting EIP past the loop) and the game continued normally with the message "
Eshtan Dodokzoluth, Peas has been possessed!"
I hope this is useful in tracking down whatever's going on here.