paint-brush
Steam chat message crash explained.by@jacksonroberts
6,857 reads
6,857 reads

Steam chat message crash explained.

by Jackson RobertsApril 18th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

About a day ago, a post was made on /r/Steam about a message that, when sent via Steam’s chat, would crash the recipient’s client.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Steam chat message crash explained.
Jackson Roberts HackerNoon profile picture

About a day ago, a post was made on /r/Steam about a message that, when sent via Steam’s chat, would crash the recipient’s client.

The bug has since been patched, so it’s now safe to delve into exactly how this exploit worked.

The crash message and its variants made heavy use of unicode combining characters, and were extremely long in length. The message is similar in appearance to Zalgo text, and its ability to cause a crash was likely discovered by accident while attempting to make something obnoxious.

The variation of the crash message I analyzed.

To figure out what was contained in this horrifying blob of text I wrote an extremely simple script:

The output of this script when provided the crash text was the following:

> python3 analyze.py

Length of message in bytes: 5135

Number of characters in message: 2044

LATIN SMALL LETTER M

LATIN SMALL LETTER E

LATIN SMALL LETTER O

LATIN SMALL LETTER W

Number of latin characters: 4

Number of combining characters: 2040


The message consisted of 2044 unicode characters. However, many of these characters were multi-byte combining marks so the total size of the message came to 5135 bytes.This message has an absolutely insane latin to combining character ratio. This message consisted of only four latin letters spelling the word “meow”. However, stacked on top of those letters are a whopping 2040 combining characters. This explains why this string of text will bog down any text renderer. Copy-paste it into Chrome, Notepad, or anywhere else you can enter text and it will produce significant lag due to the renderer having to draw such an extreme number of combining marks in such a small space. But they never crashed. The only software that completely crashed from displaying this message was Steam. Why?

Initially I hypothesized that the crash was due to the 2048 character limit in the Steam message client. This message occupies 5k bytes while only using 2k characters, so it fits in a single Steam message. Perhaps Steam was only allocating 2KB per chat message? Sending some messages from the Steam Web Client (which does not have the 2k character limit) to my desktop client proved this theory wrong. Steam could properly receive extremely long Unicode messages without crashing. In fact, the “meow” crash message was shorter than many messages that failed to crash the client.

I was stumped, so I messaged Valve employee Henry G. on reddit, and he responded with an excellent explanation of the bug. A very, very big thanks to him for taking the time to write such a great reply.

Valve’s Response

This is actually a bizarre and old bug that isn’t specific to sizes or lengths at all. It dates back to some very old code that we forgot about.

Back in the Windows 98 era, the Arial font didn’t support very many Unicode characters, but the Tahoma font (which was brand new) did. So way back when, maybe 12 years ago, someone who worked at Valve added a bit of logic inside our low-level font rendering to switch from Arial to Tahoma for certain Unicode characters and then switch back.

To perform the font switch, they made our “DrawText” routine recursive. It would draw text up until the switch, then call itself again with a different font name for the remaining characters.

What ends up happening in this exploit message (and I don’t think that the people who came up with it actually figured this out) is that many of the adjacent characters alternate from the Arial range into the Tahoma range and back. Because every font-switch is a recursive function call, we ended up making recursive function calls over a thousand levels deep. Eventually the program runs out of stack space and can’t call itself recursively any more, and crashes.

This was all a big surprise to us because we haven’t looked at the font rendering code in many years, and we had no idea that there was this recursive function call in it that someone had added just to fix a bug in the Windows 98 fonts!

It’s completely ridiculous, of course. Once we discovered the recursive code that crashed, we just removed it. Microsoft shipped better fonts back around Windows ME / Windows 2000. There hasn’t been any reason to do this weird font-splitting thing in over a decade, not since we stopped supporting Windows 98.

This chat crash could have happened at any time since Steam was originally released. But as far as we can tell, nobody found it until now! The recent trend of pasting weird-looking Unicode text happens to have triggered it by accident.

In the end, the crash was caused by a good old fashioned stack overflow! However, I was still curious why the Steam desktop client limited chat message length on PC to 2048 characters, so I asked Henry about that as well. He replied with the following:

I don’t think we have a good reason for the character limit, other than the fact that our text-entry input code is just as old as the Win98 font code, and was written with a bunch of arbitrary limits because that’s how the original author did it. The text-entry code has been on death’s door for as long as I’ve been at Valve (six years) and I hope that this is the year when we finally pull the trigger and delete all it. There have been rumors of a major client UI rewrite, and if that does happen, it will happen with a new web-based framework that uses none of the old legacy code … Or so I hope.

IIRC, we do enforce a server-side byte limit of 16kb mostly as a way to avoid having individual users suck up all our server memory.

In the end, this bizarre crash was caused by forgotten legacy code. Thanks again to Henry G. for elucidating the inner-workings of this bug.