የPVS-ስቱዲዮ ተንታኝን በመጠቀም በኤልኤልቪኤም 8 ውስጥ ስህተቶችን መፈለግ

የPVS-ስቱዲዮ ተንታኝን በመጠቀም በኤልኤልቪኤም 8 ውስጥ ስህተቶችን መፈለግ
የእኛን PVS-Studio ተንታኝ በመጠቀም የኤልኤልቪኤም ፕሮጀክት የመጨረሻ የኮድ ፍተሻ ካለፈ ከሁለት ዓመታት በላይ አልፏል። የPVS-ስቱዲዮ ተንታኝ አሁንም ስህተቶችን እና ሊሆኑ የሚችሉ ተጋላጭነቶችን ለመለየት ግንባር ቀደም መሳሪያ መሆኑን እናረጋግጥ። ይህንን ለማድረግ በኤልኤልቪኤም 8.0.0 ልቀት ውስጥ አዲስ ስህተቶችን እንፈትሻለን እና እናገኛለን።

የሚጻፍ ጽሑፍ

እውነቱን ለመናገር ይህን ጽሑፍ መጻፍ አልፈለኩም። ቀደም ሲል ብዙ ጊዜ ስለመረመርነው ፕሮጀክት መጻፍ አስደሳች አይደለም (1, 2, 3). ስለ አዲስ ነገር መጻፍ ይሻላል, ግን ምንም ምርጫ የለኝም.

አዲስ የኤልኤልቪኤም ስሪት በተለቀቀ ወይም በተዘመነ ቁጥር Clang Static Analyzerበፖስታአችን ውስጥ የሚከተሉትን አይነት ጥያቄዎች እንቀበላለን

ተመልከት፣ አዲሱ የ Clang Static Analyzer እትም አዳዲስ ስህተቶችን ለማግኘት ተምሯል! ፒቪኤስ-ስቱዲዮን የመጠቀም አስፈላጊነት እየቀነሰ ይመስላል። ክላንግ ከበፊቱ የበለጠ ስህተቶችን ያገኛል እና የ PVS-ስቱዲዮን ችሎታዎች ይይዛል። ስለዚህ ጉዳይ ምን ያስባሉ?

ለዚህም ሁሌም አንድ ነገር መመለስ እፈልጋለሁ፡-

እኛም ዝም ብለን አንቀመጥም! የPVS-ስቱዲዮ ተንታኝ አቅምን በእጅጉ አሻሽለናል። ስለዚህ አይጨነቁ፣ እንደቀድሞው መመራታችንን እንቀጥላለን።

በሚያሳዝን ሁኔታ, ይህ መጥፎ መልስ ነው. በውስጡ ምንም ማረጋገጫዎች የሉም. እና አሁን ይህን ጽሑፍ የምጽፈው ለዚህ ነው። ስለዚህ፣ የኤልኤልቪኤም ፕሮጀክቱ በድጋሚ ተፈትሸው እና በውስጡ የተለያዩ ስህተቶች ተገኝተዋል። አሁን ለእኔ አስደሳች የሚመስሉኝን አሳይሻለሁ። Clang Static Analyzer እነዚህን ስህተቶች ማግኘት አልቻለም (ወይም በእሱ እርዳታ ይህን ለማድረግ በጣም ምቹ አይደለም)። ግን እንችላለን። ከዚህም በላይ እነዚህን ሁሉ ስህተቶች በአንድ ምሽት አግኝቼ ጻፍኩ.

ነገር ግን ጽሑፉን ለመጻፍ ብዙ ሳምንታት ፈጅቷል. ይህንን ሁሉ ወደ ጽሑፍ ለማስገባት ራሴን ማምጣት አልቻልኩም :).

በነገራችን ላይ በ PVS-Studio analyzer ውስጥ ስህተቶችን እና ሊሆኑ የሚችሉ ተጋላጭነቶችን ለመለየት ምን ቴክኖሎጂዎች ጥቅም ላይ እንደሚውሉ ፍላጎት ካሎት ከዚህ ጋር ለመተዋወቅ ሀሳብ አቀርባለሁ ። ማስታወሻ.

አዲስ እና አሮጌ ምርመራዎች

ቀደም ሲል እንደተገለፀው፣ ከሁለት አመት በፊት የኤልኤልቪኤም ፕሮጀክት በድጋሚ ተፈትሸው የተገኙት ስህተቶች ተስተካክለዋል። አሁን ይህ ጽሑፍ አዲስ የስህተት ስብስቦችን ያቀርባል. ለምን አዲስ ስህተቶች ተገኙ? ለዚህ 3 ምክንያቶች አሉ፡-

  1. የኤልኤልቪኤም ፕሮጄክቱ እየተሻሻለ፣ የድሮ ኮድ እየቀየረ እና አዲስ ኮድ በመጨመር ላይ ነው። በተፈጥሮ፣ በተሻሻለው እና በተጻፈው ኮድ ውስጥ አዳዲስ ስህተቶች አሉ። ይህ በግልጽ የሚያሳየው የማይንቀሳቀስ ትንታኔ በመደበኛነት ጥቅም ላይ መዋል አለበት እንጂ አልፎ አልፎ አይደለም። ጽሑፎቻችን የ PVS-Studio analyzerን ችሎታዎች በደንብ ያሳያሉ, ነገር ግን ይህ የኮድ ጥራትን ከማሻሻል እና ስህተቶችን ለማስተካከል ወጪን ከመቀነስ ጋር ምንም ግንኙነት የለውም. የማይንቀሳቀስ ኮድ ተንታኝ በመደበኛነት ይጠቀሙ!
  2. ያሉትን ምርመራዎች እያጠናቀቅን እና እያሻሻልን ነው። ስለዚህ ተንታኙ በቀደሙት ቅኝቶች ወቅት ያላስተዋላቸውን ስህተቶች መለየት ይችላል።
  3. ከ2 አመት በፊት ያልነበረ አዲስ ምርመራ በPVS-Studio ታይቷል። የ PVS-ስቱዲዮን እድገት በግልጽ ለማሳየት በተለየ ክፍል ውስጥ ለማጉላት ወሰንኩ.

ከ 2 ዓመታት በፊት በነበሩት ምርመራዎች ተለይተው የሚታወቁ ጉድለቶች

ቁራጭ N1: ቅዳ-ለጥፍ

