Monday, March 29, 2010

Bug-o-rama, encore...

Another day, another beautiful C++ bug. My program is a shareware Windows Mobile application. And it uses a unique device ID for the purposes of licensing and user identification. On newer devices (iPhone, Blackberry, WinMobile 5+) such a DeviceID is readily available. On older WM devices, however, it was an optional feature. I have several fallback mechanisms, the most basic of those would hash together several system-provided data structures (CPU info, OS version, etc.) and use the hash value as a DeviceID. This schema is somewhat open to collisions, but such devices are rare, so I'm not worried much about those. A false negative is worse. And I had a case of a device where such false negatives were happening all the time. You retrieve a (hash-based) DeviceID, store it, on the next run it's different.

Here's the deal. One of the sources for the hash was the PROCESSOR_INFO structure that the OS kernel (or maybe the OAL) readily provides. The structure consists of some DWORD's and some wide (wchar_t) strings:

typedef struct {
WORD wVersion;
WCHAR szProcessorCore[40];
WORD wCoreRevision;
WCHAR szProcessorName[40];
WORD wProcessorRevision;
WCHAR szCatalogNumber[100];
WCHAR szVendor[100];
DWORD dwInstructionSet;
DWORD dwClockSpeed;

If you look closely, you'll see that the data size all the way up to szVendor is 566 bytes - not a multiple of 4. And DWORDs have to be aligned on 4-byte boundary. So the in-memory layout of the structure includes two bytes of padding between szVendor and dwInstructionSet. And it happened so that on this particular device, the value of those two bytes would vary from call to call. So the hash was different, so the DeviceID would vary from run to run.

One would expect a straightforward implementation of IOCTL_PROCESSOR_INFORMATION to keep a copy of the struct somewhere in ROM, and memcpy() it into userspace on demand. Not on this device, though...

Once I'd zero out the padding, the app would perform as advertised. And there was, as they say, much rejoicing. Later on, I've encountered other bugs on this antiquated device, but that's a whole another story.