Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C++ C++/CLI C MFC ATL WTL STL
Please run the code below.
int main(int argc, char* argv[])
{
	int ii = 20;
	char tmp[32] = "loveing";
	printf("[%d]hello[%s]\n", tmp); //no integer fill the %d format

	char szData[52] = {0};
	sprintf(szData, "[%d]saying [%s]\n",ii);//no string fill the %s format
	printf("%s", szData);
	return 0;
}


The compiler don't complain in the compile phase, but when you press F5 to debug, 'unhandle exception' prompt. while not debug, run it at once, you just get wield strings or output.

We all use some sort of printf or CString::Format in the code, we are not careful sometimes. So how to handle this problem or check out the bugs in the exist project?
Posted 21-Aug-08 18:58pm
Edited 15-Oct-10 12:36pm
Yusuf40.1K
v2
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

Sorry for the long post, but here's my solution (there is a companion wide string set of functions as well.) The DT... is my library prefix.

There are similar looking functions in Microsoft safe string library, but I was able to cause every one of them to throw exceptions in common scenarios. This set of code had to run in a DLL which logged errors so it simple couldn't throw exceptions (though I suppose it could in very fringe cases.)

#include <span class="code-keyword"><stdarg.h></span>

inline
char* DTSafeStrCopyLen(LPSTR pDst, LPCSTR pSrc, int len)
{
	if (pDst)
	{
		if (pSrc && len > 0)
		{
			while (len && *pSrc)
			{
				*pDst++ = *pSrc++;
				len--;
			}
		}
		*pDst = 0;
	}
	return pDst;
}

inline
int DTSafeStrCopyLen2(LPSTR pDst, LPCSTR pSrc, int len)
{
	return DTSafeStrCopyLen(pDst, pSrc, len) - pDst;
}

#pragma warning(disable:4702) // unreachable code

int DTSafeFormatStringV(LPSTR pBuffer, int bufferLen, LPCSTR pFormat, va_list args)
{
	if (!pBuffer || bufferLen <= 0)
		return -1;

	int returnLen = 0;

	if (pFormat && *pFormat)
	{
#if _MSC_VER >= 8
		__try
#else
		try
#endif
		{
			returnLen = _vsnprintf(pBuffer, bufferLen, pFormat, args);
			if (returnLen < 0)
			{
				pBuffer[bufferLen - 1] = 0;
				returnLen = bufferLen - 1;
			}
		}
#if _MSC_VER >= 8
		__except (EXCEPTION_EXECUTE_HANDLER)
#else
		catch (...)
#endif
		{
			returnLen = DTSafeStrCopyLen2(pBuffer, "!exception thrown during formatting: \"", bufferLen);
			returnLen += DTSafeStrCopyLen2(&pBuffer[returnLen], pFormat, bufferLen - returnLen); 

			if (returnLen < bufferLen - 1)
			{
				pBuffer[returnLen++] = '"';
				pBuffer[returnLen] = 0;
			}
		}
	}
	else
	{
		pBuffer[0] = 0;
	}

	return returnLen;
}

int DTSafeFormatString(LPSTR pBuffer, int bufferLen, LPCSTR pFormat, ...)
{
	if (!pBuffer || bufferLen <= 0)
		return -1;

	int returnLen = 0;

	if (pFormat && *pFormat)
	{
		va_list args;
		va_start (args, pFormat);
		returnLen = DTSafeFormatStringV(pBuffer, bufferLen, pFormat, args);
		va_end (args);
	}
	else
	{
		pBuffer[0] = 0;
	}

	return returnLen;
}


  Permalink  
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

Static code analysis tools like Lint and the the one that ships with VS2010 Ultimate, might help you there, but I would say experience is your best friend. With experience you know which constructs to avoid, and which needs extra care. Format specifiers floating around inside a string of characters is one such thing. The example you are giving is maybe not the most deceitful one. Imagine what can happen if the format string itself comes from an extern source, like a language resource DLL, which can be translated by someone else. In this case it's almost impossible to protect yourself.

Use streams wherever possible, which is the safe alternative. And if you need C-style format strings, make sure they are covered in a code review, if written by an unexperienced programmer.

Edit: Missed the date on this one. Why did you bump it Yusuf?
  Permalink  
v2
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 4

If you're using C++ there's no reason to use printf or the whole god awful family of C relics. Use a stream. Then you get type safety and you won't try reading return addresses off the stack to interpret as strings. Consider rewriting along the lines of:

int main()
{
    int d = 20;
    const std::string s( "Good Looking!" );
 
    std::cout << "[" << d << "] Hello " << s << std::endl;
}
You'll find the code's as fast, probably faster and you've less chance of messing it up as the compiler will tell you.

Cheers,

Ash

PS: Missed the date as well... Talking to the dead isn't normally my thing.
  Permalink  
v2
Comments
paul_71 at 17-Oct-10 4:20am
   
Added upvote since of all proposed solutions this is the only one I'd really recommend. Though I appriciate the effort invested by @Joe, I'm sorry to say that, don't do it. You just replace one possible source of errors with another one.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 3

Perform Code Analysis with your code with
/analyze
switch with your Visual C++ compiler. You need to have Team System or the Ultimate version to do that.
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 OriginalGriff 325
1 Sergey Alexandrovich Kryukov 133
2 Rana Waqas 105
3 Sebastiaan Meijerink 105
4 Abhinav S 99
0 Sergey Alexandrovich Kryukov 6,558
1 OriginalGriff 6,268
2 Peter Leow 2,534
3 Abhinav S 2,344
4 Maciej Los 2,297


Advertise | Privacy | Mobile
Web02 | 2.8.150414.1 | Last Updated 17 Oct 2010
Copyright © CodeProject, 1999-2015
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100