static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
  if (Name == "addcarryx.u32" || // Added in 8.0
    ....
    Name == "avx512.mask.cvtps2pd.128" || // Added in 7.0
    Name == "avx512.mask.cvtps2pd.256" || // Added in 7.0
    Name == "avx512.cvtusi2sd" || // Added in 7.0
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name == "sse2.pmulu.dq" || // Added in 7.0
    Name == "sse41.pmuldq" || // Added in 7.0
    Name == "avx2.pmulu.dq" || // Added in 7.0
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V501 [CWE-570] በ«||» በግራ እና በስተቀኝ 'Name.startswith("avx512.mask.permvar.") ተመሳሳይ ንዑስ አገላለጾች አሉ። ኦፕሬተር. ራስ-አሻሽል.cpp 73

ስሙ በንኡስ ሕብረቁምፊ "avx512.mask.permvar" መጀመሩ በእጥፍ ተረጋግጧል። በሁለተኛው ቼክ ውስጥ, ሌላ ነገር ለመጻፍ እንደሚፈልጉ ግልጽ ነው, ነገር ግን የተቀዳውን ጽሑፍ ማረም ረስተዋል.

ቁራጭ N2: Typo

enum CXNameRefFlags {
  CXNameRange_WantQualifier = 0x1,
  CXNameRange_WantTemplateArgs = 0x2,
  CXNameRange_WantSinglePiece = 0x4
};

void AnnotateTokensWorker::HandlePostPonedChildCursor(
    CXCursor Cursor, unsigned StartTokenIndex) {
  const auto flags = CXNameRange_WantQualifier | CXNameRange_WantQualifier;
  ....
}

ማስጠንቀቂያ PVS-ስቱዲዮ፡ V501 ከ'|' በስተግራ እና በስተቀኝ 'CXNameRange_WantQualifier' ተመሳሳይ ንዑስ መግለጫዎች አሉ። ኦፕሬተር. ሲንደክስ.ሲፒ 7245

በታይፖ ምክንያት፣ ተመሳሳይ ስም ያለው ቋሚ ሁለት ጊዜ ጥቅም ላይ ይውላል CXNameRange_WantQualifier.

ክፍል N3፡ ከኦፕሬተር ቀዳሚነት ጋር ግራ መጋባት

int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
  ....
  if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
    return 0;
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V502 [CWE-783] ምናልባት የ'?:' ኦፕሬተር ከተጠበቀው በተለየ መንገድ ይሰራል። የ'?:' ኦፕሬተር ከ"==" ኦፕሬተር ያነሰ ቅድሚያ አለው። PPCTargetTransformInfo.cpp 404

በእኔ አስተያየት ይህ በጣም የሚያምር ስህተት ነው. አዎ, ስለ ውበት እንግዳ ሀሳቦች እንዳሉኝ አውቃለሁ :).

አሁን, መሠረት ኦፕሬተር ቅድሚያ የሚሰጡዋቸውን, አገላለጹ እንደሚከተለው ይገመገማል.

(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0

ከተግባራዊ እይታ አንጻር ሲታይ, እንዲህ ዓይነቱ ሁኔታ ትርጉም አይሰጥም, ምክንያቱም ሊቀንስ ይችላል-

(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())

ይህ ግልጽ ስህተት ነው። ምናልባትም ፣ 0/1 ከተለዋዋጭ ጋር ማነፃፀር ይፈልጉ ነበር። ማውጫ. ኮዱን ለመጠገን በሦስተኛው ኦፕሬተር ዙሪያ ቅንፎችን ማከል ያስፈልግዎታል:

if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))

በነገራችን ላይ, የ ternary ኦፕሬተር በጣም አደገኛ እና ምክንያታዊ ስህተቶችን ያስነሳል. በእሱ ላይ በጣም ይጠንቀቁ እና በቅንፍ ውስጥ ስግብግብ አይሁኑ። ይህንን ርዕስ በበለጠ ዝርዝር ተመለከትኩኝ እዚህ“ከ?፡ ከኦፕሬተር ተጠንቀቁ እና በቅንፍ ውስጥ ያስገቡት።

ቁራጭ N4፣ N5፡ ባዶ ጠቋሚ

Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
  ....
  TypedInit *LHS = dyn_cast<TypedInit>(Result);
  ....
  LHS = dyn_cast<TypedInit>(
    UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get())
      ->Fold(CurRec));
  if (!LHS) {
    Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() +
                    "' to string");
    return nullptr;
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V522 [CWE-476] የኑል አመልካች 'LHS' ውሳኔ ሊደረግ ይችላል። TGParser.cpp 2152

ጠቋሚው ከሆነ ኤል.ኤች.ኤስ. ዋጋ የለውም፣ ማስጠንቀቂያ ሊሰጥ ይገባል። ሆኖም፣ በምትኩ፣ ይህ ተመሳሳይ ባዶ ጠቋሚ ይሰረዛል፡- LHS->getAsString().

ማንም የማይፈትናቸው ስሕተት በስህተት ተቆጣጣሪ ውስጥ ሲደበቅ ይህ በጣም የተለመደ ሁኔታ ነው። የማይንቀሳቀስ ተንታኞች ምንም ያህል በተደጋጋሚ ጥቅም ላይ ቢውሉ ሁሉንም ሊደረስባቸው የሚችሉ ኮድ ይፈትሹ። ይህ የማይንቀሳቀስ ትንታኔ እንዴት ሌሎች የሙከራ እና የስህተት መከላከያ ዘዴዎችን እንደሚያሟላ የሚያሳይ በጣም ጥሩ ምሳሌ ነው።

ተመሳሳይ የጠቋሚ አያያዝ ስህተት ፖስት ከዚህ በታች ባለው ኮድ ውስጥ ተፈቅዶለታል፡ V522 [CWE-476] የኑል አመልካች 'RHS' ን ማጣራት ሊካሄድ ይችላል። TGParser.cpp 2186

ክፍል N6: ከተንቀሳቀሱ በኋላ ጠቋሚውን መጠቀም

static Expected<bool>
ExtractBlocks(....)
{
  ....
  std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
  ....
  BD.setNewProgram(std::move(ProgClone));                                // <=
  MiscompiledFunctions.clear();

  for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
    Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);  // <=
    assert(NewF && "Function not found??");
    MiscompiledFunctions.push_back(NewF);
  }
  ....
}

የ PVS-ስቱዲዮ ማስጠንቀቂያ፡ V522 [CWE-476] ባዶ ጠቋሚ 'ProgClone'ን መወሰን ሊካሄድ ይችላል። የተሳሳተ ስብስብ.cpp 601

መጀመሪያ ላይ ብልጥ ጠቋሚ ፕሮግክሎን የነገሩ ባለቤት መሆን ያቆማል፡-

BD.setNewProgram(std::move(ProgClone));

እንደውም አሁን ፕሮግክሎን ባዶ ጠቋሚ ነው። ስለዚህ፣ ባዶ ጠቋሚ ማዘዣ ከዚህ በታች መከሰት አለበት።

Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);

ግን, በእውነቱ, ይህ አይሆንም! ምልክቱ በትክክል እንዳልተገበረ ልብ ይበሉ።

በመያዣው መጀመሪያ ላይ የተሳሳቱ ተግባራት ጸድቷል፡

MiscompiledFunctions.clear();

በመቀጠል ፣ የዚህ መያዣ መጠን በ loop ሁኔታ ውስጥ ጥቅም ላይ ይውላል ።

for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {

ምልክቱ እንደማይጀምር ለማየት ቀላል ነው። ይህ ደግሞ ስህተት ነው ብዬ አስባለሁ እና ኮዱ በተለየ መንገድ መፃፍ አለበት.

ያንን ታዋቂ የስህተት እኩልነት ያጋጠመን ይመስላል! አንድ ስህተት ሌላውን ይሸፍናል :)

ክፍል N7: ከተንቀሳቀሱ በኋላ ጠቋሚውን መጠቀም

