Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Intel IPP Samples for Windows - error correction


Published on

This is one of my posts on how PVS-Studio makes programs safer. That is where and what types of errors it detects. This time it is samples demonstrating handling of the IPP 7.0 library (Intel Performance Primitives Library) we are going to examine.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Intel IPP Samples for Windows - error correction

  1. 1. Intel IPP Samples for Windows - errorcorrectionAuthor: Andrey KarpovDate: 27.01.2011This is one of my posts on how PVS-Studio makes programs safer. That is where and what types of errorsit detects. This time it is samples demonstrating handling of the IPP 7.0 library (Intel PerformancePrimitives Library) we are going to examine.Intel Parallel Studio 2011 includes the Performance Primitives Library. This library in its turn includes alot of primitives which allow you to create efficient video and audio codecs, signal processing software,image rendering mechanisms, archivers and so on. Sure, it is rather difficult to handle such a library.That is why Intel created a lot of demonstration programs based on it. You may see descriptions ofsamples and download them here: • Code Samples for the Intel Integrated Performance Primitives (Intel IPP) Library • Intel Integrated Performance Primitives Library 7.0 Samples for Parallel Studio 2011All the samples are arranged into four groups: • IPP Samples for Windows • IPP UIC Demo for Windows • IPP DMIP Samples for Windows • IPP Cryptography Samples for WindowsEach set contains many projects, so, for a start, I took only the first set IPP Samples for Windows for thecheck. I used PVS-Studio 4.10 to perform the analysis.I want to show you in this post that static analysis is useful regardless of programmers skill and level ofa solution being developed. The idea "you must employ experts and write code without errors rightaway" does not work. Even highly skilled developers cannot be secure from all the errors and misprintswhile writing code. Errors in samples for IPP show this very well.I want you to note that IPP Samples for Windows is a high-quality project. But due to its size, 1.6 millionsof code lines, it cannot but contain various errors. Lets examine some of them.Bad replacement of arrays indexesI could well include this sample into my previous article "Consequences of using the Copy-Paste methodin C++ programming and how to deal with it":struct AVS_MB_INFO{
  2. 2. ... Ipp8u refIdx[AVS_DIRECTIONS][4]; ...};void AVSCompressor::GetRefIndiciesBSlice(void){ ... if (m_pMbInfo->predType[0] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0]; iRefNum += 1; } if (m_pMbInfo->predType[1] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1]; iRefNum += 1; } if (m_pMbInfo->predType[2] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2]; iRefNum += 1; } if (m_pMbInfo->predType[3] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30]; iRefNum += 1; } ...}
  3. 3. The PVS-Studios diagnostic message: V557 Array overrun is possible. The 30 index is pointing beyondarray bound. avs_enc umc_avs_enc_compressor_enc_b.cpp 495The programmer copied the code fragment several times and changed the arrays indexes. But in theend his hand shook and he typed number 3 but forgot to delete 0. As a result, we have got index 30 andthere is an overrun far outside the arrays boundaries.Identical code branchesSince we started with code copying, lets examine one more example related to it:AACStatus aacencGetFrame(...){ ... if (maxEn[0] > maxEn[1]) { ics[1].num_window_groups = ics[0].num_window_groups; for (g = 0; g < ics[0].num_window_groups; g++) { ics[1].len_window_group[g] = ics[0].len_window_group[g]; } } else { ics[1].num_window_groups = ics[0].num_window_groups; for (g = 0; g < ics[0].num_window_groups; g++) { ics[1].len_window_group[g] = ics[0].len_window_group[g]; } } ...}The PVS-Studios diagnostic message: V523 The then statement is equivalent to the else statement.aac_enc aac_enc_api_fp.c 1379But this time it is just on the contrary - the programmer forgot to edit the copied code. The bothbranches of the conditional operator "if" perform the same actions.Confusion with priority of "--" decrement operation and "*" pointersdereferencingstatic voidsbrencConflictResolution (..., Ipp32s *nLeftBord)
  4. 4. { ... *nLeftBord = nBordNext - 1; ... if (*lenBordNext > 1) { ... *nLeftBord--; } ...}The PVS-Studios diagnostic message: V532 Consider inspecting the statement of *pointer-- pattern.Probably meant: (*pointer)--. aac_enc sbr_enc_frame_gen.c 428The "nLeftBord" pointer returns values from the "sbrencConflictResolution" function. At first, it is thevalue "nBordNext - 1" which is written by the specified address. On certain conditions, this value mustbe decremented by one. To decrement the value, the programmer used this code:*nLeftBord--;The error is that it is the pointer itself which is decremented instead of the value. The correct code looksthis way:(*nLeftBord)--;More confusion with "++" increment operation and "*" pointersdereferencingI cannot understand the following code at all. I do not know how to fix it to make it meaningful. Perhapssomething is missing here.static IppStatus mp2_HuffmanTableInitAlloc(Ipp32s *tbl, ...){ ... for (i = 0; i < num_tbl; i++) { *tbl++; } ...}
  5. 5. The PVS-Studios diagnostic message: V532 Consider inspecting the statement of *pointer++ pattern.Probably meant: (*pointer)++. mpeg2_dec umc_mpeg2_dec.cpp 59Here, the loop from the sample above is equivalent to the following code:tbl += num_tbl;The PVS-Studio analyzer supposed that parentheses might be missing here and there must be this code:"(*tbl)++;". But this variant is meaningless too. In this case, the loop is equivalent to this code:*tbl += num_tbl;So, this loop is a rather strange one. The error does exist but only the codes author seems to know howto fix it.Loss of error flagThe code has the function "GetTrackByPidOrCreateNew" that returns "-1" if an error occurs.typedef signed int Ipp32s;typedef unsigned int Ipp32u;Ipp32s StreamParser::GetTrackByPidOrCreateNew( Ipp32s iPid, bool *pIsNew){ ... else if (!pIsNew || m_uiTracks >= MAX_TRACK) return -1; ...}The "GetTrackByPidOrCreateNew" function itself is absolutely correct. But an error occurs while using it:Status StreamParser::GetNextData(MediaData *pData, Ipp32u *pTrack){ ... *pTrack = GetTrackByPidOrCreateNew(m_pPacket->iPid, NULL); if (*pTrack >= 0 && TRACK_LPCM == m_pInfo[*pTrack]->m_Type) ippsSwapBytes_16u_I((Ipp16u *)pData->GetDataPointer(),
  6. 6. m_pPacket->uiSize / 2); ...}The PVS-Studios diagnostic message: V547 Expression * pTrack >= 0 is always true. Unsigned typevalue is always >= 0. demuxer umc_stream_parser.cpp 179The value returned by the "GetTrackByPidOrCreateNew" function is saved as the unsigned int type. Itmeans that "-1" turns into "4294967295". The "*pTrack >= 0" condition is always true.As a result, if the "GetTrackByPidOrCreateNew" function returns "-1", an Access Violation will occurwhile executing "m_pInfo[*pTrack]->m_Type".Copy-Paste and missing +1void H264SegmentDecoder::ResetDeblockingVariablesMBAFF(){ ... if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr - mb_width * 2])) m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] = m_CurMBAddr - mb_width * 2; else m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] = m_CurMBAddr - mb_width * 2; ...}The PVS-Studios diagnostic message: V523 The then statement is equivalent to the else statement.h264_dec umc_h264_segment_decoder_deblocking_mbaff.cpp 340If you look at the nearby code, you will understand that the programmer forgot to add 1 in the copiedline. This is the correct code:if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr - mb_width * 2])) m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] = m_CurMBAddr - mb_width * 2;else
  7. 7. m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] = m_CurMBAddr - mb_width * 2 + 1;Not far from this place, there is the same error with missing "+ 1" in the function"H264CoreEncoder_ResetDeblockingVariablesMBAFF".The PVS-Studios diagnostic message: V523 The then statement is equivalent to the else statement.h264_enc umc_h264_deblocking_mbaff_tmpl.cpp.h 366Remove that does not remove anythingvoid H264ThreadGroup::RemoveThread(H264Thread * thread){ AutomaticUMCMutex guard(m_mGuard); std::remove(m_threads.begin(), m_threads.end(), thread);}The PVS-Studios diagnostic message: V530 The return value of function remove is required to beutilized. h264_dec umc_h264_thread.cpp 226This is quite an interesting combination. On the one hand, everything is cool. We have mutex tocorrectly remove items in a multithreaded application. On the other hand, the developers simply forgotthat the std::remove function does not remove items from the array but only rearranges them. Actually,this code must look this way:m_threads .erase( std::remove(m_threads.begin(), m_threads.end(), thread), m_threads.end());Comparing structures fields to themselvesI was looking through the errors and noticed that implementation of the H264 video compressionstandard is somewhat defective. A lot of errors we have found relate to this very project. For instance,the programmer was in a hurry and used two wrong variable names at once.bool H264_AU_Stream::IsPictureSame(H264SliceHeaderParse & p_newHeader){ if ((p_newHeader.frame_num != m_lastSlice.frame_num) || (p_newHeader.pic_parameter_set_id != p_newHeader.pic_parameter_set_id) || (p_newHeader.field_pic_flag != p_newHeader.field_pic_flag) || (p_newHeader.bottom_field_flag != m_lastSlice.bottom_field_flag)
  8. 8. ){ return false; } ...}The PVS-Studios diagnostic messages:V501 There are identical sub-expressions p_newHeader.pic_parameter_set_id to the left and to theright of the != operator. h264_spl umc_h264_au_stream.cpp 478V501 There are identical sub-expressions p_newHeader.field_pic_flag to the left and to the right of the!= operator. h264_spl umc_h264_au_stream.cpp 479The comparison function does not work because some members of the structure are compared tothemselves. Here are the two corrected lines:(p_newHeader.pic_parameter_set_id != m_lastSlice.pic_parameter_set_id)(p_newHeader.field_pic_flag != m_lastSlice.field_pic_flag)Incorrect data copyingErrors related to use of wrong objects occur not only in comparison operations but in operations ofcopying objects states:Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par){ ... VOL.sprite_width = par->sprite_width; VOL.sprite_height = par->sprite_height; VOL.sprite_left_coordinate = par->sprite_left_coordinate; VOL.sprite_top_coordinate = par->sprite_left_coordinate; ...}The PVS-Studios diagnostic message: V537 Consider reviewing the correctness ofsprite_left_coordinate items usage. mpeg4_enc mp4_enc_misc.cpp 387A wrong value is saved into "VOL.sprite_top_coordinate". This is the correct assignment operation:VOL.sprite_top_coordinate = par->sprite_top_coordinate;
  9. 9. Two loops for one variableJERRCODE 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); } ...}The PVS-Studios diagnostic message: V535 The variable c is being used for this loop and for the outerloop. jpegcodec jpegdec.cpp 4652One variable c is used for two loops nested in each other. A decoding function like this may causestrange and unpredicted results.Double assignment for additional safetyH264EncoderFrameType*H264ENC_MAKE_NAME(H264EncoderFrameList_findOldestToEncode)(...){ ... MaxBrefPOC = H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3); MaxBrefPOC = H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3); ...}
  10. 10. The PVS-Studios diagnostic message: V519 The MaxBrefPOC object is assigned values twicesuccessively. Perhaps this is a mistake. h264_enc umc_h264_enc_cpb_tmpl.cpp.h 784When I saw this code, I recalled an old programmers joke:- Why do you have two identical GOTO one right after the other in your code?- What if the first one doesnt work!Well, this error is not crucial yet it is an error.Code making you alertAACStatus sbrencResampler_v2_32f(Ipp32f* pSrc, Ipp32f* pDst){ ... k = nCoef-1; k = nCoef; ...}The PVS-Studios diagnostic message: V519 The k object is assigned values twice successively. Perhapsthis is a mistake. aac_enc sbr_enc_resampler_fp.c 90This double assignment alerts me much more than in the previous sample. It seems as if theprogrammer was not confident. Or as if he decided to try "nCoef-1" first and then "nCoef". It is alsocalled "programming through experiment method". Anyway, it is that very case when you should stopfor a while and think it over on encountering such a fragment.Minimum value which is not quite minimumvoid MeBase::MakeVlcTableDecision(){ ... Ipp32s BestMV= IPP_MIN(IPP_MIN(m_cur.MvRate[0],m_cur.MvRate[1]), IPP_MIN(m_cur.MvRate[2],m_cur.MvRate[3])); Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]), IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[2])); ...}
  11. 11. The PVS-Studios diagnostic message: V501 There are identical sub-expressions to the left and to theright of the < operator: (m_cur.AcRate [2]) < (m_cur.AcRate [2]) me umc_me.cpp 898Here is another misprint in the arrays index. The last index must be 3, not 2. This is the correct code:Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]), IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[3]));What is unpleasant about such errors is that the code "almost works". The error occurs only if theminimum item is stored in "m_cur.AcRate[3]". Such errors like to hide during testing and show up onusers computers at user input data.Maximum value which is not quite maximumThere are problems with maximum values too:Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par){ ... i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchHorBack); ...}The PVS-Studios diagnostic message: V501 There are identical sub-expressions (mBVOPsearchHorBack)to the left and to the right of the > operator. mpeg4_enc mp4_enc_misc.cpp 547The mBVOPsearchHorBack variable is used twice. Actually, the programmer intended to usemBVOPsearchHorBack and mBVOPsearchVerBack:i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchVerBack);A bad shottypedef struct{ ... VM_ALIGN16_DECL(Ipp32f)nb_short[2][3][__ALIGNED(MAX_PPT_SHORT)]; ...} mpaPsychoacousticBlock;static void mp3encPsy_short_window(...)
  12. 12. { ... if (win_counter == 0) { nb_s = pBlock->nb_short[0][3]; } ...}The PVS-Studios diagnostic message: V557 Array overrun is possible. The 3 index is pointing beyondarray bound. mp3_enc mp3enc_psychoacoustic_fp.c 726There must be a simple misprint here. It is index 3 used accidentally instead of 2. I think youunderstand the consequences.Error causing a slowdownvoid 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; }}The PVS-Studios diagnostic message: V503 This is a nonsensical comparison: pointer < 0. ipprsampleippr_sample.cpp 501This is a nice example of code that works slower than it could due to an error. The algorithm mustnormalize only those items which are specified in the mask array. But this code normalizes all the items.The error is located in the "if(mask<0)" condition. The programmer forgot to use the "i" index. The"mask" pointer will be almost all the time above or equal to zero and therefore we will process all theitems.
  13. 13. This is the correct code:if(mask[i]<0) continue;Subtraction result always amounts to 0int ec_fb_GetSubbandNum(void *stat){ _fbECState *state=(_fbECState *)stat; return (state->freq-state->freq);}The PVS-Studios diagnostic message: V501 There are identical sub-expressions to the left and to theright of the - operator: state->freq - state->freq speech ec_fb.c 250A misprint here causes the function to return 0 all the time. We are subtracting something wrong here. Ido not know what it actually must be.Incorrect processing of buffer overflowtypedef unsigned int Ipp32u;UMC::Status Init(..., Ipp32u memSize, ...){ ... memSize -= UMC::align_value<Ipp32u>(m_nFrames*sizeof(Frame)); if(memSize < 0) return UMC::UMC_ERR_NOT_ENOUGH_BUFFER; ...}The PVS-Studios diagnostic message: V547 Expression memSize < 0 is always false. Unsigned typevalue is never < 0. vc1_enc umc_vc1_enc_planes.h 200Processing of situation when the buffers size is not sufficient is implemented incorrectly. The programwill continue working instead of returning the errors code and most likely will crash. The point is thatthe "memSize" variable has the "unsigned int" type. So, the "memSize < 0" condition is always false andwe go on working with a buffer overflow.I think it is a good example of software attack vulnerability. You may cause a buffer overflow by feedingincorrect data into the program and use it for your own purposes. By the way, we found about 10 suchvulnerabilities in the code. I will not describe them here not to overload the text.
  14. 14. Overrun in the wake of incorrect checkIpp32u m_iCurrMBIndex;VC1EncoderMBInfo* VC1EncoderMBs::GetPevMBInfo(Ipp32s x, Ipp32s y){ Ipp32s row = (y>0)? m_iPrevRowIndex:m_iCurrRowIndex; return ((m_iCurrMBIndex - x <0 || row <0)? 0 : &m_MBInfo[row][m_iCurrMBIndex - x]);}The PVS-Studios diagnostic message: V547 Expression m_iCurrMBIndex - x < 0 is always false.Unsigned type value is never < 0. vc1_enc umc_vc1_enc_mb.cpp 188The "m_iCurrMBIndex" variable has the "unsigned" type. Because of it, the "m_iCurrMBIndex - x"expression also has the "unsigned" type. Therefore, the "m_iCurrMBIndex - x < 0" condition is alwaysfalse. Lets see what consequences it has.Let the "m_iCurrMBIndex" variable amount to 5 and "x" variable amount to 10.The "m_iCurrMBIndex - x" expression equals 5u - 10i = 0xFFFFFFFBu.The "m_iCurrMBIndex - x < 0" condition is false.The "m_MBInfo[row][0xFFFFFFFBu]" expression is executed and an overrun occurs.Error of using ?: ternary operatorThe ternary operator is rather dangerous because you may easily make an error using it. Nevertheless,programmers like to write code as short as possible and use the interesting language construct. The C++language punishes them for this.vm_file* vm_file_fopen(...){ ... mds[3] = FILE_ATTRIBUTE_NORMAL | (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING; ...}The PVS-Studios diagnostic message: V502 Perhaps the ?: operator works in a different way than it wasexpected. The ?: operator has a lower priority than the | operator. vm vm_file_win.c 393There must be a combination of flags FILE_ATTRIBUTE_NORMAL and FILE_FLAG_NO_BUFFERING. Butactually, the "mds[3]" item is always assigned 0.
  15. 15. The programmer forgot that the priority of "|" operator is higher than that of "?:" operator. So it turnsout that we have the following expression in the code (note the parentheses):(FILE_ATTRIBUTE_NORMAL | (islog == 0)) ? 0 : FILE_FLAG_NO_BUFFERING;The "FILE_ATTRIBUTE_NORMAL | (islog == 0)" condition is always true and we assign 0 to "mds[3]" item.This is the correct expression (note the parentheses once again):FILE_ATTRIBUTE_NORMAL | ((islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING);Strange handling of arrayAACStatus alsdecGetFrame(...){ ... for (i = 0; i < num; i++) { ... *tmpPtr = (Ipp32s)((tmp << 24) + ((tmp & 0xff00) << 8) + ((tmp >> 8) & 0xff00) + (tmp >> 24)); *tmpPtr = *srcPrt; ... } ...}The PVS-Studios diagnostic message: V519 The * tmpPtr object is assigned values twice successively.Perhaps this is a mistake. aac_dec als_dec_api.c 928I suggest that the readers examine the code themselves and draw conclusions. I would just call this code"peculiar".Paranormal assignmentsstaticIPLStatus ownRemap8u_Pixel(...) { ... saveXMask = xMap->maskROI;
  16. 16. saveXMask = NULL; saveYMask = yMap->maskROI; saveYMask = NULL; ...}The PVS-Studios diagnostic messages:V519 The saveXMask object is assigned values twice successively. Perhaps this is a mistake. ipliplremap.c 36V519 The saveYMask object is assigned values twice successively. Perhaps this is a mistake. ipliplremap.c 38I cannot see the reason for such strange code. Note that this block is repeated 8 times in differentfunctions!There are also other strange assignments of one variable:Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par){ ... mNumOfFrames = par->NumOfFrames; mNumOfFrames = -1; ...}The PVS-Studios diagnostic message: V519 The mNumOfFrames object is assigned values twicesuccessively. Perhaps this is a mistake. mpeg4_enc mp4_enc_misc.cpp 276SummaryI described only some of the errors detected in IPP Samples for Windows in this article. I have not listedsome errors because they are twins with those I have discussed in the article, so it would not beinteresting to read about them. I also have not given inessential errors here. For instance, take assert()which always has a true condition because of a misprint. I skipped many code fragments because Isimply did not know if there were errors or just poor code. But I think I have described enough defectsto show you how difficult it is to write large projects even for skilled developers.Let me once again formulate the idea I have mentioned in the beginning of the article. Even a goodprogrammer is not secure from misprints, absent-mindedness, urge to use Copy-Paste and logical errors.I think this article will be a good answer for those people who believe that the phrase "you must writecorrect code" will protect them against any errors.
  17. 17. I wish you luck in all your C/C++/C++0x projects. May you find as many errors as possible using the staticanalysis methodology I love so much!