RF455-049 RF 4.55: the client crashes on every login while the character has an item on the auction house (1E/19 names an item that isn't in the bag)
Game: RF 4.55 (Dragonborn), server v0.2.0, Windows build (stock gameserver.exe, SHA-256 96452fbd…).
Client: the supported Taiwan build, RF_Online.bin SHA-256 46528aac…2bbc558 (2025-07-16), Language=Taiwan.
Summary
- After a character registers an item on the auction house, the client closes itself on every world entry with that character. It closes right as the world finishes loading, with no message.
- The character can't play until the listing sells or expires. It can't cancel the listing, because it never gets into the world. Other characters on the account are fine.
- Cause: at world entry the server sends 1E/19
unmannedtrader_Regist_item_inform_zocl. The client looks up each entry's item in the bag and uses the result without checking it.- The server has already taken the item out of the bag, and names it with a fresh serial.
- Its entries are 18 bytes, and the client reads 23, so even the serial the client reads isn't the one sent.
- The lookup returns nothing, and the client reads through a null pointer.
Steps to reproduce
- Enter the world with any character, open the auction house at an NPC store, and register an item. For example, register a stack of 99 potions.
- The server logs
atrade register {"listing": 1, ...}, and the item leaves the bag. The DB has the listing inatrade_listingsand no row for the item incharacter_items. - Go back to character select, or log out, and enter the world with the same character.
- The client closes as the world loads. The server logs only the dropped connection:
client entered world {"session": 1, "name": "Helios", ...}
tcpnet: session 1 read error: ... wsarecv: An existing connection was forcibly closed by the remote host.
client disconnected {"session": 1, ...}- Every later attempt does the same (seen at 20:50:13 and 20:51:05).
What the server sends
(*Server).sendATradeWorldEntry (0x140760f40) builds one entry per open listing:
0x140761bc0:(*client).allocItemSerialhands out a new serial for the listed item;0x140761be6:(*Server).rememberATradeSerialrecords it. It's never added to the bag.0x140761405:protocol.ATradeRegistItemInform(0x14063cc60) packs it.
u8 count, then count × { u16 serial, u32, u32, u32, u32 } (18-byte entries)0x14063cd74 stores the word at body+1+18·i, then dwords at +3, +7, +0xB and +0xF.
What the client reads
The 1E/19 handler is at 0x59c250 in RF_Online.bin. It reads 23-byte entries (stride 0x17):
u8 count, then count × { u8 (+0), u16 item serial (+1), u32 (+3), u32 (+7), u32 (+0xB), ..., u16 (+0x13) }For each entry:
0x59c2c8:0x4fae40looks the serial up in the bag (board 0).- The result goes unchecked to the registered-items board's add,
0x4bf0a0(board0x1B). 0x4bf0d8: that calls0x412000, which reads[item + 0x248]. A null item crashes there: an access violation at0x41200f, reading0x248.
The client expects a registered item to still be in the bag, under the serial 1E/19 names.
Suggested fix
Keep a registered item in the bag, locked, under the serial that 1E/19 sends. Remove it when it sells; unlock it or return it when the listing is cancelled or expires. character_items.item_locked already exists.
If the item can't stay in the bag, leave such listings out of 1E/19. The listing still sells and expires; the client's auction window just won't show it as registered.
Workaround
A local client patch (atrade-missing-item, 2 sites) skips an entry whose bag lookup finds nothing. It does that
with a test/je stub in the int3 padding after the handler. With it, login works and the listing still sells or
expires, but a stock client still crashes, so the fix belongs on the server.
History
-
cybercyber