static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
                                    std::unique_ptr<Module> Safe) {
  outs() << "  Optimizing functions being tested: ";
  std::unique_ptr<Module> Optimized =
      BD.runPassesOn(Test.get(), BD.getPassesToRun());
  if (!Optimized) {
    errs() << " Error running this sequence of passes"
           << " on the input program!n";
    BD.setNewProgram(std::move(Test));                       // <=
    BD.EmitProgressBitcode(*Test, "pass-error", false);      // <=
    if (Error E = BD.debugOptimizerCrash())
      return std::move(E);
    return false;
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V522 [CWE-476] የኑል ጠቋሚ 'ሙከራ' ውሳኔ ሊደረግ ይችላል። የተሳሳተ ስብስብ.cpp 709

እንደገና ተመሳሳይ ሁኔታ. መጀመሪያ ላይ የእቃው ይዘት ይንቀሳቀሳል, ከዚያም ምንም እንዳልተከሰተ ሆኖ ያገለግላል. በ C ++ ውስጥ የእንቅስቃሴ ፍቺዎች ከታዩ በኋላ ይህንን ሁኔታ በፕሮግራም ኮድ ውስጥ ብዙ ጊዜ አይቻለሁ። ለዚህ ነው የC++ ቋንቋን የምወደው! የራስዎን እግር ለመምታት ብዙ እና ብዙ አዳዲስ መንገዶች አሉ። የ PVS-ስቱዲዮ ተንታኝ ሁልጊዜ ሥራ ይኖረዋል :).

ቁራጭ N8፡ ባዶ ጠቋሚ

void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
  uint32_t TypeId = Symbol.getTypeId();
  auto Type = Symbol.getSession().getSymbolById(TypeId);
  if (Type)
    Printer << "<unknown-type>";
  else
    Type->dump(*this);
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V522 [CWE-476] ባዶ ጠቋሚ 'አይነት'ን መወሰን ሊከሰት ይችላል። PrettyFunctionDumper.cpp 233

ከስህተት ተቆጣጣሪዎች በተጨማሪ የህትመት ስራዎችን ማረም ብዙውን ጊዜ አይሞከርም. በፊታችን እንደዚህ ያለ ጉዳይ አለን። ተግባሩ ተጠቃሚውን እየጠበቀ ነው, ችግሮቹን ከመፍታት ይልቅ, ለማስተካከል ይገደዳል.

በትክክል ፦

if (Type)
  Type->dump(*this);
else
  Printer << "<unknown-type>";

ቁራጭ N9፡ ባዶ ጠቋሚ

void SearchableTableEmitter::collectTableEntries(
    GenericTable &Table, const std::vector<Record *> &Items) {
  ....
  RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
  if (!Ty)                                                              // <=
    PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
                    Table.Name + "' has incompatible type: " +
                    Ty->getAsString() + " vs. " +                       // <=
                    TI->getType()->getAsString());
   ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V522 [CWE-476] ባዶ ጠቋሚ 'ታይ'ን መወሰን ሊካሄድ ይችላል። ሊፈለግ የሚችል ጠረጴዛEmitter.cpp 614

ሁሉም ነገር ግልጽ ነው እና ማብራሪያ አያስፈልገውም ብዬ አስባለሁ.

ቁራጭ N10: Typo

bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
  ....
  auto &Identifier = *(Tokens.end() - 2);
  auto &Question = *(Tokens.end() - 1);
  ....
  Identifier->ColumnWidth += Question->ColumnWidth;
  Identifier->Type = Identifier->Type;                    // <=
  Tokens.erase(Tokens.end() - 1);
  return true;
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V570 የ'Identifier-> አይነት' ተለዋዋጭ ለራሱ ተመድቧል። FormatTokenLexer.cpp 249

ተለዋዋጭ ለራሱ መመደብ ምንም ፋይዳ የለውም. ምናልባት እነሱ ለመጻፍ ይፈልጉ ነበር-

Identifier->Type = Question->Type;

ክፍል N11፡ አጠራጣሪ እረፍት

void SystemZOperand::print(raw_ostream &OS) const {
  switch (Kind) {
    break;
  case KindToken:
    OS << "Token:" << getToken();
    break;
  case KindReg:
    OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
    break;
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V622 [CWE-478] የ'መቀየሪያ' መግለጫውን ለመመርመር ያስቡበት። ምናልባት የመጀመሪያው 'ኬዝ' ኦፕሬተር ጠፍቷል። ስርዓትZAsmParser.cpp 652

መጀመሪያ ላይ በጣም አጠራጣሪ ኦፕሬተር አለ። ቆረጣ. እዚህ ሌላ ነገር መጻፍ ረስተዋል?

ክፍልፋይ N12፡ ጠቋሚውን ከተከለከለ በኋላ በመፈተሽ ላይ

InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
  Function *Callee = CS.getCalledFunction();
  Function *Caller = CS.getCaller();
  TargetTransformInfo &TTI = TTIWP->getTTI(*Callee);

  if (!Callee || Callee->isDeclaration())
    return llvm::InlineCost::getNever("undefined callee");
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V595 [CWE-476] የ'Callee' ጠቋሚ በ nullptr ላይ ከመረጋገጡ በፊት ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ: 172, 174. AMDGPUInline.cpp 172

ጠቋሚ ካሊ መጀመሪያ ላይ ተግባሩ በሚጠራበት ጊዜ ይገለጻል ማግኘትTTI.

እና ከዚያ ይህ ጠቋሚ ለእኩልነት መረጋገጥ እንዳለበት ተለወጠ nullptr:

if (!Callee || Callee->isDeclaration())

ግን በጣም ዘግይቷል…

ክፍልፋይ N13 - N...፡ ጠቋሚውን ከተከለከለ በኋላ በመፈተሽ ላይ

በቀድሞው የኮድ ቁርጥራጭ ውስጥ የተብራራው ሁኔታ ልዩ አይደለም. እዚህ ይታያል፡-

static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B,
                               bool isBinary, bool isPrecise = false) {
  ....
  Function *CalleeFn = CI->getCalledFunction();
  StringRef CalleeNm = CalleeFn->getName();                 // <=
  AttributeList CalleeAt = CalleeFn->getAttributes();
  if (CalleeFn && !CalleeFn->isIntrinsic()) {               // <=
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V595 [CWE-476] የ'CalleeFn' ጠቋሚ በ nullptr ላይ ከመረጋገጡ በፊት ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 1079, 1081. SimplifyLibCalls.cpp 1079

እና እዚህ:

void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
                            const Decl *Tmpl, Decl *New,
                            LateInstantiatedAttrVec *LateAttrs,
                            LocalInstantiationScope *OuterMostScope) {
  ....
  NamedDecl *ND = dyn_cast<NamedDecl>(New);
  CXXRecordDecl *ThisContext =
    dyn_cast_or_null<CXXRecordDecl>(ND->getDeclContext());         // <=
  CXXThisScopeRAII ThisScope(*this, ThisContext, Qualifiers(),
                             ND && ND->isCXXInstanceMember());     // <=
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V595 [CWE-476] የ'ND' ጠቋሚ በ nullptr ላይ ከመረጋገጡ በፊት ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 532፣ 534. SemaTemplateInstantiateDecl.cpp 532

እና እዚህ:

  • V595 [CWE-476] የ'U' ጠቋሚ በ nullptr ላይ ከመረጋገጡ በፊት ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] የ'ND' ጠቋሚ በ nullptr ላይ ከመረጋገጡ በፊት ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 2149, 2151. SemaTemplateInstantiate.cpp 2149

እና ከዚያ በ V595 ቁጥር ማስጠንቀቂያዎችን ለማጥናት ፍላጎት የለኝም። ስለዚህ እዚህ ከተዘረዘሩት በተጨማሪ ተመሳሳይ ስህተቶች መኖራቸውን አላውቅም። በጣም አይቀርም።

ቁራጭ N17፣ N18፡ አጠራጣሪ ለውጥ

static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
                                           uint64_t &Encoding) {
  ....
  unsigned Size = RegSize;
  ....
  uint64_t NImms = ~(Size-1) << 1;
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V629 [CWE-190] '~(መጠን - 1) << 1' የሚለውን አገላለጽ ለመመርመር ያስቡበት። የ32-ቢት እሴትን ከቀጣይ መስፋፋት ጋር ወደ 64-ቢት አይነት መቀየር። AArch64AddressingModes.h 260

ስህተት ላይሆን ይችላል እና ኮዱ እንደታሰበው ይሰራል። ግን ይህ በግልጽ በጣም አጠራጣሪ ነው እና መፈተሽ አለበት።

ተለዋዋጭውን እንበል መጠን ከ 16 ጋር እኩል ነው, ከዚያም የኮዱ ደራሲ በተለዋዋጭ ውስጥ ለማግኘት አቅዷል NImms እሴት:

1111111111111111111111111111111111111111111111111111111111100000

ሆኖም ፣ በእውነቱ ውጤቱ የሚከተለው ይሆናል-

0000000000000000000000000000000011111111111111111111111111100000

እውነታው ግን ሁሉም ስሌቶች የሚከሰቱት ባለ 32-ቢት ያልተፈረመ ዓይነት በመጠቀም ነው. እና ከዚያ በኋላ ብቻ፣ ይህ ባለ 32-ቢት ያልተፈረመ አይነት በተዘዋዋሪ የሚሰፋ ይሆናል። uint64_t. በዚህ ሁኔታ, በጣም ጉልህ የሆኑ ቢትዎች ዜሮ ይሆናሉ.

ሁኔታውን እንደሚከተለው ማስተካከል ይችላሉ-

uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;

ተመሳሳይ ሁኔታ፡ V629 [CWE-190] 'Immr << 6' የሚለውን አገላለጽ ለመመርመር ያስቡበት። የ32-ቢት እሴትን ከቀጣይ መስፋፋት ጋር ወደ 64-ቢት አይነት መቀየር። AArch64AddressingModes.h 269

N19 ክፍልፋይ፡ ቁልፍ ቃል ይጎድላል ያለዚያ?

void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
  ....
  if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
    // VOP2b (v_add_u32, v_sub_u32 ...) dpp use "vcc" token.
    // Skip it.
    continue;
  } if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) {    // <=
    Op.addRegWithFPInputModsOperands(Inst, 2);
  } else if (Op.isDPPCtrl()) {
    Op.addImmOperands(Inst, 1);
  } else if (Op.isImm()) {
    // Handle optional arguments
    OptionalIdx[Op.getImmTy()] = I;
  } else {
    llvm_unreachable("Invalid operand type");
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V646 [CWE-670] የመተግበሪያውን አመክንዮ መፈተሽ ያስቡበት። ምናልባት 'ሌላ' ቁልፍ ቃል ይጎድላል። AMDGPUAsmParser.cpp 5655

እዚህ ምንም ስህተት የለም. ከመጀመሪያው ጊዜ-አግድ ጀምሮ if ጋር ያበቃል ቀጥል, ከዚያ ምንም አይደለም, ቁልፍ ቃል አለ ያለዚያ ኦር ኖት. በማንኛውም መንገድ ኮዱ በተመሳሳይ መልኩ ይሰራል. አሁንም አምልጦታል። ያለዚያ ኮዱን የበለጠ ግልጽ ያልሆነ እና አደገኛ ያደርገዋል። ወደፊት ከሆነ ቀጥል ይጠፋል, ኮዱ ሙሉ ለሙሉ በተለየ መንገድ መስራት ይጀምራል. በእኔ አስተያየት መጨመር ይሻላል ያለዚያ.

ቁራጭ N20፡ አራት አይነት ተመሳሳይ ምልክቶች

LLVM_DUMP_METHOD void Symbol::dump(raw_ostream &OS) const {
  std::string Result;
  if (isUndefined())
    Result += "(undef) ";
  if (isWeakDefined())
    Result += "(weak-def) ";
  if (isWeakReferenced())
    Result += "(weak-ref) ";
  if (isThreadLocalValue())
    Result += "(tlv) ";
  switch (Kind) {
  case SymbolKind::GlobalSymbol:
    Result + Name.str();                        // <=
    break;
  case SymbolKind::ObjectiveCClass:
    Result + "(ObjC Class) " + Name.str();      // <=
    break;
  case SymbolKind::ObjectiveCClassEHType:
    Result + "(ObjC Class EH) " + Name.str();   // <=
    break;
  case SymbolKind::ObjectiveCInstanceVariable:
    Result + "(ObjC IVar) " + Name.str();       // <=
    break;
  }
  OS << Result;
}

የPVS-ስቱዲዮ ማስጠንቀቂያዎች፡-

  • V655 [CWE-480] ገመዶቹ ተጣምረው ግን ጥቅም ላይ አልዋሉም። የ'ውጤት + ስም.str()' አገላለፅን ለመመርመር ያስቡበት። Symbol.cpp 32
  • V655 [CWE-480] ገመዶቹ ተጣምረው ግን ጥቅም ላይ አልዋሉም። የ'ውጤት +"(ObjC ክፍል)" + Name.str()" አገላለፅን ለመመርመር ያስቡበት። Symbol.cpp 35
  • V655 [CWE-480] ገመዶቹ ተጣምረው ግን ጥቅም ላይ አልዋሉም። የ'ውጤት +"(ObjC Class EH)" + Name.str()" አገላለፅን ለመመርመር ያስቡበት። Symbol.cpp 38
  • V655 [CWE-480] ገመዶቹ ተጣምረው ግን ጥቅም ላይ አልዋሉም። የ'ውጤት +"(ObjC IVar)" + Name.str()" አገላለፅን ለመመርመር ያስቡበት። Symbol.cpp 41

በአጋጣሚ፣ + ኦፕሬተሩ ከ += ኦፕሬተር ይልቅ ጥቅም ላይ ይውላል። ውጤቱ ትርጉም የሌላቸው ንድፎች ናቸው.

ክፍል N21፡ ያልተገለጸ ባህሪ

static void getReqFeatures(std::map<StringRef, int> &FeaturesMap,
                           const std::vector<Record *> &ReqFeatures) {
  for (auto &R : ReqFeatures) {
    StringRef AsmCondString = R->getValueAsString("AssemblerCondString");

    SmallVector<StringRef, 4> Ops;
    SplitString(AsmCondString, Ops, ",");
    assert(!Ops.empty() && "AssemblerCondString cannot be empty");

    for (auto &Op : Ops) {
      assert(!Op.empty() && "Empty operator");
      if (FeaturesMap.find(Op) == FeaturesMap.end())
        FeaturesMap[Op] = FeaturesMap.size();
    }
  }
}

አደገኛውን ኮድ እራስዎ ለማግኘት ይሞክሩ። እና መልሱን ወዲያውኑ ላለመመልከት ትኩረትን ለመከፋፈል ይህ ስዕል ነው-

የPVS-ስቱዲዮ ተንታኝን በመጠቀም በኤልኤልቪኤም 8 ውስጥ ስህተቶችን መፈለግ

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V708 [CWE-758] አደገኛ ግንባታ ጥቅም ላይ ይውላል፡ 'FeaturesMap[Op] = FeaturesMap.size()'፣ 'FeaturesMap' የ'ካርታ' ክፍል የሆነበት። ይህ ወደማይታወቅ ባህሪ ሊያመራ ይችላል። RISCVCompressInstEmitter.cpp 490

የችግር መስመር፡

FeaturesMap[Op] = FeaturesMap.size();

ኤለመንት ከሆነ Op አልተገኘም, ከዚያም በካርታው ውስጥ አዲስ ኤለመንት ተፈጠረ እና በዚህ ካርታ ውስጥ ያሉት ንጥረ ነገሮች ብዛት እዚያ ተጽፏል. ተግባሩ መጠራቱ የማይታወቅ ነው። ልክ አዲስ ንጥረ ነገር ከመጨመሩ በፊት ወይም በኋላ.

ክፍልፋይ N22-N24፡ ተደጋጋሚ ስራዎች

Error MachOObjectFile::checkSymbolTable() const {
  ....
  } else {
    MachO::nlist STE = getSymbolTableEntry(SymDRI);
    NType = STE.n_type;                              // <=
    NType = STE.n_type;                              // <=
    NSect = STE.n_sect;
    NDesc = STE.n_desc;
    NStrx = STE.n_strx;
    NValue = STE.n_value;
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V519 [CWE-563] የ'NType' ተለዋዋጭ በተከታታይ ሁለት ጊዜ እሴቶች ተሰጥቷል። ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ: 1663, 1664. MachOObjectFile.cpp 1664

እዚህ ትክክለኛ ስህተት ያለ አይመስለኝም። አላስፈላጊ ተደጋጋሚ ምደባ ብቻ። ግን አሁንም ስህተት ነው።

በተመሳሳይ፡-

  • V519 [CWE-563] የ'B.NDesc' ተለዋዋጭ እሴቶች በተከታታይ ሁለት ጊዜ ይመደባሉ. ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] ተለዋዋጭው በተከታታይ ሁለት ጊዜ እሴቶች ተሰጥቷል። ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ፡ 59, 61. coff2yaml.cpp 61

ክፍልፋይ N25-N27፡ ተጨማሪ ምደባዎች

አሁን ትንሽ ለየት ያለ የዳግም መመደብ ስሪት እንይ።

bool Vectorizer::vectorizeLoadChain(
    ArrayRef<Instruction *> Chain,
    SmallPtrSet<Instruction *, 16> *InstructionsProcessed) {
  ....
  unsigned Alignment = getAlignment(L0);
  ....
  unsigned NewAlign = getOrEnforceKnownAlignment(L0->getPointerOperand(),
                                                 StackAdjustedAlignment,
                                                 DL, L0, nullptr, &DT);
  if (NewAlign != 0)
    Alignment = NewAlign;
  Alignment = NewAlign;
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V519 [CWE-563] የ'አሰላለፍ' ተለዋዋጭ በተከታታይ ሁለት ጊዜ እሴቶች ተሰጥቷል። ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ፡ 1158፣ 1160. LoadStoreVectorizer.cpp 1160

ይህ በጣም እንግዳ የሆነ ኮድ ሲሆን ይመስላል ምክንያታዊ ስህተት የያዘ። መጀመሪያ ላይ, ተለዋዋጭ አሰላለፍ እንደ ሁኔታው ​​እሴት ይመደባል. እና ከዚያ ምደባው እንደገና ይከሰታል ፣ ግን አሁን ያለ ምንም ማረጋገጫ።

ተመሳሳይ ሁኔታዎች እዚህ ሊታዩ ይችላሉ-

  • V519 [CWE-563] የ'Effects' ተለዋዋጭ እሴቶች በተከታታይ ሁለት ጊዜ ይመደባሉ. ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ፡ 152፣ 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] የ'ExpectNoDerefChunk' ተለዋዋጭ እሴቶች በተከታታይ ሁለት ጊዜ ይመደባሉ. ምናልባት ይህ ስህተት ነው. መስመሮችን ያረጋግጡ: 4970, 4973. SemaType.cpp 4973

ክፍል N28: ሁልጊዜ እውነተኛ ሁኔታ

static int readPrefixes(struct InternalInstruction* insn) {
  ....
  uint8_t byte = 0;
  uint8_t nextByte;
  ....
  if (byte == 0xf3 && (nextByte == 0x88 || nextByte == 0x89 ||
                       nextByte == 0xc6 || nextByte == 0xc7)) {
    insn->xAcquireRelease = true;
    if (nextByte != 0x90) // PAUSE instruction support             // <=
      break;
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V547 [CWE-571] 'nextByte!= 0x90' አገላለጽ ሁልጊዜ እውነት ነው። X86DisassemblerDecoder.cpp 379

መፈተሽ ትርጉም የለውም። ተለዋዋጭ nextByte ሁልጊዜ ከዋጋው ጋር እኩል አይደለም 0x90, ይህም ከቀዳሚው ቼክ ይከተላል. ይህ አንዳንድ ዓይነት ምክንያታዊ ስህተት ነው።

ቁራጭ N29 - N...: ሁልጊዜ እውነት/ሐሰት ሁኔታዎች

ተንታኙ ብዙ ማስጠንቀቂያዎችን ይሰጣል አጠቃላይ ሁኔታ (V547) ወይም ከፊል (V560) ምንጊዜም እውነት ወይም ሐሰት ነው። ብዙውን ጊዜ እነዚህ ትክክለኛ ስህተቶች አይደሉም, ነገር ግን በቀላሉ ስሎፒ ኮድ, የማክሮ መስፋፋት ውጤት እና የመሳሰሉት ናቸው. ይሁን እንጂ እውነተኛ አመክንዮአዊ ስህተቶች በየጊዜው ስለሚከሰቱ እነዚህን ሁሉ ማስጠንቀቂያዎች መመልከቱ ምክንያታዊ ነው። ለምሳሌ፣ ይህ የኮድ ክፍል አጠራጣሪ ነው፡-

static DecodeStatus DecodeGPRPairRegisterClass(MCInst &Inst, unsigned RegNo,
                                   uint64_t Address, const void *Decoder) {
  DecodeStatus S = MCDisassembler::Success;

  if (RegNo > 13)
    return MCDisassembler::Fail;

  if ((RegNo & 1) || RegNo == 0xe)
     S = MCDisassembler::SoftFail;
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V560 [CWE-570] ሁኔታዊ አገላለጽ አንድ አካል ሁል ጊዜ ሐሰት ነው፡ RegNo == 0xe። ARMDisassembler.cpp 939

ቋሚው 0xE በአስርዮሽ ዋጋ 14 ነው። ምርመራ RegNo == 0xe ትርጉም የለውም ምክንያቱም ከሆነ RegNo > 13, ከዚያ ተግባሩ አፈፃፀሙን ያጠናቅቃል.

መታወቂያ V547 እና V560 ያላቸው ሌሎች ብዙ ማስጠንቀቂያዎች ነበሩ፣ ግን እንደዚሁ V595, እነዚህን ማስጠንቀቂያዎች ለማጥናት ፍላጎት አልነበረኝም. አንድ ጽሑፍ ለመጻፍ በቂ ቁሳቁስ እንዳለኝ ቀድሞውኑ ግልጽ ነበር :). ስለዚህ፣ PVS-Studioን በመጠቀም በኤልኤልቪኤም ውስጥ የዚህ አይነት ምን ያህል ስህተቶች ሊታወቁ እንደሚችሉ አይታወቅም።

እነዚህን ቀስቅሴዎች ማጥናት ለምን አሰልቺ እንደሆነ ምሳሌ እሰጣችኋለሁ. ተንታኙ ለሚከተለው ኮድ ማስጠንቀቂያ ሲሰጥ ፍጹም ትክክል ነው። ግን ይህ ስህተት አይደለም.

bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
                                          tok::TokenKind ClosingBraceKind) {
  bool HasError = false;
  ....
  HasError = true;
  if (!ContinueOnSemicolons)
    return !HasError;
  ....
}

PVS-Studio ማስጠንቀቂያ፡ V547 [CWE-570] አገላለጽ '!HasError' ምንጊዜም ሐሰት ነው። ያልተጠቀለለ መስመር ፓርሰር.cpp 1635

ቁራጭ N30፡ አጠራጣሪ መመለስ

static bool
isImplicitlyDef(MachineRegisterInfo &MRI, unsigned Reg) {
  for (MachineRegisterInfo::def_instr_iterator It = MRI.def_instr_begin(Reg),
      E = MRI.def_instr_end(); It != E; ++It) {
    return (*It).isImplicitDef();
  }
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V612 [CWE-670] በ loop ውስጥ ያለ ቅድመ ሁኔታ 'መመለስ'። R600OptimizeVectorRegisters.cpp 63

ይህ ስህተት ወይም የተለየ ቴክኒክ ነው ኮዱን ለሚያነቡ ፕሮግራመሮች። ይህ ንድፍ ምንም ነገር አይገልጽልኝም እና በጣም አጠራጣሪ ይመስላል. እንደዚያ አለመጻፍ ይሻላል :).

ደክሞኝል? ከዚያም ሻይ ወይም ቡና ለመሥራት ጊዜው አሁን ነው.

የPVS-ስቱዲዮ ተንታኝን በመጠቀም በኤልኤልቪኤም 8 ውስጥ ስህተቶችን መፈለግ

ጉድለቶች በአዲስ ምርመራዎች ተለይተው ይታወቃሉ

እኔ እንደማስበው 30 የድሮ ምርመራዎች ማግበር በቂ ነው። አሁን በአነቃቂው ውስጥ ከታዩት አዳዲስ ምርመራዎች ምን አስደሳች ነገሮች ሊገኙ እንደሚችሉ እንመልከት ቀዳሚ ቼኮች. በዚህ ጊዜ በጠቅላላው 66 አጠቃላይ ዓላማዎች ወደ C++ ተንታኝ ተጨምረዋል።

ክፍልፋይ N31፡ የማይደረስ ኮድ

Error CtorDtorRunner::run() {
  ....
  if (auto CtorDtorMap =
          ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
                    NoDependenciesToRegister, true))
  {
    ....
    return Error::success();
  } else
    return CtorDtorMap.takeError();

  CtorDtorsByPriority.clear();

  return Error::success();
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V779 [CWE-561] የማይደረስ ኮድ ተገኝቷል። ስህተት ሊኖር ይችላል. ExecutionUtils.cpp 146

እንደምታየው, ሁለቱም የኦፕሬተሩ ቅርንጫፎች if ወደ ኦፕሬተሩ በመደወል ያበቃል መመለስ. በዚህ መሠረት መያዣው የCtorDtorsByPriority ፈጽሞ አይጸዳም.

ክፍልፋይ N32፡ የማይደረስ ኮድ

bool LLParser::ParseSummaryEntry() {
  ....
  switch (Lex.getKind()) {
  case lltok::kw_gv:
    return ParseGVEntry(SummaryID);
  case lltok::kw_module:
    return ParseModuleEntry(SummaryID);
  case lltok::kw_typeid:
    return ParseTypeIdEntry(SummaryID);                        // <=
    break;                                                     // <=
  default:
    return Error(Lex.getLoc(), "unexpected summary kind");
  }
  Lex.setIgnoreColonInIdentifiers(false);                      // <=
  return false;
}

PVS-ስቱዲዮ ማስጠንቀቂያ፡- V779 [CWE-561] የማይደረስ ኮድ ተገኝቷል። ስህተት ሊኖር ይችላል. LLParser.cpp 835

አስደሳች ሁኔታ. በመጀመሪያ ይህንን ቦታ እንይ፡-

return ParseTypeIdEntry(SummaryID);
break;

በመጀመሪያ ሲታይ, እዚህ ምንም ስህተት የሌለ ይመስላል. ኦፕሬተሩን ይመስላል ቆረጣ እዚህ አንድ ተጨማሪ አለ, እና በቀላሉ መሰረዝ ይችላሉ. ሆኖም ግን, ሁሉም በጣም ቀላል አይደሉም.

ተንታኙ በመስመሮቹ ላይ ማስጠንቀቂያ ይሰጣል፡-

Lex.setIgnoreColonInIdentifiers(false);
return false;

እና በእርግጥ, ይህ ኮድ ሊደረስበት የማይችል ነው. ሁሉም ጉዳዮች በ ቀይር በኦፕሬተሩ ጥሪ ያበቃል መመለስ. እና አሁን ብቻውን ትርጉም የለሽ ቆረጣ ምንም ጉዳት የሌለው አይመስልም! ምናልባት ከቅርንጫፎቹ ውስጥ አንዱ ማለቅ አለበት ቆረጣ፣ ላይ አይደለም መመለስ?

ቁራጭ N33፡ የከፍተኛ ቢት በዘፈቀደ ዳግም ማስጀመር

unsigned getStubAlignment() override {
  if (Arch == Triple::systemz)
    return 8;
  else
    return 1;
}

Expected<unsigned>
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
                             const SectionRef &Section,
                             bool IsCode) {
  ....
  uint64_t DataSize = Section.getSize();
  ....
  if (StubBufSize > 0)
    DataSize &= ~(getStubAlignment() - 1);
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V784 የቢት ጭንብል መጠን ከመጀመሪያው ኦፕሬተር መጠን ያነሰ ነው. ይህ ከፍ ያለ ብስቶች መጥፋት ያስከትላል. RuntimeDyld.cpp 815

እባክዎን ተግባሩን ያስተውሉ getStubAligment የመመለሻ አይነት ያልተፈረመ. ተግባሩ እሴቱን 8 እንደሚመልስ በማሰብ የገለጻውን ዋጋ እናሰላ።

~(getStubAligment() - 1)

~(8u-1)

0xFFFFFFFF8u

አሁን ተለዋዋጭ መሆኑን ልብ ይበሉ የውሂብ መጠን ባለ 64-ቢት ያልተፈረመ አይነት አለው። የDataSize & 0xFFFFFFF8u ስራ ሲሰራ ሁሉም ሠላሳ ሁለት ባለከፍተኛ ቢት ወደ ዜሮ ይቀናበራል። ምናልባትም, ይህ ፕሮግራም አውጪው የፈለገው አይደለም. እሱ ለማስላት እንደፈለገ እጠራጠራለሁ፡ DataSize & 0xFFFFFFFFFFFFFFFF8u.

ስህተቱን ለማስተካከል, የሚከተለውን መጻፍ አለብዎት:

DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);

ወይም ስለዚህ:

DataSize &= ~(getStubAlignment() - 1ULL);

ቁርሾ N34፡ ያልተሳካ ግልጽ የሆነ ውሰድ

template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
                      SmallVectorImpl<T> &ScaledMask) {
  assert(0 < Scale && "Unexpected scaling factor");
  int NumElts = Mask.size();
  ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V1028 [CWE-190] ሊበዛ የሚችል የትርፍ ፍሰት። የ'NumElts * Scale' ኦፕሬተርን ወደ 'size_t' አይነት ለመውሰድ ያስቡበት እንጂ ውጤቱን አይደለም። X86ISelLowering.h 1577

የዓይነት ተለዋዋጮችን በማባዛት ጊዜ ከመጠን በላይ መፍሰስን ለማስወገድ ግልጽ ዓይነት መውሰድ ጥቅም ላይ ይውላል int. ነገር ግን፣ እዚህ ላይ ግልጽ የሆነ መውሰዱ ከትርፍ አይከላከልም። በመጀመሪያ፣ ተለዋዋጮቹ ይባዛሉ፣ እና ከዚያ በኋላ ብቻ የ32-ቢት የማባዛት ውጤት ወደ አይነቱ ይሰፋል። መጠን_t.

ቁራጭ N35፡ አልተሳካም ቅጂ-መለጠፍ

Instruction *InstCombiner::visitFCmpInst(FCmpInst &I) {
  ....
  if (!match(Op0, m_PosZeroFP()) && isKnownNeverNaN(Op0, &TLI)) {
    I.setOperand(0, ConstantFP::getNullValue(Op0->getType()));
    return &I;
  }
  if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
    I.setOperand(1, ConstantFP::getNullValue(Op0->getType()));        // <=
    return &I;
  }
  ....
}

V778 [CWE-682] ሁለት ተመሳሳይ የኮድ ቁርጥራጮች ተገኝተዋል። ምናልባት፣ ይህ የትየባ ነው እና 'Op1' ተለዋዋጭ ከ'Op0' ይልቅ ጥቅም ላይ መዋል አለበት። InstCombineCompares.cpp 5507

ይህ አዲስ አስደሳች ምርመራ ኮድ የተቀዳበትን እና በውስጡ አንዳንድ ስሞች መለወጥ የጀመሩበትን ሁኔታዎችን ይለያል ፣ ግን በአንድ ቦታ ላይ እነሱ አላስተካከሉም።

እባክዎ በሁለተኛው ብሎክ ውስጥ ተለውጠዋል ኦፕ0 ላይ ኦፕ1. ግን በአንድ ቦታ ላይ አላስተካከሉም. ምናልባትም እንዲህ ተብሎ መፃፍ ነበረበት።

if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
  I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
  return &I;
}

