Re: [bug report] lib: zstd: Upgrade to latest upstream zstd version 1.4.10

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> On Oct 8, 2021, at 10:59 AM, Nick Terrell <terrelln@xxxxxx> wrote:
> 
> 
> 
>> On Oct 7, 2021, at 11:37 PM, Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
>> 
>> Hello Nick Terrell,
>> 
>> I grepped the README for zstd and it doesn't appear there is a mailing
>> list to add to the CC.
> 
> Thanks for the report Dan! Yeah, we don’t have a mailing list, but we use GitHub Issues.
> Mind if I open up an issue for this bug?
> 
> The `nbCompares == -1` looks like a real bug. It won’t cause infinite loops, because the
> tree data structure has a bounded depth, but it could cause longer execution time than
> intended.
> 
> I’ll look into it and fix it!
> 
> Best,
> Nick
> 
>> The patch c684b4e9a301: "lib: zstd: Upgrade to latest upstream zstd
>> version 1.4.10" from Sep 11, 2020, leads to the following Smatch static
>> checker warnings:
>> 
>> lib/zstd/compress/zstd_opt.c:695 ZSTD_insertBtAndGetAllMatches() warn: should this be 'nbCompares == -1'
>> lib/zstd/compress/zstd_lazy.c:360 ZSTD_DUBT_findBestMatch() warn: should this be 'nbCompares == -1'
>> lib/zstd/compress/zstd_lazy.c:1267 ZSTD_compressBlock_lazy_extDict_generic() warn: inconsistent indenting
>> lib/zstd/compress/zstd_compress.c:726 ZSTD_CCtxParams_setParameter() warn: no lower bound on 'value'
>> lib/zstd/decompress/zstd_decompress.c:177 ZSTD_createDDictHashSet() warn: variable dereferenced before check 'ret' (see line 174)

I’m not sure if you pre-filtered the Smatch results for true bugs, but I’m super impressed that all of these are true bugs!
And I just want to say thank you again for the report, this is the most useful static analysis bug report I’ve ever seen.
Normally when people submit a static analysis warnings, it is a list of garbage false positives, and there are far too many
false positives to sort through to find any potential true bugs.

I’m working on fixing these upstream now [0][1][2].

Thanks,
Nick

[0] https://github.com/facebook/zstd/pull/2817
[1] https://github.com/facebook/zstd/pull/2818
[2] https://github.com/facebook/zstd/pull/2819

