宽字W串中的每个字符??个字节(依Compiler使用?/span>字符?/span>~码而定Q,因此q里?copy 7个字节显然是不够的,应该? * sizeof(wchar_t)?/p>
[#9] CMake目 – 循环內的数组界
static const struct {
DWORD winerr;
int doserr;
} doserrors[] =
{
…
};
static void
la_dosmaperr(unsigned long e)
{
…
for (i = 0; i < sizeof(doserrors); i++)
{
if (doserrors[i].winerr == e)
{
errno = doserrors[i].doserr;
return;
}
}
…
}
作者原本意图la_dosmaperr中for循环的次数等于数l的元素个数Q但sizeof(doserrors)q回的却是数l占用的字节个数Q这q远大于数组元素个数Q因此造成数组界。正的写法Q?/p>
for (i = 0; i < sizeof(doserrors) / sizeof(*doserrors); i++)
[#10] CPU Identifying Tool目 – 打印到自w的字符?/p>
char * OSDetection ()
{
…
sprintf(szOperatingSystem,
"%sversion %d.%d %s (Build %d)",
szOperatingSystem,
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.szCSDVersion,
osvi.dwBuildNumber & 0xFFFF);
…
sprintf (szOperatingSystem, "%s%s(Build %d)",
szOperatingSystem, osvi.szCSDVersion,
osvi.dwBuildNumber & 0xFFFF);
…
}
通过sprintfQ?span style="color: red">szOperatingSystem字符串将自己打印到自己里面,q是十分危险?/span>Q将D无法预知的错误结果,可能?x)导致栈溢出{严重问题?/p>
[#12] Notepad++目 – 数组局部clear
#define CONT_MAP_MAX 50
int _iContMap[CONT_MAP_MAX];
…
DockingManager::DockingManager()
{
…
memset(_iContMap, -1, CONT_MAP_MAX);
…
}
代码的原本试囑ְ数组_iContMap清零Q但memset的第三个参数CONT_MAP_MAXq不能代表数l的真正大小Q?span style="color: red">而只是数l的元素个数而已Q显然其忘记乘以sizeof(int)了?/span>
二、未定义行ؓ(f)
在C/C++的语a规范中,我们常常能看?#8220;xx is undefined”。规范中q没有明表明这c错误是什么样子的Q只是说取决于Compiler的实玎ͼ也许Compiler?x)给出正的l果Q但q么使用却是不可UL的?/p>
[#1] Chromium目 – 指针的误?/p>
void AccessibleContainsAccessible(…)
{
…
auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
…
}
q里的问题在于用new[]分配的内存,在智能指针释放时却用了deleteQ这会(x)D未定义行为?/span>看看autoptr的destructorq道了Q?/p>
~auto_ptr() {
delete _Myptr;
}
我们可以找一些更合适的cLfixq个问题Q比如boost::scopedarray?/p>
[#2] IPP Sample目 – l典未定义行?/p>
template<typename T, Ipp32s size> void HadamardFwdFast(…)
{
Ipp32s *pTemp;
…
for(j=0;j<4;j++) {
a[0] = pTemp[0*4] + pTemp[1*4];
a[1] = pTemp[0*4] – pTemp[1*4];
a[2] = pTemp[2*4] + pTemp[3*4];
a[3] = pTemp[2*4] – pTemp[3*4];
pTemp = pTemp++;
…
}
…
}
很多Z眼就看到?pTemp = pTemp++"q行Q?/span>对于q个代码~译器会(x)产生两种l果截然不同的翻译:(x)
pTemp = pTemp + 1;
pTemp = pTemp;
?/p>
TMP = pTemp;
pTemp = pTemp + 1;
pTemp = TMP;
到底是哪U呢Q依赖于~译器的实现Q甚x优化U别的设定?/p>
三、与q算优先U相关的错误
[#1] MySQL工程 – !?amp;的运优先
int ha_innobase::create(…)
{
…
if (srv_file_per_table
&& !mysqld_embedded
&& (!create_info->options & HA_LEX_CREATE_TMP_TABLE)) {
…
}
q段代码原意是想试create_info->options变量中几个bit位的值是否set了,?(create_info->options & HA_LEX_CREATE_TMP_TABLE)Q但׃!的运优先高于&Q实际逻辑变成?!create_info->options) & HA_LEX_CREATE_TMP_TABLE了。如果想要这D代码如期工作,׃要吝啬小括号了?/p>
[#2] Emule工程 – *?+的运优先
STDMETHODIMP
CCustomAutoComplete::Next(…, ULONG *pceltFetched)
{
…
if (pceltFetched != NULL)
*pceltFetched++;
…
}
昄作者原意是惛_pceltFetched所指向的long型变量进?+操作Q但׃*?+的运优先没有搞对Q导致实际上执行?(pceltFetched++)的操作,而不?*pceltFetched)++操作?/p>
[#3] Chromium目 – &?=的运优先
#define FILE_ATTRIBUTE_DIRECTORY 0×00000010
bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
…
info->is_directory =
file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0;
…
}
q个E序员的意图是通过试file_info.dwFileAttributes的几个bit位的值来判定是否是目录,逻辑上应该是(file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0Q但׃!=优先U高?amp;Q原代码中无括号Q结果逻辑变成了file_info.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY != 0)Q导致is_directory永q求gؓ(f)true?/p>
[#4] BCmenu目 – if和else弄
void BCMenu::InsertSpaces(void)
{
if(IsLunaMenuStyle())
if(!xp_space_accelerators) return;
else
if(!original_space_accelerators) return;
…
}
q又是C语言的一?#8220;大坑”Q无奈这个BCMenu目的程序员掉坑里了。虽然从代码~进上来看,elseg是与最外层的if配对使用Q但实际q段代码的效果是Q?/p>
if(IsLunaMenuStyle())
{
if(!xp_space_accelerators) {
return;
} else {
if(!original_space_accelerators) return;
}
}
q显然不是程序员原意Q看来括号必要时q是不能省略的。修改后的代码如下:(x)
if(IsLunaMenuStyle()) {
if(!xp_space_accelerators) return;
} else {
if(!original_space_accelerators) return;
}
四、格式化输出错误
[#1] ReactOS目 – 错误地输出WCHAR字符
static void REGPROC_unescape_string(WCHAR* str)
{
…
default:
fprintf(stderr,
"Warning! Unrecognized escape sequence: \\%c'\n",
str[str_idx]);
…
}
%c是用来格式化输出非宽字符的,q里用来输出WCHAR昄?x)得到错误的l果Q?span style="color: red">fix solution是将%c换位%C?/span>
[#2] Intel AMT SDK目 – ~少%s 【VS2010Q运行可能会(x)崩溃?/span>
void addAttribute(…)
{
…
int index = _snprintf(temp, 1023,
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
"%02x%02x:02x%02x:%02x%02x:%02x%02x",
value[0],value[1],value[2],value[3],value[4],
value[5],value[6],value[7],value[8],
value[9],value[10],value[11],value[12],
value[13],value[14],value[15]);
…
}
不解释了Q自己慢慢数和对照吧?/p>
[#3] Intel AMT SDK目 – 未用的参数
bool GetUserValues(…)
{
…
printf("Error: illegal value. Aborting.\n", tmp);
return false;
}
?span style="color: red">然tmp是多余的?/span>
五、书写错?/strong>
[#1] Miranda IM目 – 在if中赋?/p>
void CIcqProto::handleUserOffline(BYTE *buf, WORD wLen)
{
…
else if (wTLVType = 0×29 && wTLVLen == sizeof(DWORD))
…
}
“wTLVType = 0×29”昄是笔误,应该?#8220;wTLVType == 0×29”才对?/p>
[#3] Clang目 – 对象名书写错?/p>
static Value *SimplifyICmpInst(…) {
…
case Instruction::Shl: {
bool NUW =
LBO->hasNoUnsignedWrap() && LBO->hasNoUnsignedWrap();
bool NSW =
LBO->hasNoSignedWrap() && RBO->hasNoSignedWrap();
…
}
从最后一行先后用了LBO和RBO来看Q前面只用了LBO的那行很可能是有问题的,正确的应该是Q?/span>
bool NUW =
LBO->hasNoUnsignedWrap() && RBO->hasNoUnsignedWrap();
[#6] G3D Content Pak目 – 一Ҏ(gu)h错了地方
bool Matrix4::operator==(const Matrix4& other) const {
if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
return true;
}
…
}
׃括号N了地方,Dmemcmp最后的参数变成了sizeof(Matrix4) == 0Q这行代码的正确写法应该是:(x)
if (memcmp(this, &other, sizeof(Matrix4)) == 0) {
[#8] Apache Http Server目 – 多余的sizeof
PSECURITY_ATTRIBUTES GetNullACL(void)
{
PSECURITY_ATTRIBUTES sa;
sa = (PSECURITY_ATTRIBUTES)
LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));
…
}
最后一行显然是W误Q?span style="color: red">sizeof(sizeof(SECURITY_ATTRIBUTES))应该写ؓ(f)sizeof(SECURITY_ATTRIBUTES)才对?/span>
[#10] Notepad++目 – 在本来应该用&的地方用了&&
TCHAR GetASCII(WPARAM wParam, LPARAM lParam)
{
…
result=ToAscii(wParam,
(lParam >> 16) && 0xff, keys,&dwReturnedValue,0);
…
}
(lParam >> 16) && 0xff没有什么意义,求值结果Ltrue。这里的代码应该?lParam >> 16) & 0xff?/span>
[#12] Fennec Media Project目 – 额外的分?/p>
int settings_default(void)
{
…
for(i=0; i<16; i++);
for(j=0; j<32; j++)
{
settings.conversion.equalizer_bands.boost[i][j] = 0.0;
settings.conversion.equalizer_bands.preamp[i] = 0.0;
}
}
q又是一个实际逻辑与代码羃q不W的例子。作者的原意是这L(fng)Q?/p>
for(i=0; i<16; i++)
{
for(j=0; j<32; j++)
{
settings.conversion.equalizer_bands.boost[i][j] = 0.0;
settings.conversion.equalizer_bands.preamp[i] = 0.0;
}
}
但实际执行代码逻辑却是Q?/p>
for(i=0; i<16; i++)
{
;
}
for(j=0; j<32; j++)
{
settings.conversion.equalizer_bands.boost[i][j] = 0.0;
settings.conversion.equalizer_bands.preamp[i] = 0.0;
}
q一切都是那?D的?/p>
六、对基本函数和类的误?/strong>
[#2] TortoiseSVN目 – remove函数的误?/p>
STDMETHODIMP CShellExt::Initialize(….)
{
…
ignoredprops = UTF8ToWide(st.c_str());
// remove all escape chars ('\\')
std::remove(ignoredprops.begin(), ignoredprops.end(), '\\');
break;
…
}
作者意囑ֈ除所?\\'Q但他用错了函数Qremove函数只是交换元素的位|?/span>Q将要删除的元素交换到尾部trashQƈ且返回指向trash首地址的iterator。正的做法应该?span style="color: red">"v.erase(remove(v.begin(), v.end(), 2), v.end())"?/span>
[#5] Pixie目 – 在@环中使用alloca函数
inline void triangulatePolygon(…) {
…
for (i=1;i<nloops;i++) {
…
do {
…
do {
…
CTriVertex *snVertex =
(CTriVertex *)alloca(2*sizeof(CTriVertex));
…
} while(dVertex != loops[0]);
…
} while(sVertex != loops[i]);
…
}
…
}
alloca函数在栈上分配内存,因此在@环中使用alloca可能?x)很快导致栈溢出?/span>
七、无意义的代?/strong>
[#1] IPP Samples目 – 不完整的条g
void lNormalizeVector_32f_P3IM(Ipp32f *vec[3],
Ipp32s* mask, Ipp32s len)
{
Ipp32s i;
Ipp32f norm;
for(i=0; i<len; i++) {
if(mask<0) continue;
norm = 1.0f/sqrt(vec[0][i]*vec[0][i]+
vec[1][i]*vec[1][i]+vec[2][i]*vec[2][i]);
vec[0][i] *= norm; vec[1][i] *= norm; vec[2][i] *= norm;
}
}
mask是Ipp32scd指针Q这?span style="color: red">if (mask< 0)
q句代码昄没啥意义Q正的代码应该是:(x)
if (mask[i] < 0) continue;
[#2] QT目 – 重复的检?/p>
Q3TextCustomItem* Q3TextDocument::parseTable(…)
{
…
while (end < length
&& !hasPrefix(doc, length, end, QLatin1String("</td"))
&& !hasPrefix(doc, length, end, QLatin1String("<td"))
&& !hasPrefix(doc, length, end, QLatin1String("</th"))
&& !hasPrefix(doc, length, end, QLatin1String("<th"))
&& !hasPrefix(doc, length, end, QLatin1String("<td"))
&& !hasPrefix(doc, length, end, QLatin1String("</tr"))
&& !hasPrefix(doc, length, end, QLatin1String("<tr"))
&& !hasPrefix(doc, length, end, QLatin1String("</table"))) {
…
}
q里?<td"做了两次check?/p>
八、LTrue或False的条?/strong>
[#1] Shareaza目 – charcd的D?/span>
void CRemote::Output(LPCTSTR pszName)
{
…
CHAR* pBytes = new CHAR[ nBytes ];
hFile.Read( pBytes, nBytes );
…
if ( nBytes > 3 && pBytes[0] == 0xEF &&
pBytes[1] == 0xBB && pBytes[2] == 0xBF )
{
pBytes += 3;
nBytes -= 3;
bBOM = true;
}
…
}
表达?pBytes[0] == 0xEF"LFalse。charcd的D围是-128~127 < 0xEFQ因此这个表辑ּLFalseQ导致整个if conditionL为FalseQ与预期逻辑不符?/p>
[#3] VirtualDub目 – 无符L(fng)型L>=0
typedef unsigned short wint_t;
…
void lexungetc(wint_t c) {
if (c < 0)
return;
g_backstack.push_back(c);
}
c是unsigned shortcdQ永q不?x)小?,也就是说if (c < 0)永远为False?/p>
[#8] MySQL目 – 条g错误
enum enum_mysql_timestamp_type
str_to_datetime(…)
{
…
else if (str[0] != ‘a’ || str[0] != 'A')
continue; /* Not AM/PM */
…
}
if (str[0] != ‘a’ || str[0] != 'A')q个条g永远为真。也许这块本意是想用&&?/span>
?ji)、代码漏z?/strong>
D漏洞的代码错误实际上也都是笔误、不正确的条件以?qing)不正确的数l操作等。但q里q是惛_一些特定错误划归ؓ(f)一c,因ؓ(f)入R者可以利用这些错误来d你的代码Q获取其利益?/p>
[#1] Ultimate TCP/IP目 – I字W串的错误检?/p>
char *CUT_CramMd5::GetClientResponse(LPCSTR ServerChallenge)
{
…
if (m_szPassword != NULL)
{
…
if (m_szPassword != '\0')
{
…
}
W二个if condition check意图查m_szPassword是否为空字符Ԍ但却错误的将指针?\0'q行比较Q正的代码应该是这L(fng)Q?/p>
if (*m_szPassword != '\0')
[#2] Chromium目 – NULL指针的处?/p>
bool ChromeFrameNPAPI::Invoke(…)
{
ChromeFrameNPAPI* plugin_instance =
ChromeFrameInstanceFromNPObject(header);
if (!plugin_instance &&
(plugin_instance->automation_client_.get()))
return false;
…
}
一旦plugin_instance为NULLQ?plugin_instance为TrueQ代码对&&后面的子条g求|引用plugin_instance导致程序崩溃。正的做法应该是:(x)
if (plugin_instance &&
(plugin_instance->automation_client_.get()))
return false;
[#5] Apache httpd Server目 – 不完整的~冲区clear
#define MEMSET_BZERO(p,l) memset((p), 0, (l))
void apr__SHA256_Final(…, SHA256_CTX* context) {
…
MEMSET_BZERO(context, sizeof(context));
…
}
q个错误前面提到q,sizeof(context)只是指针的大,之改ؓ(f)sizeof(*context)O(jin)K了?/p>
[#7] PNG Library目 – 意外的指针clear
png_size_t
png_check_keyword(png_structp png_ptr, png_charp key,
png_charpp new_key)
{
…
if (key_len > 79)
{
png_warning(png_ptr, "keyword length must be 1 – 79 characters");
new_key[79] = '\0';
key_len = 79;
}
…
}
new_key的类型ؓ(f)png_charppQ顾名思义Q这是一个char**cdQ但代码中new_key[79] = ‘\0′q句昄是要l某个char赋|但new_key[n]得到的应该是一个地址Q给一个地址赋gؓ(f)’\0′昄是有误的。正的写法应该?*new_key)[79] = '\0'?/span>
[#10] Miranda IM目 – 保护没生?/p>
void Append( PCXSTR pszSrc, int nLength )
{
…
UINT nOldLength = GetLength();
if (nOldLength < 0)
{
// protects from underflow
nOldLength = 0;
}
…
}
nOldLength椒UINTcdQ?span style="color: red">其值永q不?x)小?,因此if (nOldLength < 0)q行成了摆设?/span>
[#12] Ultimate TCP/IP目 – 不正的循环l束条g
void CUT_StrMethods::RemoveSpaces(LPSTR szString) {
…
size_t loop, len = strlen(szString);
// Remove the trailing spaces
for(loop = (len-1); loop >= 0; loop–) {
if(szString[loop] != ' ')
break;
}
…
}
?span style="color: red">环中的结束条件loop >= 0永qؓ(f)TrueQ因为loop变量的类型是size_t是unsignedcdQ永q不?x)小??/span>
十、拷贝粘?/strong>
和笔误不同,E序员们决不因该低估拯_脓(chung)问题Q这c问题发生了太多。程序员们花费了大量旉在这些问题的debug上?/p>
[#1] Fennec Media Project目 – 处理数组元素时出?/p>
void* tag_write_setframe(char *tmem,
const char *tid, const string dstr)
{
…
if(lset)
{
fhead[11] = '\0';
fhead[12] = '\0';
fhead[13] = '\0';
fhead[13] = '\0';
}
…
}
咋看一下,fhead[13]做了两次赋|g没啥问题。但仔细想一下,最后那行程序员的原意极可能是想写fhead[14] = '\0'。问题就在这里了?/p>
[#2] MySQL目 – 处理数组元素时出?/p>
static int rr_cmp(uchar *a,uchar *b)
{
if (a[0] != b[0])
return (int) a[0] – (int) b[0];
if (a[1] != b[1])
return (int) a[1] – (int) b[1];
if (a[2] != b[2])
return (int) a[2] – (int) b[2];
if (a[3] != b[3])
return (int) a[3] – (int) b[3];
if (a[4] != b[4])
return (int) a[4] – (int) b[4];
if (a[5] != b[5])
return (int) a[1] – (int) b[5];
if (a[6] != b[6])
return (int) a[6] – (int) b[6];
return (int) a[7] – (int) b[7];
}
~写q类代码Ӟ我猜l大多数Z(x)选择Copy-PasteQ然后再逐行修改Q问题就发生在修改过E中Q上面的代码中当处理a[5] != b[5]时就忘记修改一个下标了Qreturn (int) a[1] – (int) b[5];昄q里的正代码应该是return (int) a[5] – (int) b[5]?/p>
[#3] TortoiseSVN目 文g名不正确
BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS)
{
return(GetInMemoryFileVersion(("DBGHELP.DLL"),
dwMS,
dwLS)) ;
}
BOOL GetDbgHelpVersion(DWORD &dwMS, DWORD &dwLS)
{
return(GetInMemoryFileVersion(("DBGHELP.DLL"),
dwMS,
dwLS)) ;
}
GetImageHlpVersion和GetDbgHelpVersion都用了"DBGHELP.DLL"文gQ显然GetImageHlpVersion写错文g名了。应该用"IMAGEHLP.DLL"对了?/span>
[#4] Clang目 – {同的函C
MapTy PerPtrTopDown;
MapTy PerPtrBottomUp;
void clearBottomUpPointers() {
PerPtrTopDown.clear();
}
void clearTopDownPointers() {
PerPtrTopDown.clear();
}
我们看到虽然两个函数名不同,但是函数体的内容是相同的Q显然又是copy-paste惹的。做如下修改卛_Q?/p>
void clearBottomUpPointers() {
PerPtrBottomUp.clear();
}
十一、Null指针的校验迟?/strong>
q里?#8220;q了”的含义是先用指针,然后再校验指针是否ؓ(f)NULL?/p>
[#1] Quake-III-Arena目 – 校验q了
void Item_Paint(itemDef_t *item) {
vec4_t red;
menuDef_t *parent = (menuDef_t*)item->parent;
red[0] = red[3] = 1;
red[1] = red[2] = 0;
if (item == NULL) {
return;
}
…
}
?span style="color: red">校验item是否为NULL前已l用过item了,一旦item真的为NULLQ那E序必然崩溃?/span>
十二、其他杂?/strong>
[#1] Image Processing 目 – 八进制数
inline
void elxLuminocity(const PixelRGBus& iPixel,
LuminanceCell< PixelRGBus >& oCell)
{
oCell._luminance = uint16(0.2220f*iPixel._red +
0.7067f*iPixel._blue + 0.0713f*iPixel._green);
oCell._pixel = iPixel;
}
inline
void elxLuminocity(const PixelRGBi& iPixel,
LuminanceCell< PixelRGBi >& oCell)
{
oCell._luminance = 2220*iPixel._red +
7067*iPixel._blue + 0713*iPixel._green;
oCell._pixel = iPixel;
}
W二个函敎ͼE序员原意是使用713q个十进制整敎ͼ?713 != 713Q在C中,0713是八q制的表C法QCompiler?x)认是个八进制数?/p>
[#2] IPP Sample工程 – 一个变量用于两个loop?/p>
JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void)
{
…
for(c = 0; c < m_scan_ncomps; c++)
{
block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU)));
// skip any relevant components
for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++)
{
block += (DCTSIZE2*m_ccomp
[/c]
.m_nblocks);
}
…
}
变量c用在了两个loop中,q会(x)D只有部分数据被处理,或外部@环中止?/p>
[#3] Notepad++目 – 怪异的条件表辑ּ
int Notepad_plus::getHtmlXmlEncoding(….) const
{
…
if (langT != L_XML && langT != L_HTML && langT == L_PHP)
return -1;
…
}
代码中的那行if条g{h(hun)?span style="color: red"> if (langT == L_PHP)Q显
然似乎不是作者原意,猜测正确的代码应该是q样的:(x)