ቁራጭ N36: ተለዋዋጭ ግራ መጋባት

struct Status {
  unsigned Mask;
  unsigned Mode;

  Status() : Mask(0), Mode(0){};

  Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
    Mode &= Mask;
  };
  ....
};

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V1001 [CWE-563] የ'ሞድ' ተለዋዋጭ ተመድቧል ነገር ግን በተግባሩ መጨረሻ ጥቅም ላይ አይውልም። SIModeRegister.cpp 48

የተግባር ክርክሮችን ከክፍል አባላት ጋር ተመሳሳይ ስሞችን መስጠት በጣም አደገኛ ነው። ግራ መጋባት በጣም ቀላል ነው። በፊታችን እንደዚህ ያለ ጉዳይ አለን። ይህ አገላለጽ ትርጉም የለውም፡-

Mode &= Mask;

የተግባር ክርክር ይቀየራል። ይኼው ነው. ይህ ነጋሪ እሴት ከአሁን በኋላ ጥቅም ላይ አይውልም። ምናልባት እርስዎ እንደዚህ ብለው መጻፍ ነበረብዎት-

Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
  this->Mode &= Mask;
};

ቁራጭ N37: ተለዋዋጭ ግራ መጋባት

class SectionBase {
  ....
  uint64_t Size = 0;
  ....
};