>> lib/zstd/compress/zstd_opt.c
>>   508 FORCE_INLINE_TEMPLATE
>>   509 U32 ZSTD_insertBtAndGetAllMatches (
>>   510                     ZSTD_match_t* matches,   /* store result (found matches) in this table (presumed large enough) */
>>   511                     ZSTD_matchState_t* ms,
>>   512                     U32* nextToUpdate3,
>>   513                     const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode,
>>   514                     const U32 rep[ZSTD_REP_NUM],
>>   515                     U32 const ll0,   /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
>>   516                     const U32 lengthToBeat,
>>   517                     U32 const mls /* template */)
>>   518 {
>>   519     const ZSTD_compressionParameters* const cParams = &ms->cParams;
>>   520     U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
>>   521     const BYTE* const base = ms->window.base;
>>   522     U32 const curr = (U32)(ip-base);
>>   523     U32 const hashLog = cParams->hashLog;
>>   524     U32 const minMatch = (mls==3) ? 3 : 4;
>>   525     U32* const hashTable = ms->hashTable;
>>   526     size_t const h  = ZSTD_hashPtr(ip, hashLog, mls);
>>   527     U32 matchIndex  = hashTable[h];
>>   528     U32* const bt   = ms->chainTable;
>>   529     U32 const btLog = cParams->chainLog - 1;
>>   530     U32 const btMask= (1U << btLog) - 1;
>>   531     size_t commonLengthSmaller=0, commonLengthLarger=0;
>>   532     const BYTE* const dictBase = ms->window.dictBase;
>>   533     U32 const dictLimit = ms->window.dictLimit;
>>   534     const BYTE* const dictEnd = dictBase + dictLimit;
>>   535     const BYTE* const prefixStart = base + dictLimit;
>>   536     U32 const btLow = (btMask >= curr) ? 0 : curr - btMask;
>>   537     U32 const windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog);
>>   538     U32 const matchLow = windowLow ? windowLow : 1;
>>   539     U32* smallerPtr = bt + 2*(curr&btMask);
>>   540     U32* largerPtr  = bt + 2*(curr&btMask) + 1;
>>   541     U32 matchEndIdx = curr+8+1;   /* farthest referenced position of any match => detects repetitive patterns */
>>   542     U32 dummy32;   /* to be nullified at the end */
>>   543     U32 mnum = 0;
>>   544     U32 nbCompares = 1U << cParams->searchLog;
>>           ^^^^^^^^^^^^^^
>> This is unsigned.
>> 
>> 
>>   545 
>>   546     const ZSTD_matchState_t* dms    = dictMode == ZSTD_dictMatchState ? ms->dictMatchState : NULL;
>>   547     const ZSTD_compressionParameters* const dmsCParams =
>>   548                                       dictMode == ZSTD_dictMatchState ? &dms->cParams : NULL;
>>   549     const BYTE* const dmsBase       = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL;
>>   550     const BYTE* const dmsEnd        = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL;
>>   551     U32         const dmsHighLimit  = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0;
>>   552     U32         const dmsLowLimit   = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0;
>>   553     U32         const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0;
>>   554     U32         const dmsHashLog    = dictMode == ZSTD_dictMatchState ? dmsCParams->hashLog : hashLog;
>>   555     U32         const dmsBtLog      = dictMode == ZSTD_dictMatchState ? dmsCParams->chainLog - 1 : btLog;
>>   556     U32         const dmsBtMask     = dictMode == ZSTD_dictMatchState ? (1U << dmsBtLog) - 1 : 0;
>>   557     U32         const dmsBtLow      = dictMode == ZSTD_dictMatchState && dmsBtMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - dmsBtMask : dmsLowLimit;
>>   558 
>>   559     size_t bestLength = lengthToBeat-1;
>>   560     DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", curr);
>>   561 
>>   562     /* check repCode */
>>   563     assert(ll0 <= 1);   /* necessarily 1 or 0 */
>>   564     {   U32 const lastR = ZSTD_REP_NUM + ll0;
>>   565         U32 repCode;
>>   566         for (repCode = ll0; repCode < lastR; repCode++) {
>>   567             U32 const repOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
>>   568             U32 const repIndex = curr - repOffset;
>>   569             U32 repLen = 0;
>>   570             assert(curr >= dictLimit);
>>   571             if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < curr-dictLimit) {  /* equivalent to `curr > repIndex >= dictLimit` */
>>   572                 /* We must validate the repcode offset because when we're using a dictionary the
>>   573                  * valid offset range shrinks when the dictionary goes out of bounds.
>>   574                  */
>>   575                 if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) {
>>   576                     repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch;
>>   577                 }
>>   578             } else {  /* repIndex < dictLimit || repIndex >= curr */
>>   579                 const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ?
>>   580                                              dmsBase + repIndex - dmsIndexDelta :
>>   581                                              dictBase + repIndex;
>>   582                 assert(curr >= windowLow);
>>   583                 if ( dictMode == ZSTD_extDict
>>   584                   && ( ((repOffset-1) /*intentional overflow*/ < curr - windowLow)  /* equivalent to `curr > repIndex >= windowLow` */
>>   585                      & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)
>>   586                   && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
>>   587                     repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch;
>>   588                 }
>>   589                 if (dictMode == ZSTD_dictMatchState
>>   590                   && ( ((repOffset-1) /*intentional overflow*/ < curr - (dmsLowLimit + dmsIndexDelta))  /* equivalent to `curr > repIndex >= dmsLowLimit` */
>>   591                      & ((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */
>>   592                   && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
>>   593                     repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch;
>>   594             }   }
>>   595             /* save longer solution */
>>   596             if (repLen > bestLength) {
>>   597                 DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u",
>>   598                             repCode, ll0, repOffset, repLen);
>>   599                 bestLength = repLen;
>>   600                 matches[mnum].off = repCode - ll0;
>>   601                 matches[mnum].len = (U32)repLen;
>>   602                 mnum++;
>>   603                 if ( (repLen > sufficient_len)
>>   604                    | (ip+repLen == iLimit) ) {  /* best possible */
>>   605                     return mnum;
>>   606     }   }   }   }
>>   607 
>>   608     /* HC3 match finder */
>>   609     if ((mls == 3) /*static*/ && (bestLength < mls)) {
>>   610         U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip);
>>   611         if ((matchIndex3 >= matchLow)
>>   612           & (curr - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
>>   613             size_t mlen;
>>   614             if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) {
>>   615                 const BYTE* const match = base + matchIndex3;
>>   616                 mlen = ZSTD_count(ip, match, iLimit);
>>   617             } else {
>>   618                 const BYTE* const match = dictBase + matchIndex3;
>>   619                 mlen = ZSTD_count_2segments(ip, match, iLimit, dictEnd, prefixStart);
>>   620             }
>>   621 
>>   622             /* save best solution */
>>   623             if (mlen >= mls /* == 3 > bestLength */) {
>>   624                 DEBUGLOG(8, "found small match with hlog3, of length %u",
>>   625                             (U32)mlen);
>>   626                 bestLength = mlen;
>>   627                 assert(curr > matchIndex3);
>>   628                 assert(mnum==0);  /* no prior solution */
>>   629                 matches[0].off = (curr - matchIndex3) + ZSTD_REP_MOVE;
>>   630                 matches[0].len = (U32)mlen;
>>   631                 mnum = 1;
>>   632                 if ( (mlen > sufficient_len) |
>>   633                      (ip+mlen == iLimit) ) {  /* best possible length */
>>   634                     ms->nextToUpdate = curr+1;  /* skip insertion */
>>   635                     return 1;
>>   636         }   }   }
>>   637         /* no dictMatchState lookup: dicts don't have a populated HC3 table */
>>   638     }
>>   639 
>>   640     hashTable[h] = curr;   /* Update Hash Table */
>>   641 
>>   642     while (nbCompares-- && (matchIndex >= matchLow)) {
>>                  ^^^^^^^^^^^^
>> This is a post op so it will exit the loop with nbCompares set to -1U.
>> 
>>   643         U32* const nextPtr = bt + 2*(matchIndex & btMask);
>>   644         const BYTE* match;
>>   645         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
>>   646         assert(curr > matchIndex);
>>   647 
>>   648         if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) {
>>   649             assert(matchIndex+matchLength >= dictLimit);  /* ensure the condition is correct when !extDict */
>>   650             match = base + matchIndex;
>>   651             if (matchIndex >= dictLimit) assert(memcmp(match, ip, matchLength) == 0);  /* ensure early section of match is equal as expected */
>>   652             matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit);
>>   653         } else {
>>   654             match = dictBase + matchIndex;
>>   655             assert(memcmp(match, ip, matchLength) == 0);  /* ensure early section of match is equal as expected */
>>   656             matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart);
>>   657             if (matchIndex+matchLength >= dictLimit)
>>   658                 match = base + matchIndex;   /* prepare for match[matchLength] read */
>>   659         }
>>   660 
>>   661         if (matchLength > bestLength) {
>>   662             DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)",
>>   663                     (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
>>   664             assert(matchEndIdx > matchIndex);
>>   665             if (matchLength > matchEndIdx - matchIndex)
>>   666                 matchEndIdx = matchIndex + (U32)matchLength;
>>   667             bestLength = matchLength;
>>   668             matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
>>   669             matches[mnum].len = (U32)matchLength;
>>   670             mnum++;
>>   671             if ( (matchLength > ZSTD_OPT_NUM)
>>   672                | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
>>   673                 if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */
>>                                                            ^^^^^^^^^^^^^^
>> Or it could exit here.
>> 
>>   674                 break; /* drop, to preserve bt consistency (miss a little bit of compression) */
>>   675             }
>>   676         }
>>   677 
>>   678         if (match[matchLength] < ip[matchLength]) {
>>   679             /* match smaller than current */
>>   680             *smallerPtr = matchIndex;             /* update smaller idx */
>>   681             commonLengthSmaller = matchLength;    /* all smaller will now have at least this guaranteed common length */
>>   682             if (matchIndex <= btLow) { smallerPtr=&dummy32; break; }   /* beyond tree size, stop the search */
>>   683             smallerPtr = nextPtr+1;               /* new candidate => larger than match, which was smaller than current */
>>   684             matchIndex = nextPtr[1];              /* new matchIndex, larger than previous, closer to current */
>>   685         } else {
>>   686             *largerPtr = matchIndex;
>>   687             commonLengthLarger = matchLength;
>>   688             if (matchIndex <= btLow) { largerPtr=&dummy32; break; }   /* beyond tree size, stop the search */
>>   689             largerPtr = nextPtr;
>>   690             matchIndex = nextPtr[0];
>>   691     }   }
>>   692 
>>   693     *smallerPtr = *largerPtr = 0;
>>   694 
>> --> 695     if (dictMode == ZSTD_dictMatchState && nbCompares) {
>>                                                  ^^^^^^^^^^
>> The should this be check for if nbCompares == -1?  This could be
>> intentional.  Hard to tell.
>> 
>>   696         size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls);
>>   697         U32 dictMatchIndex = dms->hashTable[dmsH];
>>   698         const U32* const dmsBt = dms->chainTable;
>>   699         commonLengthSmaller = commonLengthLarger = 0;
>>   700         while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) {
>>   701             const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask);
>>   702             size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
>>   703             const BYTE* match = dmsBase + dictMatchIndex;
>>   704             matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dmsEnd, prefixStart);
>>   705             if (dictMatchIndex+matchLength >= dmsHighLimit)
>>   706                 match = base + dictMatchIndex + dmsIndexDelta;   /* to prepare for next usage of match[matchLength] */
>>   707 
>>   708             if (matchLength > bestLength) {
>>   709                 matchIndex = dictMatchIndex + dmsIndexDelta;
>>   710                 DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)",
>>   711                         (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
>>   712                 if (matchLength > matchEndIdx - matchIndex)
>>   713                     matchEndIdx = matchIndex + (U32)matchLength;
>>   714                 bestLength = matchLength;
>>   715                 matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
>>   716                 matches[mnum].len = (U32)matchLength;
>>   717                 mnum++;
>>   718                 if ( (matchLength > ZSTD_OPT_NUM)
>>   719                    | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
>>   720                     break;   /* drop, to guarantee consistency (miss a little bit of compression) */
>>   721                 }
>>   722             }
>>   723 
>>   724             if (dictMatchIndex <= dmsBtLow) { break; }   /* beyond tree size, stop the search */
>>   725             if (match[matchLength] < ip[matchLength]) {
>>   726                 commonLengthSmaller = matchLength;    /* all smaller will now have at least this guaranteed common length */
>>   727                 dictMatchIndex = nextPtr[1];              /* new matchIndex larger than previous (closer to current) */
>>   728             } else {
>>   729                 /* match is larger than current */
>>   730                 commonLengthLarger = matchLength;
>>   731                 dictMatchIndex = nextPtr[0];
>>   732             }
>>   733         }
>>   734     }
>>   735 
>>   736     assert(matchEndIdx > curr+8);
>>   737     ms->nextToUpdate = matchEndIdx - 8;  /* skip repetitive patterns */
>>   738     return mnum;
>>   739 }
>> 
>> regards,
>> dan carpenter
> 





[Index of Archives]     [Kernel Development]     [Kernel Announce]     [Kernel Newbies]     [Linux Networking Development]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Device Mapper]

  Powered by Linux