• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            glxhyt

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              15 隨筆 :: 0 文章 :: 4 評(píng)論 :: 0 Trackbacks
            http://www.who1753.com/100-bugs-in-c-cpp-opensource-projects/

            俄羅斯OOO Program Verification Systems公司用自己的靜態(tài)源碼分析產(chǎn)品PVS-Studio對(duì)一些知名的C/C++開源項(xiàng)目,諸如Apache Http ServerChromiumClangCMakeMySQL等的源碼進(jìn)行了分析,找出了100個(gè)典型的Bugs。個(gè)人覺得這份列表對(duì)C/C++ 程序員有一定參考意義。與其說事后用靜態(tài)工具分析,倒不如在編碼時(shí)就提高自知自覺,避免這份列表上的錯(cuò)誤發(fā)生在你的代碼中,因此這里將部分摘錄一些Bugs(Bug編號(hào)這里不連續(xù),為的是對(duì)應(yīng)原文的編號(hào))并做簡(jiǎn)要說明。原文將這份Bug列表分為了幾類,這里也將沿用這個(gè)思路。

            一、數(shù)組和字符串處理錯(cuò)誤

            數(shù)組和字符串處理錯(cuò)誤是C/C++程序中最多的一類缺陷類型。這也可以看作是我們?yōu)閾碛懈咝У氐讓觾?nèi)存操作能力而付出的代價(jià)。

            [#1] Wolfenstein 3D項(xiàng)目 -"只有部分對(duì)象被clear了"

            1
            2
            3
            4
            5
            6
            7
            8
            void CG_RegisterItemVisuals( int itemNum )
            {
                   
                  itemInfo_t *itemInfo;
                  
                  memset( itemInfo, 0, sizeof( &itemInfo ) ); 
                  
            }

            這里的Bug出現(xiàn)在memset那一行。代碼的真實(shí)意圖是clear iteminfo這塊內(nèi)存,但調(diào)用memset時(shí),第三個(gè)參數(shù)傳入的卻是sizeof(&iteminfo),要知道 sizeof(&itemInfo) != sizeof(itemInfo_t),前者只是一個(gè)指針的大小罷了。正確的寫法是:

            memset(itemInfo, 0, sizeof(itemInfo_t)); 或memset(itemInfo, 0, sizeof(*itemInfo));

            [#2] Wolfenstein 3D項(xiàng)目 -"只有部分Matrix被clear了"

            ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
            memcpy( mat, src, sizeof( src ) );
            }

            這里的Bug出現(xiàn)在memcpy一行。程序的原意是將clear src[3][3]這個(gè)二維數(shù)組。但這里有個(gè)坑:那就是作為函數(shù)形式參數(shù)的數(shù)組名已經(jīng)退化為指針了,對(duì)其sizeof只能得到一個(gè)指針的長(zhǎng)度,因此這里的 memcpy只是copy了一個(gè)指針的長(zhǎng)度,沒有copy全。這里的代碼是C++代碼,原文中給出了正確的改正方法 – 傳reference:

            ID_INLINE mat3_t::mat3_t( float (&src)[3][3] )
            {
            memcpy( mat, src, sizeof( src ) );
            }

            [#4] ReactOS項(xiàng)目 – "錯(cuò)誤地計(jì)算一個(gè)字符串的長(zhǎng)度"

            static const PCHAR Nv11Board = "NV11 (GeForce2) Board";
            static const PCHAR Nv11Chip = "Chip Rev B2";
            static const PCHAR Nv11Vendor = "NVidia Corporation";

            BOOLEAN
            IsVesaBiosOk(…)
            {

            if (!(strncmp(Vendor, Nv11Vendor, sizeof(Nv11Vendor))) &&
            !(strncmp(Product, Nv11Board, sizeof(Nv11Board))) &&
            !(strncmp(Revision, Nv11Chip, sizeof(Nv11Chip))) &&
            (OemRevision == 0×311))

            }

            Bug處在IsVesaBiosOK中那一串strncmp調(diào)用中,代碼將一個(gè)指針的size傳入strncmp作為第三個(gè)參數(shù),導(dǎo)致 strncmp實(shí)際只是比較了字符串的前4 or 8個(gè)字節(jié),而不是字符串的全部?jī)?nèi)容。

            [#6] CPU Identifying Tool項(xiàng)目 – 數(shù)組越界

            #define FINDBUFFLEN 64  // Max buffer find/replace size

            int WINAPI Sticky (…)
            {

            static char findWhat[FINDBUFFLEN] = {'\0'};

            findWhat[FINDBUFFLEN] = '\0';

            }

            bug出在"findWhat[FINDBUFFLEN] = ‘\0′;”這一行。數(shù)組的最大長(zhǎng)度為FINDBUFFLEN,但下標(biāo)的最大值應(yīng)該是FINDBUFFLEN-1,而不是FINDBUFFLEN。因此這 行代碼顯然應(yīng)該改為findWhat[FINDBUFFLEN-1] = '\0';

            [#7] Wolfenstein 3D項(xiàng)目 – 數(shù)組越界

            typedef struct bot_state_s
            {

            char teamleader[32]; //netname of the team leader

            }  bot_state_t;

            void BotTeamAI( bot_state_t *bs ) {

            bs->teamleader[sizeof( bs->teamleader )] = '\0';

            }

            "sizeof( bs->teamleader )]"這行的結(jié)果值已經(jīng)超出了數(shù)組的最大邊界,正確的代碼是:

            bs->teamleader[
            sizeof(bs->teamleader) / sizeof(bs->teamleader[0]) – 1
            ] = '\0';

            [#8] Miranda IM項(xiàng)目 – 只Copy了部分字符串

            struct _textrangew
            {
            CHARRANGE chrg;
            LPWSTR lpstrText;
            } TEXTRANGEW;

            const wchar_t* Utils::extractURLFromRichEdit(…)
            {

            ::CopyMemory(tr.lpstrText, L"mailto:", 7);

            }

            這里的bug在于L"mailto:"是寬字符串,寬字符串中的每個(gè)字符占2或4個(gè)字節(jié)(依Compiler使用的字符集編碼而定),因此這里只 copy 7個(gè)字節(jié)顯然是不夠的,應(yīng)該是7 * sizeof(wchar_t)。

            [#9] CMake項(xiàng)目 – 循環(huán)內(nèi)的數(shù)組越界

            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循環(huán)的次數(shù)等于數(shù)組的元素個(gè)數(shù),但sizeof(doserrors)返回的卻是數(shù)組占用的字節(jié)個(gè)數(shù),這遠(yuǎn)遠(yuǎn)大于數(shù)組元素個(gè)數(shù),因此造成數(shù)組越界。正確的寫法:

            for (i = 0; i < sizeof(doserrors) / sizeof(*doserrors); i++)

            [#10] CPU Identifying Tool項(xiàng)目 – 打印到自身的字符串

            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);

            }

            通過sprintf,szOperatingSystem字符串將自己打印到自己里面,這是十分危險(xiǎn)的,將導(dǎo)致無法預(yù)知的錯(cuò)誤結(jié)果,可能會(huì)導(dǎo)致棧溢出等嚴(yán)重問題。

            [#12] Notepad++項(xiàng)目 – 數(shù)組局部clear

            #define CONT_MAP_MAX 50
            int _iContMap[CONT_MAP_MAX];

            DockingManager::DockingManager()
            {

            memset(_iContMap, -1, CONT_MAP_MAX);

            }

            代碼的原本試圖將數(shù)組_iContMap清零,但memset的第三個(gè)參數(shù)CONT_MAP_MAX并不能代表數(shù)組的真正大小,而只是數(shù)組的元素個(gè)數(shù)而已,顯然其忘記乘以sizeof(int)了。

            二、未定義行為

            在C/C++的語言規(guī)范中,我們常常能看到“xx is undefined”。規(guī)范中并沒有明確表明這類錯(cuò)誤是什么樣子的,只是說取決于Compiler的實(shí)現(xiàn),也許Compiler會(huì)給出正確的結(jié)果,但這么使用卻是不可移植的。

            [#1] Chromium項(xiàng)目 – 智能指針的誤用

            void AccessibleContainsAccessible(…)
            {

            auto_ptr<VARIANT> child_array(new VARIANT[child_count]);

            }

            這里的問題在于使用new[]分配的內(nèi)存,在智能指針釋放時(shí)卻用了delete,這將會(huì)導(dǎo)致未定義行為。看看autoptr的destructor就知道了:

            ~auto_ptr() {
            delete _Myptr;
            }

            我們可以找一些更合適的類來fix這個(gè)問題,比如boost::scopedarray。

            [#2] IPP Sample項(xiàng)目 – 經(jīng)典未定義行為

            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++;

            }

            }

            很多人一眼就看到了"pTemp = pTemp++"這行,對(duì)于這個(gè)代碼編譯器會(huì)產(chǎn)生兩種結(jié)果截然不同的翻譯:

            pTemp = pTemp + 1;
            pTemp = pTemp;

            TMP = pTemp;
            pTemp = pTemp + 1;
            pTemp = TMP;

            到底是哪種呢?依賴于編譯器的實(shí)現(xiàn),甚至是優(yōu)化級(jí)別的設(shè)定。

            三、與運(yùn)算優(yōu)先級(jí)相關(guān)的錯(cuò)誤

            [#1] MySQL工程 – !和&的運(yùn)算優(yōu)先級(jí)

            int ha_innobase::create(…)
            {

            if (srv_file_per_table
            && !mysqld_embedded
            && (!create_info->options & HA_LEX_CREATE_TMP_TABLE)) {

            }

            這段代碼原意是想測(cè)試create_info->options變量中幾個(gè)bit位的值是否set了,即!(create_info->options & HA_LEX_CREATE_TMP_TABLE),但由于!的運(yùn)算優(yōu)先級(jí)高于&,實(shí)際邏輯變成了(!create_info->options) & HA_LEX_CREATE_TMP_TABLE了。如果想要這段代碼如期工作,就不要吝嗇小括號(hào)了。

            [#2] Emule工程 – *和++的運(yùn)算優(yōu)先級(jí)

            STDMETHODIMP
            CCustomAutoComplete::Next(…, ULONG *pceltFetched)
            {

            if (pceltFetched != NULL)
            *pceltFetched++;

            }

            顯然作者原意是想對(duì)pceltFetched所指向的long型變量進(jìn)行++操作,但由于*和++的運(yùn)算優(yōu)先級(jí)沒有搞對(duì),導(dǎo)致實(shí)際上執(zhí)行了*(pceltFetched++)的操作,而不是(*pceltFetched)++操作。

            [#3] Chromium項(xiàng)目 – &和!=的運(yùn)算優(yōu)先級(jí)

            #define FILE_ATTRIBUTE_DIRECTORY 0×00000010

            bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {

            info->is_directory =
            file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0;

            }

            這個(gè)程序員的意圖是通過測(cè)試file_info.dwFileAttributes的幾個(gè)bit位的值來判定是否是目錄,邏輯上應(yīng)該是(file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0,但由于!=優(yōu)先級(jí)高于&,原代碼中無括號(hào),結(jié)果邏輯變成了file_info.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY != 0),導(dǎo)致is_directory將永遠(yuǎn)求值為true。

            [#4] BCmenu項(xiàng)目 – if和else弄混

            void BCMenu::InsertSpaces(void)
            {
            if(IsLunaMenuStyle())
            if(!xp_space_accelerators) return;
            else
            if(!original_space_accelerators) return;

            }

            這又是C語言的一個(gè)“大坑”,無奈這個(gè)BCMenu項(xiàng)目的程序員掉坑里了。雖然從代碼縮進(jìn)上來看,else似乎是與最外層的if配對(duì)使用,但實(shí)際這段代碼的效果是:

            if(IsLunaMenuStyle())
            {
            if(!xp_space_accelerators) {
            return;
            } else {
            if(!original_space_accelerators) return;
            }
            }

            這顯然不是程序員原意,看來括號(hào)必要時(shí)還是不能省略的。修改后的代碼如下:

            if(IsLunaMenuStyle()) {
            if(!xp_space_accelerators) return;
            } else {
            if(!original_space_accelerators) return;
            }

            四、格式化輸出錯(cuò)誤

            [#1] ReactOS項(xiàng)目 – 錯(cuò)誤地輸出WCHAR字符

            static void REGPROC_unescape_string(WCHAR* str)
            {

            default:
            fprintf(stderr,
            "Warning! Unrecognized escape sequence: \\%c'\n",
            str[str_idx]);

            }

            %c是用來格式化輸出非寬字符的,這里用來輸出WCHAR顯然會(huì)得到錯(cuò)誤的結(jié)果,fix solution是將%c換位%C。

            [#2] Intel AMT SDK項(xiàng)目 – 缺少%s 【VS2010,運(yùn)行可能會(huì)崩潰】

            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]);

            }

             

            不解釋了,自己慢慢數(shù)和對(duì)照吧。

            [#3] Intel AMT SDK項(xiàng)目 – 未使用的參數(shù)

            bool GetUserValues(…)
            {

            printf("Error: illegal value. Aborting.\n", tmp);
            return false;
            }

            然tmp是多余的。

            五、書寫錯(cuò)誤

            [#1] Miranda IM項(xiàng)目 – 在if中賦值

            void CIcqProto::handleUserOffline(BYTE *buf, WORD wLen)
            {

            else if (wTLVType = 0×29 && wTLVLen == sizeof(DWORD))

            }

            “wTLVType = 0×29”顯然是筆誤,應(yīng)該是“wTLVType == 0×29”才對(duì)。

            [#3] Clang項(xiàng)目 – 對(duì)象名書寫錯(cuò)誤

            static Value *SimplifyICmpInst(…) {

            case Instruction::Shl: {
            bool NUW =
            LBO->hasNoUnsignedWrap() && LBO->hasNoUnsignedWrap();
            bool NSW =
            LBO->hasNoSignedWrap() && RBO->hasNoSignedWrap();

            }

            從最后一行先后使用了LBO和RBO來看,前面只用了LBO的那行很可能是有問題的,正確的應(yīng)該是:

            bool NUW =
            LBO->hasNoUnsignedWrap() && RBO->hasNoUnsignedWrap();

            [#6] G3D Content Pak項(xiàng)目 – 一對(duì)括號(hào)放錯(cuò)了地方

            bool Matrix4::operator==(const Matrix4& other) const {
            if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
            return true;
            }

            }

            由于括號(hào)放錯(cuò)了地方,導(dǎo)致memcmp最后的參數(shù)變成了sizeof(Matrix4) == 0,這行代碼的正確寫法應(yīng)該是:

            if (memcmp(this, &other, sizeof(Matrix4)) == 0) {

            [#8] Apache Http Server項(xiàng)目 – 多余的sizeof

            PSECURITY_ATTRIBUTES GetNullACL(void)
            {
            PSECURITY_ATTRIBUTES sa;
            sa  = (PSECURITY_ATTRIBUTES)
            LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
            sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));

            }

            最后一行顯然是筆誤,sizeof(sizeof(SECURITY_ATTRIBUTES))應(yīng)該寫為sizeof(SECURITY_ATTRIBUTES)才對(duì)。

            [#10] Notepad++項(xiàng)目 – 在本來應(yīng)該用&的地方使用了&&

            TCHAR GetASCII(WPARAM wParam, LPARAM lParam)
            {

            result=ToAscii(wParam,
            (lParam >> 16) && 0xff, keys,&dwReturnedValue,0);

            }

            (lParam >> 16) && 0xff沒有什么意義,求值結(jié)果總是true。這里的代碼應(yīng)該是(lParam >> 16) & 0xff。

            [#12] Fennec Media Project項(xiàng)目 – 額外的分號(hào)

            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;
            }
            }

            這又是一個(gè)實(shí)際邏輯與代碼縮進(jìn)不符的例子。作者的原意是這樣的:

            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;
            }
            }

            但實(shí)際執(zhí)行代碼邏輯卻是:

            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;
            }

            這一切都是那個(gè);導(dǎo)致的。

            六、對(duì)基本函數(shù)和類的誤用

            [#2] TortoiseSVN項(xiàng)目 – remove函數(shù)的誤用

            STDMETHODIMP CShellExt::Initialize(….)
            {

            ignoredprops = UTF8ToWide(st.c_str());
            // remove all escape chars ('\\')
            std::remove(ignoredprops.begin(), ignoredprops.end(), '\\');
            break;

            }

            作者意圖刪除所有'\\',但他用錯(cuò)了函數(shù),remove函數(shù)只是交換元素的位置,將要?jiǎng)h除的元素交換到尾部trash,并且返回指向trash首地址的iterator。正確的做法應(yīng)該是"v.erase(remove(v.begin(), v.end(), 2), v.end())"。

            [#5] Pixie項(xiàng)目 – 在循環(huán)中使用alloca函數(shù)

            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函數(shù)在棧上分配內(nèi)存,因此在循環(huán)中使用alloca可能會(huì)很快導(dǎo)致棧溢出。

            七、無意義的代碼

            [#1] IPP Samples項(xiàng)目 – 不完整的條件

            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是Ipp32s類型指針,這樣if (mask< 0)這句代碼顯然沒啥意義,正確的代碼應(yīng)該是:

            if (mask[i] < 0) continue;

            [#2] QT項(xiàng)目 – 重復(fù)的檢查

            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"))) {


            }

            這里對(duì)"<td"做了兩次check。

            八、總是True或False的條件

            [#1] Shareaza項(xiàng)目 – char類型的值范圍

            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;
            }

            }

            表達(dá)式"pBytes[0] == 0xEF"總是False。char類型的值范圍是-128~127 < 0xEF,因此這個(gè)表達(dá)式總是False,導(dǎo)致整個(gè)if condition總是為False,與預(yù)期邏輯不符。

            [#3] VirtualDub項(xiàng)目 – 無符號(hào)類型總是>=0

            typedef unsigned short wint_t;

            void lexungetc(wint_t c) {
            if (c < 0)
            return;
            g_backstack.push_back(c);
            }

            c是unsigned short類型,永遠(yuǎn)不會(huì)小于0,也就是說if (c < 0)永遠(yuǎn)為False。

            [#8] MySQL項(xiàng)目 – 條件錯(cuò)誤

            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')這個(gè)條件永遠(yuǎn)為真。也許這塊本意是想用&&。

            九、代碼漏洞

            導(dǎo)致漏洞的代碼錯(cuò)誤實(shí)際上也都是筆誤、不正確的條件以及不正確的數(shù)組操作等。但這里還是想將一些特定錯(cuò)誤劃歸為一類,因?yàn)槿肭终呖梢岳眠@些錯(cuò)誤來攻擊你的代碼,獲取其利益。

            [#1] Ultimate TCP/IP項(xiàng)目 – 空字符串的錯(cuò)誤檢查

            char *CUT_CramMd5::GetClientResponse(LPCSTR ServerChallenge)
            {

            if (m_szPassword != NULL)
            {

            if (m_szPassword != '\0')
            {

            }

            第二個(gè)if condition check意圖檢查m_szPassword是否為空字符串,但卻錯(cuò)誤的將指針與'\0'進(jìn)行比較,正確的代碼應(yīng)該是這樣的:

            if (*m_szPassword != '\0')

            [#2] Chromium項(xiàng)目 – NULL指針的處理

            bool ChromeFrameNPAPI::Invoke(…)
            {
            ChromeFrameNPAPI* plugin_instance =
            ChromeFrameInstanceFromNPObject(header);
            if (!plugin_instance &&
            (plugin_instance->automation_client_.get()))
            return false;

            }

            一旦plugin_instance為NULL,!plugin_instance為True,代碼對(duì)&&后面的子條件求值,引用plugin_instance將導(dǎo)致程序崩潰。正確的做法應(yīng)該是:

            if (plugin_instance &&
            (plugin_instance->automation_client_.get()))
            return false;

            [#5] Apache httpd Server項(xiàng)目 – 不完整的緩沖區(qū)clear

            #define MEMSET_BZERO(p,l)       memset((p), 0, (l))

            void apr__SHA256_Final(…, SHA256_CTX* context) {

            MEMSET_BZERO(context, sizeof(context));

            }

            這個(gè)錯(cuò)誤前面提到過,sizeof(context)只是指針的大小,將之改為sizeof(*context)就OK了。

            [#7] PNG Library項(xiàng)目 – 意外的指針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的類型為png_charpp,顧名思義,這是一個(gè)char**類型,但代碼中new_key[79] = ‘\0′這句顯然是要給某個(gè)char賦值,但new_key[n]得到的應(yīng)該是一個(gè)地址,給一個(gè)地址賦值為’\0′顯然是有誤的。正確的寫法應(yīng)該是(*new_key)[79] = '\0'。

            [#10] Miranda IM項(xiàng)目 – 保護(hù)沒生效

            void Append( PCXSTR pszSrc, int nLength )
            {

            UINT nOldLength = GetLength();
            if (nOldLength < 0)
            {
            // protects from underflow
            nOldLength = 0;
            }

            }

            nOldLength椒UINT類型,其值永遠(yuǎn)不會(huì)小于0,因此if (nOldLength < 0)這行成了擺設(shè)。

            [#12] Ultimate TCP/IP項(xiàng)目 – 不正確的循環(huán)結(jié)束條件

            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;
            }

            }

            環(huán)中的結(jié)束條件loop >= 0將永遠(yuǎn)為True,因?yàn)閘oop變量的類型是size_t是unsigned類型,永遠(yuǎn)不會(huì)小于0。

            十、拷貝粘貼

            和筆誤不同,程序員們決不因該低估拷貝粘貼問題,這類問題發(fā)生了太多。程序員們花費(fèi)了大量時(shí)間在這些問題的debug上。

            [#1] Fennec Media Project項(xiàng)目 – 處理數(shù)組元素時(shí)出錯(cuò)

            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]做了兩次賦值,似乎沒啥問題。但仔細(xì)想一下,最后那行程序員的原意極可能是想寫fhead[14] = '\0'。問題就在這里了。

            [#2] MySQL項(xiàng)目 – 處理數(shù)組元素時(shí)出錯(cuò)

            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];
            }

             

            編寫這類代碼時(shí),我猜絕大多數(shù)人會(huì)選擇Copy-Paste,然后再逐行修改,問題就發(fā)生在修改過程中,上面的代碼中當(dāng)處理a[5] != b[5]時(shí)就忘記修改一個(gè)下標(biāo)了:return (int) a[1] – (int) b[5];顯然這里的正確代碼應(yīng)該是return (int) a[5] – (int) b[5]。

            [#3] TortoiseSVN項(xiàng)目 文件名不正確

            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"文件,顯然GetImageHlpVersion寫錯(cuò)文件名了。應(yīng)該用"IMAGEHLP.DLL"就對(duì)了。

            [#4] Clang項(xiàng)目 – 等同的函數(shù)體

            MapTy PerPtrTopDown;
            MapTy PerPtrBottomUp;

            void clearBottomUpPointers() {
            PerPtrTopDown.clear();
            }

            void clearTopDownPointers() {
            PerPtrTopDown.clear();
            }

            我們看到雖然兩個(gè)函數(shù)名不同,但是函數(shù)體的內(nèi)容是相同的,顯然又是copy-paste惹的禍。做如下修改即可:

            void clearBottomUpPointers() {
            PerPtrBottomUp.clear();
            }

             

            十一、Null指針的校驗(yàn)遲了

            這里的“遲了”的含義是先使用指針,然后再校驗(yàn)指針是否為NULL。

            [#1] Quake-III-Arena項(xiàng)目 – 校驗(yàn)遲了

            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;
            }

            }

             

            校驗(yàn)item是否為NULL前已經(jīng)使用過item了,一旦item真的為NULL,那程序必然崩潰。

            十二、其他雜項(xiàng)

            [#1] Image Processing 項(xiàng)目 – 八進(jìn)制數(shù)

            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;
            }

            第二個(gè)函數(shù),程序員原意是使用713這個(gè)十進(jìn)制整數(shù),但0713 != 713,在C中,0713是八進(jìn)制的表示法,Compiler會(huì)認(rèn)為這是個(gè)八進(jìn)制數(shù)。

            [#2] IPP Sample工程 – 一個(gè)變量用于兩個(gè)loop中

            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用在了兩個(gè)loop中,這會(huì)導(dǎo)致只有部分?jǐn)?shù)據(jù)被處理,或外部循環(huán)中止。

            [#3] Notepad++項(xiàng)目 – 怪異的條件表達(dá)式

            int Notepad_plus::getHtmlXmlEncoding(….) const
            {

            if (langT != L_XML && langT != L_HTML && langT == L_PHP)
            return -1;

            }

            代碼中的那行if條件等價(jià)于 if (langT == L_PHP),顯然似乎不是作者原意,猜測(cè)正確的代碼應(yīng)該是這樣的:

            int Notepad_plus::getHtmlXmlEncoding(….) const
            {

            if (langT != L_XML && langT != L_HTML && langT != L_PHP)
            return -1;

            }

            posted on 2013-05-19 21:10 郭龍 閱讀(701) 評(píng)論(1)  編輯 收藏 引用

            評(píng)論

            # re: 【轉(zhuǎn)】C/C++開源項(xiàng)目中的100個(gè)Bugs | WHO1753 2013-05-20 18:09 zgpxgame
            .  回復(fù)  更多評(píng)論
              


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            午夜不卡久久精品无码免费| 久久久久香蕉视频| 亚洲人成电影网站久久| 88久久精品无码一区二区毛片 | 天天做夜夜做久久做狠狠| 精品国产福利久久久| 97精品久久天干天天天按摩| 久久婷婷五月综合国产尤物app| 久久九九兔免费精品6| 久久午夜夜伦鲁鲁片免费无码影视| 久久久久久国产a免费观看不卡| 国产福利电影一区二区三区,免费久久久久久久精| 日韩精品久久无码人妻中文字幕| 久久99精品久久久大学生| 色综合久久久久综合体桃花网| 久久久久久午夜成人影院| 久久精品国产亚洲AV大全| 久久免费精品视频| 久久99精品国产麻豆婷婷| 日韩中文久久| 久久人人爽人人爽人人AV东京热| 国产精品99久久99久久久| 99久久无码一区人妻| 久久国产AVJUST麻豆| 国产精品一久久香蕉国产线看观看| 久久青草国产精品一区| 亚洲精品视频久久久| 亚洲国产另类久久久精品小说| 久久线看观看精品香蕉国产| 久久精品国产亚洲AV不卡| 无码人妻久久一区二区三区免费 | 99久久国产综合精品五月天喷水| 大蕉久久伊人中文字幕| 久久久久久久久久久精品尤物| 久久久精品人妻一区二区三区四| 久久亚洲高清观看| 97久久国产露脸精品国产| 99国内精品久久久久久久| 亚洲午夜久久久久妓女影院| 久久高清一级毛片| 人妻无码久久一区二区三区免费|