class SymbolTableSection : public SectionBase {
  ....
};

void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
                                   SectionBase *DefinedIn, uint64_t Value,
                                   uint8_t Visibility, uint16_t Shndx,
                                   uint64_t Size) {
  ....
  Sym.Value = Value;
  Sym.Visibility = Visibility;
  Sym.Size = Size;
  Sym.Index = Symbols.size();
  Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
  Size += this->EntrySize;
}

ማስጠንቀቂያ PVS-ስቱዲዮ፡ V1001 [CWE-563] የ'መጠን' ተለዋዋጭ ተመድቧል ግን እስከ ተግባሩ መጨረሻ ድረስ ጥቅም ላይ አይውልም። Object.cpp 424

ሁኔታው ከቀዳሚው ጋር ተመሳሳይ ነው. ተብሎ መፃፍ አለበት።

this->Size += this->EntrySize;

ክፍልፋይ N38-N47: ጠቋሚውን ለመፈተሽ ረስተዋል

ቀደም ሲል, የምርመራ ቀስቅሴዎችን ምሳሌዎችን ተመልክተናል V595. ዋናው ነገር ጠቋሚው መጀመሪያ ላይ የተጠቀሰው እና ከዚያ በኋላ ብቻ ነው. ወጣት ምርመራዎች V1004 በትርጉም ተቃራኒ ነው, ነገር ግን ብዙ ስህተቶችን ያሳያል. ጠቋሚው መጀመሪያ ላይ የተፈተሸበት እና ከዚያ የተረሳበትን ሁኔታዎች ይለያል። በኤልኤልቪኤም ውስጥ የሚገኙትን እንደዚህ ያሉ ጉዳዮችን እንይ።

