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