charstring version, respectively. If you’ve done any development on Windows systems, you’ll definitely be no stranger to wide character strings.
int) from the packet and performs a bounds check. If the check passes, a string of the specified length (assumed to be a UTF-16 string) is copied from the packet buffer to a fresh wide
chararray on heap.
[ … ]
if(packet->dataLen > 34 || packet->dataLen < sizeof(wchar_t)) bailout_and_exit();
size_t bufLen = packet->dataLen / sizeof(wchar_t);
wchar_t *appData = new wchar_t[bufLen];
memcpy(appData, packet->payload, packet->dataLen);
[ … ]
chararray. But consider what would happen if
packet->dataLenwas an odd number. For example, if
packet->dataLen = 11, we end up with
size_t bufLen = 11 / 2 = 5since the remainder of the division will be discarded.
memcpy()copies 11 bytes. Since five wide chars on Windows is 10 bytes (and 11 bytes are copied), we have an off-by-one overflow. To avoid this, the modulo operator should be used to check that
packet->dataLenis even to begin with; that is:
if(packet->dataLen % 2) bailout()
len + 1is used instead of the
len + 2that is required to account for the extra NULL byte(s) needed to terminate wide
chararrays, for example:
int alloc_len = len + 1;
wchar_t *buf = (wchar_t *)malloc(alloc_len);
memset(buf, 0x00, len);
wcsncpy(buf, srcBuf, len);
lenwide chars in it, all of these would be copied into
wcsncpy()would not NULL terminate
buf. With normal character arrays, the added byte (which will be a NULL because of the
memset) would be the NULL terminator and everything would be fine. But since wide
charstrings need either a two- or four-byte NULL terminator (Windows and UNIX, respectively), we now have a non-terminated string that could cause problems later on.
int destLen = (stringLen * (sizeof(wchar_t)) + sizeof(wchar_t);
wchar_t *destBuf = (wchar_t *)malloc(destLen);
MultiByteToWideChar(CP_UTF8, 0, srcBuf, stringLen, destBuf, destLen);
[ do something ]
MultiByteToWideAPI is documented at <http://msdn.microsoft.com/en-us/library/windows/desktop/dd319072%28v=vs.85%29.aspx>.
MultiByteToWideCharis the length of the destination buffer in wide characters, not in bytes, as the call above was done. Our destination length is out by a factor of two here (or four on UNIX-like systems, generally) and ultimately we can end up overrunning the buffer. These sorts of mistakes result in overflows and they’re surprisingly common.
charstring functions, like
wcsncpy(), for example:
unsigned int destLen = (stringLen * sizeof(wchar_t)) + sizeof(wchar_t);
memset(destBuf, 0x00, destLen);
wcsncpy(destBuf, srcBuf, sizeof(destBuf));
sizeof(destuff)for maximum destination size would be fine if we were dealing with normal characters, this doesn’t work for wide character buffers. Instead,
sizeof(destBuf)will return the number of bytes in
destBuf, which means the
wcsncpy()call above it can end up copying twice as many bytes to
destBufas intended—again, an overflow.
charequivalent string manipulation functions are also prone to misuse in the same ways as their normal
charcounterparts—look for all the wide
charequivalents when auditing such functions as
wcsncpy, etc. There also are a few wide
char-specific APIs that are easily misused; take, for example,
, whichconverts a wide
charstring to a multi-byte string. The prototype looks like this:
size_t wcstombs(char *restrict s, const wchar_t *restrict pwcs, size_t n);
nbytes have been written to
sor when a NULL terminator is encountered in
pwcs(the source buffer). If an error occurs, i.e. a wide
pwcscan’t be converted, the conversion stops and the function returns
(size_t)-1, else the number of bytes written is returned. The MSDN considers
wcstombs()to be deprecated, but there are still a few common ways to mess when using it, and they all revolve around not checking return values.
i = wcstombs( … ) // wcstombs() can return -1
buf[i] = L'';
n, the destination buffer won’t be NULL terminated, so any string operations later carried out on or using the destination buffer could run past the end of the buffer. Two possible consequences are a potential page fault if an operation runs off the end of a page or potential memory corruption bugs, depending on how
destbufis usedlater . Developers should avoid
wcstombs_s()or another, safer alternative. Bottom line: always read the docs before using a new function since APIs don’t always do what you’d expect (or want) them to do.
charfunctions. A good example would be incorrectly using
strlen()on a wide character string instead of
wcharstrings are chock full of NULL bytes,
strlen()isn’t going to return the length you were after. It’s easy to see how this can end up causing security problems if a memory allocation is done based on a
strlen()that was incorrectly performed on a wide char array.
wchars. In the examples above, I have assumed
sizeof(wchar_t) = 2; however, as I’ve said a few times, this is NOT necessarily the case at all, since many UNIX-like systems have
sizeof(wchar_t) = 4.
wchar_t *destBuf = (wchar_t *)malloc(32 * 2 + 2);
wcsncpy(destBuf, srcBuf, 32);
destBufffor 32 wide
chars + NULL terminator (66 bytes). But as soon as you run this on a Linux box—where wide
chars are four bytes—you’re going to get
4 * 32 + 2 = 130bytes and resulting in a pretty obvious overflow.
sizeof(wchar_t)to find out.
charfunctions, and ensure the math is right when allocating and copying memory. Make sure you check your return values properly and, most obviously, read the docs to make absolutely sure you’re not making any mistakes or missing something important.