int getGEPCost(Type *PointeeType, const Value *Ptr,
               ArrayRef<const Value *> Operands) {
  ....
  if (Ptr != nullptr) {                                            // <=
    assert(....);
    BaseGV = dyn_cast<GlobalValue>(Ptr->stripPointerCasts());
  }
  bool HasBaseReg = (BaseGV == nullptr);

  auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());  // <=
  ....
}

የ PVS-ስቱዲዮ ማስጠንቀቂያ፡ V1004 [CWE-476] የ'Ptr' ጠቋሚው በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 729፣ 738. TargetTransformInfoImpl.h 738

ተለዋዋጭ Ptr እኩል ሊሆን ይችላል nullptrበቼኩ እንደተረጋገጠው፡-

if (Ptr != nullptr)

ሆኖም፣ ከዚህ አመልካች በታች ያለ ቅድመ ምርመራ ተዘርዝሯል፡-

auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());

ሌላ ተመሳሳይ ጉዳይ እንመልከት።

llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
                                                          bool Stub) {
  ....
  auto *FD = dyn_cast<FunctionDecl>(GD.getDecl());
  SmallVector<QualType, 16> ArgTypes;
  if (FD)                                                                // <=
    for (const ParmVarDecl *Parm : FD->parameters())
      ArgTypes.push_back(Parm->getType());
  CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv(); // <=
  ....
}

የ PVS-ስቱዲዮ ማስጠንቀቂያ፡ V1004 [CWE-476] የ'FD' ጠቋሚው በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ: 3228, 3231. CGDebugInfo.cpp 3231

ለምልክቱ ትኩረት ይስጡ FD. እርግጠኛ ነኝ ችግሩ በግልጽ የሚታይ እና ልዩ ማብራሪያ አያስፈልግም.

እና ተጨማሪ፡-

static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
                                         Value *&BasePtr,
                                         const DataLayout &DL) {
  PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType());
  if (!PtrTy) {                                                   // <=
    Result = Polynomial();
    BasePtr = nullptr;
  }
  unsigned PointerBits =
      DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());     // <=
  ....
}

የ PVS-ስቱዲዮ ማስጠንቀቂያ፡ V1004 [CWE-476] የ'PtrTy' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 960፣ 965. InterleavedLoadCombinePass.cpp 965

እራስዎን ከእንደዚህ አይነት ስህተቶች እንዴት እንደሚከላከሉ? በ Code-Review ላይ የበለጠ ትኩረት ይስጡ እና ኮድዎን በመደበኛነት ለመፈተሽ የPVS-Studio static analyzer ይጠቀሙ።

የዚህ አይነት ስህተቶች ያሉባቸው ሌሎች የኮድ ቁርጥራጮችን መጥቀስ ምንም ፋይዳ የለውም. በጽሁፉ ውስጥ የማስጠንቀቂያዎች ዝርዝርን ብቻ እተወዋለሁ-

  • V1004 [CWE-476] የ'Expr' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 1049፣ 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] የ'PI' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] የ'StatepointCall' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] የ'RV' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] የ'CalleeFn' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 1081፣ 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] የ'TC' ጠቋሚ በ nullptr ላይ ከተረጋገጠ በኋላ ደህንነቱ ባልተጠበቀ ሁኔታ ጥቅም ላይ ውሏል። መስመሮችን ያረጋግጡ፡ 1819፣ 1824. Driver.cpp 1824

ክፍልፋይ N48-N60፡ ወሳኝ አይደለም፣ ግን ጉድለት (ሊቻል የሚችል የማስታወስ ችሎታ መፍሰስ)

std::unique_ptr<IRMutator> createISelMutator() {
  ....
  std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
  Strategies.emplace_back(
      new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
  ....
}

የPVS-ስቱዲዮ ማስጠንቀቂያ፡- V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'ስልቶች' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-isel-fuzzer.cpp 58

እንደ መያዣው መጨረሻ ላይ አንድ ንጥረ ነገር ለመጨመር std :: ቬክተር > ዝም ብለህ መጻፍ አትችልም። xxx.push_back(አዲስ X), ከ የተዘዋዋሪ ልወጣ የለም ጀምሮ X* в std :: ልዩ_ptr.

አንድ የተለመደ መፍትሔ መጻፍ ነው xxx.emplace_back(አዲስ X)ስለሚያጠናቅቅ፡ ዘዴ emplace_back አንድ ኤለመንትን ከመከራከሪያዎቹ በቀጥታ ይገነባል እና ስለዚህ ግልጽ ገንቢዎችን መጠቀም ይችላል።

አስተማማኝ አይደለም. ቬክተሩ ሙሉ ከሆነ, ማህደረ ትውስታ እንደገና ይመደባል. የማህደረ ትውስታ ቦታ ማስያዝ ክዋኔ ሊሳካ ይችላል፣ በዚህም ምክንያት የተለየ መወርወርን ያስከትላል std :: መጥፎ_alloc. በዚህ አጋጣሚ ጠቋሚው ይጠፋል እና የተፈጠረው ነገር ፈጽሞ አይሰረዝም.

አስተማማኝ መፍትሔ መፍጠር ነው ልዩ_ptrቬክተሩ ማህደረ ትውስታን ወደ ሌላ ቦታ ለመቀየር ከመሞከሩ በፊት የጠቋሚው ባለቤት የሆነው፡

xxx.push_back(std::unique_ptr<X>(new X))

ከC++14 ጀምሮ 'std:: make_unique' መጠቀም ይችላሉ፡-

xxx.push_back(std::make_unique<X>())

ይህ ዓይነቱ ጉድለት ለኤልኤልቪኤም ወሳኝ አይደለም። ማህደረ ትውስታ መመደብ ካልተቻለ, ማጠናከሪያው በቀላሉ ይቆማል. ይሁን እንጂ ለረጅም ጊዜ መተግበሪያዎች የስራ ሰዓትየማህደረ ትውስታ ድልድል ካልተሳካ ዝም ብሎ ማቋረጥ የማይችል፣ ይህ እውነተኛ መጥፎ ስህተት ሊሆን ይችላል።

ስለዚህ ምንም እንኳን ይህ ኮድ ለኤልኤልቪኤም ተግባራዊ ስጋት ባይፈጥርም ፣ስለዚህ የስህተት ንድፍ ማውራት ጠቃሚ ሆኖ አግኝቼዋለሁ እና የ PVS-ስቱዲዮ ተንታኝ እሱን ለመለየት ተምሯል።

የዚህ አይነት ሌሎች ማስጠንቀቂያዎች፡-

  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'ማለፊያዎች' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. PassManager.h 546
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'AAs' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. አሊያስ ትንታኔ.ህ 324
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'የመግቢያ' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'AllEdges' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. CFGMST.h 268
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'VMaps' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'ሪኮርዶች' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. FDRLogBuilder.h 30
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'በመጠባበቅ ላይ ያሉ ንዑስ ሞዱሎች' መያዣ ውስጥ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. ModuleMap.cpp 810
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'ነገሮች' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. DebugMap.cpp 88
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'ስልቶች' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 685
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 686
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 688
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 689
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 690
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 691
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 692
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 693
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Modifiers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. lvm-stress.cpp 694
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'Operands' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ ወደ 'እስታሽ' መያዣ በ'emplace_back' ዘዴ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] ባለቤት የሌለው ጠቋሚ በ'emplace_back' ዘዴ ወደ 'Matchers' መያዣ ይታከላል። በተለየ ሁኔታ የማስታወስ ችሎታ መፍሰስ ይከሰታል. GlobalISelEmitter.cpp 2702

መደምደሚያ

በአጠቃላይ 60 ማስጠንቀቂያዎችን አውጥቼ ከዚያ አቆምኩ። የPVS-Studio analyzer በኤልኤልቪኤም ውስጥ የሚያገኛቸው ሌሎች ጉድለቶች አሉ? አዎ አለኝ። ነገር ግን፣ ለጽሁፉ ኮድ ቁርጥራጭ ስጽፍ፣ አመሻሹ ላይ ነበር፣ ወይም እንዲያውም ምሽት ነበር፣ እና አንድ ቀን ለመጥራት ጊዜው እንደደረሰ ወሰንኩኝ።

አስደሳች ሆኖ እንዳገኙት እና የPVS-Studio analyzerን መሞከር እንደሚፈልጉ ተስፋ አደርጋለሁ።

ተንታኙን አውርደው የማዕድን ስዊፐር ቁልፍን በ ላይ ማግኘት ይችላሉ። ይህ ገጽ.

ከሁሉም በላይ, ቋሚ ትንታኔን በመደበኛነት ይጠቀሙ. የአንድ ጊዜ ቼኮች, በእኛ የተካሄደው የስታቲክ ትንተና ዘዴን እና PVS-ስቱዲዮን ለማስተዋወቅ የተለመደ ሁኔታ አይደለም.

የኮድዎን ጥራት እና አስተማማኝነት ለማሻሻል መልካም ዕድል!

የPVS-ስቱዲዮ ተንታኝን በመጠቀም በኤልኤልቪኤም 8 ውስጥ ስህተቶችን መፈለግ

ይህን ጽሑፍ ለእንግሊዝኛ ተናጋሪ ታዳሚዎች ማጋራት ከፈለጉ፣ እባክዎ የትርጉም ማያያዣውን አንድሬ ካርፖቭ ይጠቀሙ። በኤልኤልቪኤም 8 ከPVS-ስቱዲዮ ጋር ሳንካዎችን መፈለግ.

ምንጭ: hab.com

አስተያየት ያክሉ