Han pasado más de dos años desde la última verificación del código del proyecto LLVM utilizando nuestro analizador PVS-Studio. Asegurémonos de que el analizador PVS-Studio sigue siendo la herramienta líder para detectar errores y vulnerabilidades potenciales. Para hacer esto, verifique y encuentre nuevos errores en la versión LLVM 8.0.0.
Artículo a escribir
Para ser sincero, no quería escribir este artículo. No es interesante escribir sobre un proyecto que ya hemos probado repetidamente (
1 ,
2 ,
3 ). Es mejor escribir sobre algo nuevo, pero no tengo otra opción.
Cada vez que se lanza una nueva versión de LLVM o
se actualiza
Clang Static Analyzer , aparecen las siguientes preguntas en nuestro correo:
¡Mira, la nueva versión de Clang Static Analyzer ha aprendido a encontrar nuevos errores! Me parece que la relevancia del uso de PVS-Studio está disminuyendo. Clang encuentra más errores que antes y se pone al día con las capacidades de PVS-Studio. ¿Qué opinas sobre esto?A esto, siempre quiero responder algo en el espíritu:
¡Tampoco estamos sentados ociosos! Hemos mejorado significativamente las capacidades del analizador PVS-Studio. Así que no te preocupes, seguimos liderando, como antes.
Lamentablemente, esta es una mala respuesta. No hay pruebas en ello. Y es por eso que estoy escribiendo este artículo ahora. Entonces, el proyecto LLVM ha sido probado nuevamente y se han encontrado una variedad de errores. Los que me parecieron interesantes, ahora lo demostraré. El Analizador estático de Clang no puede encontrar estos errores (o es extremadamente inconveniente hacerlo). Y nosotros podemos. Y encontré y escribí todos estos errores en una noche.
Pero la redacción del artículo se prolongó durante varias semanas. No pude obligarme a organizar todo esto en forma de texto :).
Por cierto, si está interesado en qué tecnologías se utilizan en el analizador PVS-Studio para detectar errores y vulnerabilidades potenciales, le sugiero que se familiarice con esta
nota .
Diagnósticos nuevos y viejos
Como ya se señaló, hace aproximadamente dos años, el proyecto LLVM se verificó una vez más y se corrigieron los errores encontrados. Ahora en este artículo se presentará una nueva porción de errores. ¿Por qué se encontraron nuevos errores? Hay 3 razones para esto:
- El proyecto LLVM se desarrolla, el código antiguo cambia y aparece uno nuevo. Naturalmente, hay nuevos errores en el código modificado y escrito. Esto demuestra bien que el análisis estático debe aplicarse regularmente, y no de un caso a otro. Nuestros artículos muestran bien las capacidades del analizador PVS-Studio, pero esto no tiene nada que ver con mejorar la calidad del código y reducir el costo de corregir errores. ¡Use un analizador de código estático regularmente!
- Estamos finalizando y mejorando los diagnósticos existentes. Por lo tanto, el analizador puede detectar errores que no se notaron durante las comprobaciones anteriores.
- PVS-Studio introdujo nuevos diagnósticos, que no eran hace 2 años. Decidí separarlos en una sección separada para demostrar claramente el desarrollo de PVS-Studio.
Defectos identificados por diagnósticos que existían hace 2 años
Fragmento N1: Copiar-Pegarstatic bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) { if (Name == "addcarryx.u32" ||
Advertencia de PVS-Studio:
V501 [CWE-570] Hay subexpresiones idénticas 'Name.startswith ("avx512.mask.permvar.")' A la izquierda y a la derecha de '||' operador AutoUpgrade.cpp 73
Se verifica dos veces que el nombre comienza con la subcadena "avx512.mask.permvar". En la segunda prueba, claramente querían escribir algo más, pero olvidaron arreglar el texto copiado.
Fragmento N2: Error tipográfico 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; .... }
Advertencia de PVS-Studio: V501 Hay subexpresiones idénticas 'CXNameRange_WantQualifier' a la izquierda y a la derecha de '|' operador CIndex.cpp 7245
Debido a un error tipográfico, la misma constante con nombre
CXNameRange_WantQualifier se usa dos veces .
Fragmento N3: confusión sobre las prioridades del operador int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) { .... if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0) return 0; .... }
Advertencia de PVS-Studio:
V502 [CWE-783] Quizás el operador '?:' Funcione de una manera diferente a la esperada. El operador '?:' Tiene una prioridad menor que el operador '=='. PPCTargetTransformInfo.cpp 404
En mi opinión, este es un error muy hermoso. Sí, sé que tengo ideas extrañas sobre la belleza :).
Ahora, de acuerdo con las
prioridades de los operadores , la expresión se calcula de la siguiente manera:
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
Desde un punto de vista práctico, tal condición no tiene sentido, ya que puede reducirse a:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
Este es un claro error. Lo más probable es que 0/1 quisiera comparar con la variable
Índice . Para arreglar el código, agregue corchetes alrededor del operador ternario:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
Por cierto, el operador ternario es muy peligroso y provoca errores lógicos. Ten mucho cuidado con eso y no seas codicioso para poner paréntesis. Considere este tema con más detalle
aquí en el capítulo "¿Temer al operador?: Y encerrarlo entre paréntesis".
Fragmento N4, N5: puntero nulo 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; } .... }
Advertencia de PVS-Studio:
V522 [CWE-476] Puede tener lugar la desreferenciación del puntero nulo 'LHS'. TGParser.cpp 2152
Si el puntero
LHS es nulo, se debe emitir una advertencia. Sin embargo, en cambio, esto desreferenciará este puntero muy nulo:
LHS-> getAsString () .
Esta es una situación muy típica cuando el error está oculto en el controlador de errores, ya que nadie los está probando. Los analizadores estáticos verifican todo el código accesible, sin importar con qué frecuencia se use. Este es un muy buen ejemplo de cómo el análisis estático complementa otras pruebas y técnicas de protección contra errores.
Se produce un error similar al procesar el puntero
RHS en el siguiente código: V522 [CWE-476] Puede producirse una desreferenciación del puntero nulo 'RHS'. TGParser.cpp 2186
Fragmento N6: Usando el cursor después de mover static Expected<bool> ExtractBlocks(....) { .... std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap); .... BD.setNewProgram(std::move(ProgClone));
Advertencia de PVS-Studio: V522 [CWE-476] Puede tener lugar la desreferenciación del puntero nulo 'ProgClone'. Miscompilation.cpp 601
Al principio, el puntero inteligente
ProgClone deja de ser dueño del objeto:
BD.setNewProgram(std::move(ProgClone));
De hecho, ahora
ProgClone es un puntero nulo. Por lo tanto, una desreferenciación del puntero nulo debería ocurrir justo debajo:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
Pero, de hecho, ¡esto no sucederá! Tenga en cuenta que el bucle no se está ejecutando realmente.
Al principio, se
borra el contenedor
MiscompiledFunctions :
MiscompiledFunctions.clear();
A continuación, el tamaño de este contenedor se usa en la condición de bucle:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Es fácil ver que el ciclo no se inicia. Creo que esto también es un error, y el código debe escribirse de manera diferente.
¡Parece que conocimos esa famosa paridad de errores! Un error disfraza a otro :).
Fragmento N7: Usando el cursor después de mover 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));
Advertencia de PVS-Studio: V522 [CWE-476] Puede tener lugar la referencia del puntero nulo 'Prueba'. Miscompilation.cpp 709
De nuevo la misma situación. Al principio, el contenido del objeto se mueve y luego se usa como si nada hubiera pasado. Cada vez más veo esta situación en el código del programa, después de que la semántica del movimiento apareció en C ++. ¡Por esto me encanta el lenguaje C ++! Hay más y más nuevas formas de dispararle a su propia pierna. El analizador PVS-Studio siempre funcionará :).
Fragmento N8: Puntero Nulo 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); }
Advertencia de PVS-Studio: V522 [CWE-476] Puede tener lugar la desreferenciación del puntero nulo 'Tipo'. PrettyFunctionDumper.cpp 233
Además de los controladores de errores, las funciones para depurar impresiones generalmente no se prueban. Ante nosotros es un caso así. La función está esperando a un usuario que, en lugar de resolver sus problemas, se verá obligado a solucionarlo.
Correctamente:
if (Type) Type->dump(*this); else Printer << "<unknown-type>";
Fragmento N9: Puntero Nulo void SearchableTableEmitter::collectTableEntries( GenericTable &Table, const std::vector<Record *> &Items) { .... RecTy *Ty = resolveTypes(Field.RecType, TI->getType()); if (!Ty)
Advertencia de PVS-Studio: V522 [CWE-476] Puede tener lugar la desreferenciación del puntero nulo 'Ty'. SearchableTableEmitter.cpp 614
Creo, y todo está claro y no requiere explicación.
Fragmento N10: Error tipográfico bool FormatTokenLexer::tryMergeCSharpNullConditionals() { .... auto &Identifier = *(Tokens.end() - 2); auto &Question = *(Tokens.end() - 1); .... Identifier->ColumnWidth += Question->ColumnWidth; Identifier->Type = Identifier->Type;
PVS-Studio Advertencia:
V570 La variable 'Identificador-> Tipo' se asigna a sí misma. FormatTokenLexer.cpp 249
No tiene sentido asignar una variable a sí mismo. Lo más probable es que quisieran escribir:
Identifier->Type = Question->Type;
Fragmento N11: ruptura sospechosa void SystemZOperand::print(raw_ostream &OS) const { switch (Kind) { break; case KindToken: OS << "Token:" << getToken(); break; case KindReg: OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg()); break; .... }
Advertencia de PVS-Studio:
V622 [CWE-478] Considere inspeccionar la declaración 'switch'. Es posible que falte el primer operador de 'caso'. SystemZAsmParser.cpp 652
Al principio hay una declaración de
ruptura muy sospechosa. ¿Has olvidado escribir algo más aquí?
Fragmento N12: Verificación del puntero después de desreferenciar 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"); .... }
Advertencia de PVS-Studio:
V595 [CWE-476] El puntero 'Callee' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 172, 174. AMDGPUInline.cpp 172
El puntero de
Callee al principio se desreferencia cuando se
llama a la función
getTTI .
Y luego resulta que este puntero debe verificarse para la igualdad
nullptr :
if (!Callee || Callee->isDeclaration())
Pero es muy tarde ...
Fragmento N13 - N ...: Verificación del puntero después de desreferenciarLa situación discutida en el fragmento de código anterior no es única. Ella se encuentra aquí:
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B, bool isBinary, bool isPrecise = false) { .... Function *CalleeFn = CI->getCalledFunction(); StringRef CalleeNm = CalleeFn->getName();
Advertencia de PVS-Studio: V595 [CWE-476] El puntero 'CalleeFn' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 1079, 1081. SimplifyLibCalls.cpp 1079
Y aqui:
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());
Advertencia de PVS-Studio: V595 [CWE-476] El puntero 'ND' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Y aqui:
- V595 [CWE-476] El puntero 'U' se utilizó antes de que se verificara contra nullptr. Verifique las líneas: 404, 407. DWARFFormValue.cpp 404
- V595 [CWE-476] El puntero 'ND' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Y luego no me interesó estudiar las advertencias con el número V595. Por lo tanto, no sé si hay errores similares además de los enumerados aquí. Lo más probable es que lo haya.
Fragmento N17, N18: cambio sospechoso static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize, uint64_t &Encoding) { .... unsigned Size = RegSize; .... uint64_t NImms = ~(Size-1) << 1; .... }
Advertencia de PVS-Studio:
V629 [CWE-190] Considere inspeccionar la expresión '~ (Tamaño - 1) << 1'. Desplazamiento de bits del valor de 32 bits con una expansión posterior al tipo de 64 bits. AArch64AddressingModes.h 260
Quizás esto no sea un error, y el código funciona exactamente como se esperaba. Pero este es claramente un lugar muy sospechoso, y necesita ser revisado.
Suponga que la variable
Tamaño es 16, y luego el autor del código planeó obtener el valor en la variable
NImms :
11111111111111111111111111111111111111111111111111111111111111100000000
Sin embargo, de hecho, el resultado es:
0000000000000000000000000000000000001111111111111111111111111111100000
El hecho es que todos los cálculos se realizan utilizando el tipo sin signo de 32 bits. Y solo entonces, este tipo sin signo de 32 bits se expandirá implícitamente a
uint64_t . En este caso, los bits más significativos serán cero.
Puedes arreglar la situación así:
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
Situación similar: V629 [CWE-190] Considere inspeccionar la expresión 'Immr << 6'. Desplazamiento de bits del valor de 32 bits con una expansión posterior al tipo de 64 bits. AArch64AddressingModes.h 269
Fragmento N19: ¿Falta otra palabra clave ? void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) { .... if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
Advertencia de PVS-Studio:
V646 [CWE-670] Considere inspeccionar la lógica de la aplicación. Es posible que falte la palabra clave 'else'. AMDGPUAsmParser.cpp 5655
No hay error aquí. Dado que el bloque del primer
if termina con
continuar , no importa
si hay una palabra clave
más o no. En cualquier caso, el código funcionará igual. Sin embargo, la falta de otra
cosa hace que el código sea más oscuro y peligroso. Si en el futuro
continúa desapareciendo, entonces el código comenzará a funcionar de una manera completamente diferente. En mi opinión, es mejor agregar
más .
Fragmento N20: cuatro errores tipográficos del mismo tipo 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();
Advertencias de PVS-Studio:
- V655 [CWE-480] Las cadenas se concatenaron pero no se utilizan. Considere inspeccionar la expresión 'Result + Name.str ()'. Symbol.cpp 32
- V655 [CWE-480] Las cadenas se concatenaron pero no se utilizan. Considere inspeccionar la expresión 'Resultado + "(Clase ObjC)" + Nombre.str ()'. Symbol.cpp 35
- V655 [CWE-480] Las cadenas se concatenaron pero no se utilizan. Considere inspeccionar la expresión 'Resultado + "(ObjC Clase EH)" + Nombre.str ()'. Symbol.cpp 38
- V655 [CWE-480] Las cadenas se concatenaron pero no se utilizan. Considere inspeccionar la expresión 'Resultado + "(ObjC IVar)" + Nombre.str ()'. Symbol.cpp 41
Por casualidad, se usa el operador + en lugar del operador + =. El resultado son diseños sin sentido.
Fragmento N21: comportamiento indefinido 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(); } } }
Intenta encontrar el código peligroso tú mismo. Y esta es una imagen de distracción, para no mirar de inmediato la respuesta:
Advertencia de PVS-Studio:
V708 [CWE-758] Se utiliza una construcción peligrosa: 'FeaturesMap [Op] = FeaturesMap.size ()', donde 'FeaturesMap' es de la clase 'map'. Esto puede conducir a un comportamiento indefinido. RISCVCompressInstEmitter.cpp 490
Línea del problema:
FeaturesMap[Op] = FeaturesMap.size();
Si no se encuentra el elemento
Op , se crea un nuevo elemento en el mapa y allí se escribe el número de elementos en este mapa. Simplemente no se sabe si la función de
tamaño se llamará antes o después de agregar un nuevo elemento.
Fragmento N22-N24: reasignaciones Error MachOObjectFile::checkSymbolTable() const { .... } else { MachO::nlist STE = getSymbolTableEntry(SymDRI); NType = STE.n_type;
Advertencia de PVS-Studio:
V519 [CWE-563] A la variable 'NType' se le asignan valores dos veces sucesivamente. Quizás esto sea un error. Líneas de verificación: 1663, 1664. MachOObjectFile.cpp 1664
Creo que no hay ningún error real aquí. Solo tarea repetida superflua. Pero sigue siendo un error.
Del mismo modo:
- V519 [CWE-563] A la variable 'B.NDesc' se le asignan valores dos veces seguidas. Quizás esto sea un error. Líneas de verificación: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] A la variable se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas: 59, 61. coff2yaml.cpp 61
Fragmento N25-N27: más reasignacionesAhora considere una opción de reasignación ligeramente diferente.
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; .... }
Advertencia de PVS-Studio: V519 [CWE-563] La variable 'Alineación' recibe valores asignados dos veces sucesivamente. Quizás esto sea un error. Líneas de verificación: 1158, 1160. LoadStoreVectorizer.cpp 1160
Este es un código muy extraño que parece contener un error lógico. Al principio, a la variable
Alineación se le asigna un valor según la condición. Y luego la asignación ocurre nuevamente, pero ahora sin ninguna verificación.
Situaciones similares se pueden ver aquí:
- V519 [CWE-563] A la variable 'Efectos' se le asignan valores dos veces seguidas. Quizás esto sea un error. Líneas de verificación: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] La variable 'ExpectNoDerefChunk' recibe valores asignados dos veces sucesivamente. Quizás esto sea un error. Líneas de verificación: 4970, 4973. SemaType.cpp 4973
Fragmento N28: siempre una condición verdadera 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)
Advertencia de PVS-Studio:
V547 [CWE-571] La expresión 'nextByte! = 0x90' siempre es verdadera. X86DisassemblerDecoder.cpp 379
La verificación no tiene sentido. La variable
nextByte no siempre es igual a
0x90 , que se deduce de la verificación anterior. Este es algún tipo de error lógico.
Fragmento N29 - N ...: Siempre condiciones verdaderas / falsasEl analizador da muchas advertencias de que toda la condición (
V547 ) o parte de ella (
V560 ) siempre es verdadera o falsa. A menudo, estos no son errores reales, sino simplemente código inexacto, el resultado del despliegue de macros y similares. Sin embargo, tiene sentido observar todas estas advertencias, ya que de vez en cuando hay errores lógicos reales. Por ejemplo, este fragmento de código es sospechoso:
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; .... }
Advertencia de PVS-Studio:
V560 [CWE-570] Una parte de la expresión condicional siempre es falsa: RegNo == 0xe. ARMDisassembler.cpp 939
La constante 0xE es el valor 14 en el sistema decimal. Comprobar
RegNo == 0xe no tiene sentido, porque si
RegNo> 13 , entonces la función completará su ejecución.
Hubo muchas otras advertencias con los identificadores V547 y V560, pero, como en el caso de
V595 , no estaba interesado en estudiar estas advertencias. Ya estaba claro que tenía suficiente material para escribir un artículo :). Por lo tanto, no se sabe cuántos errores de este tipo se pueden detectar en LLVM utilizando PVS-Studio.
Daré un ejemplo de por qué es aburrido estudiar estas respuestas. El analizador tiene toda la razón al emitir una advertencia para el siguiente código. Pero esto no es un error.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons, tok::TokenKind ClosingBraceKind) { bool HasError = false; .... HasError = true; if (!ContinueOnSemicolons) return !HasError; .... }
Advertencia de PVS-Studio: V547 [CWE-570] La expresión '! HasError' siempre es falsa. UnwrappedLineParser.cpp 1635
Fragmento N30: regreso sospechoso 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(); } .... }
Advertencia de PVS-Studio:
V612 [CWE-670] Un 'retorno' incondicional dentro de un bucle. R600OptimizeVectorRegisters.cpp 63
Esto es un error o un truco específico que pretende explicar algo a los programadores que leen el código. Este diseño no me explica nada y parece muy sospechoso. Es mejor no escribir así :).
Estas cansado Luego es hora de hacer té o café.
Defectos detectados por nuevos diagnósticos
Creo que 30 disparos de diagnósticos antiguos es suficiente. Veamos ahora qué es interesante se puede encontrar con los nuevos diagnósticos que aparecieron en el analizador después de una verificación
previa . En total, se agregaron 66 diagnósticos de uso general al analizador C ++ durante este tiempo.
Fragmento N31: código inalcanzable 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(); }
Advertencia de PVS-Studio:
V779 [CWE-561] Código inalcanzable detectado. Es posible que haya un error presente. ExecutionUtils.cpp 146
Como puede ver, ambas ramas de la
declaración if terminan con una llamada a la
declaración return . En consecuencia, el contenedor
CtorDtorsByPriority nunca se
vaciará .
Fragmento N32: Código inalcanzable 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);
Advertencia de PVS-Studio: V779 [CWE-561] Código inalcanzable detectado. Es posible que haya un error presente. LLParser.cpp 835
Una situación interesante Veamos el comienzo de este lugar:
return ParseTypeIdEntry(SummaryID); break;
A primera vista, parece que no hay error. Parece que la declaración de
ruptura es superflua aquí, y simplemente puede eliminarla. Sin embargo, no todo es tan simple.
El analizador genera una advertencia en las líneas:
Lex.setIgnoreColonInIdentifiers(false); return false;
De hecho, este código es inalcanzable. Todos los casos en
switch finalizan con una llamada a la
declaración de devolución . ¡Y ahora el inútil
descanso solitario no parece tan inofensivo! ¿Quizás una de las ramas debería terminar con
descanso y no con
retorno ?
Fragmento N33: Accidentalización de bits altos 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); .... }
Advertencia de PVS-Studio:
V784 El tamaño de la máscara de bits es menor que el tamaño del primer operando. Esto causará la pérdida de bits más altos. RuntimeDyld.cpp 815
Tenga en cuenta que la función
getStubAlignment devuelve un tipo
sin signo . Calculamos el valor de la expresión si suponemos que la función devuelve el valor 8:
~ (getStubAlignment () - 1)
~ (8u-1)
0xFFFFFFF8u
Ahora observe que la variable
DataSize tiene un tipo sin signo de 64 bits. Resulta que durante la operación DataSize & 0xFFFFFFF88, se restablecerán los treinta y dos bits de orden superior. Lo más probable es que esto no sea lo que el programador quería. Sospecho que quería calcular: DataSize & 0xFFFFFFFFFFFFFFFFF8u.
Para corregir el error, debe escribir así:
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Más o menos:
DataSize &= ~(getStubAlignment() - 1ULL);
Fragmento N34: lanzamiento explícito fallido 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); .... }
Advertencia de PVS-Studio:
V1028 [CWE-190] Posible desbordamiento. Considere convertir los operandos del operador 'NumElts * Scale' al tipo 'size_t', no el resultado. X86ISelLowering.h 1577
La conversión explícita de tipos se utiliza para evitar el desbordamiento al multiplicar variables de tipo
int . Sin embargo, la transmisión explícita aquí no protege contra el desbordamiento.
Al principio, las variables se multiplicarán, y solo entonces el resultado de la multiplicación de 32 bits se expandirá al tipo size_t .Fragmento N35: Copiar y pegar sin éxito 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()));
V778 [CWE-682] Se encontraron dos fragmentos de código similares. Quizás, este es un error tipográfico y se debe usar la variable 'Op1' en lugar de 'Op0'. InstCombineCompares.cpp 5507Este nuevo diagnóstico interesante revela situaciones en las que se copió un fragmento de código y algunos nombres comenzaron a cambiar, pero no se corrigieron en un solo lugar.Tenga en cuenta que en el segundo bloque cambiaron Op0 a Op1 . Pero en un lugar no lo arreglaron. Lo más probable es que debería haberse escrito así: if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) { I.setOperand(1, ConstantFP::getNullValue(Op1->getType())); return &I; }
Fragmento N36: Confusión en variables struct Status { unsigned Mask; unsigned Mode; Status() : Mask(0), Mode(0){}; Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) { Mode &= Mask; }; .... };
Advertencia de PVS-Studio: V1001 [CWE-563] La variable 'Modo' se asigna pero no se usa al final de la función. SIModeRegister.cpp 48Es muy peligroso dar a los argumentos de función los mismos nombres que los miembros de la clase. Muy fácil confundirse. Ante nosotros es un caso así. Esta expresión no tiene sentido: Mode &= Mask;
El argumento de la función cambia. Y eso es todo.
Este argumento ya no se usa. Lo más probable es que fuera necesario escribir así: Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) { this->Mode &= Mask; };
Fragmento N37: Confusión en variables 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; }
Advertencia de PVS-Studio: V1001 [CWE-563] La variable 'Tamaño' se asigna pero no se usa al final de la función. Object.cpp 424La situación es similar a la anterior. Debería estar escrito: this->Size += this->EntrySize;
Fragmento N38-N47: el puntero se olvidó de verificar.Anteriormente, examinamos ejemplos de activación de diagnóstico V595 . Su esencia es que el puntero se desreferencia al principio, y solo luego se verifica. El joven diagnóstico del V1004 es lo contrario de su significado, pero también detecta muchos errores. Identifica situaciones en las que el puntero se verificó al principio y luego se olvidó de hacerlo. Considere tales casos encontrados dentro de LLVM. int getGEPCost(Type *PointeeType, const Value *Ptr, ArrayRef<const Value *> Operands) { .... if (Ptr != nullptr) {
Advertencia de PVS-Studio: V1004 [CWE-476] El puntero 'Ptr' se usó de manera insegura después de que se verificó contra nullptr. Líneas de verificación: 729, 738. TargetTransformInfoImpl.h 738La variable Ptr puede ser nullptr , como lo demuestra la verificación: if (Ptr != nullptr)
Sin embargo, debajo de este puntero se desreferencia sin verificación previa: auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
Considere otro caso similar. llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD, bool Stub) { .... auto *FD = dyn_cast<FunctionDecl>(GD.getDecl()); SmallVector<QualType, 16> ArgTypes; if (FD)
Advertencia de PVS-Studio: V1004 [CWE-476] El puntero 'FD' se usó de manera insegura después de que se verificó contra nullptr. Verifique las líneas: 3228, 3231. CGDebugInfo.cpp 3231Preste atención al puntero FD . Estoy seguro de que el problema es claramente visible y no se requiere ninguna explicación especial.Y también: static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result, Value *&BasePtr, const DataLayout &DL) { PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType()); if (!PtrTy) {
Advertencia de PVS-Studio: V1004 [CWE-476] El puntero 'PtrTy' se usó de manera insegura después de que se verificó contra nullptr. Verifique las líneas: 960, 965. InterleavedLoadCombinePass.cpp 965¿Cómo protegerse de tales errores? Tenga cuidado con la revisión de código y use el analizador estático PVS-Studio para verificar regularmente su código.No tiene sentido traer otros fragmentos de código con errores de este tipo. Dejaré solo una lista de advertencias en el artículo:- V1004 [CWE-476] El puntero 'Expr' se usó de forma insegura después de que se verificó contra nullptr. Líneas de verificación: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] El puntero 'PI' se usó de forma insegura después de que se verificó contra nullptr. Líneas de verificación: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] The 'StatepointCall' pointer was used unsafely after it was verified against nullptr. Check lines: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] The 'RV' pointer was used unsafely after it was verified against nullptr. Check lines: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] The 'CalleeFn' pointer was used unsafely after it was verified against nullptr. Check lines: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] The 'TC' pointer was used unsafely after it was verified against nullptr. Check lines: 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())); .... }
Advertencia de PVS-Studio: V1023 [CWE-460] El método 'emplace_back' agrega un puntero sin propietario al contenedor 'Estrategias'. Se producirá una pérdida de memoria en caso de una excepción. llvm-isel-fuzzer.cpp 58Para agregar un elemento al final de un contenedor como std :: vector <std :: unique_ptr <X>> no puede simplemente escribir xxx.push_back (nueva X) , ya que no hay una conversión implícita de X * a std: : unique_ptr <X> .Una solución común es escribir xxx.emplace_back (nueva X) , porque compila: el método emplace_back construye un elemento directamente de los argumentos y, por lo tanto, puede usar constructores explícitos.Esto no es seguro. Si el vector está lleno, se asigna memoria. Una operación de reasignación de memoria puede fallar, dando como resultado una excepción std :: bad_alloc . En este caso, el puntero se perderá y el objeto creado nunca se eliminará.Una solución segura es crear unique_ptr , que será el propietario del puntero antes de que el vector intente reasignar la memoria: xxx.push_back(std::unique_ptr<X>(new X))
Comenzando con C ++ 14, puede usar 'std :: make_unique': xxx.push_back(std::make_unique<X>())
Este tipo de defecto no es crítico para LLVM. Si no se puede asignar memoria, entonces el compilador simplemente dejará de funcionar. Sin embargo, para aplicaciones con un tiempo de actividad prolongado que no puede terminar si falla la asignación de memoria, esto puede ser un error realmente desagradable.Entonces, aunque este código no representa un peligro práctico para LLVM, me pareció útil hablar sobre este patrón de error y que el analizador PVS-Studio aprendió a detectarlo.Otras advertencias de este tipo:- V1023 [CWE-460] El método 'emplace_back' agrega un puntero sin propietario al contenedor 'Passes'. Se producirá una pérdida de memoria en caso de una excepción. PassManager.h 546
- V1023 [CWE-460] A pointer without owner is added to the 'AAs' container by the 'emplace_back' method. A memory leak will occur in case of an exception. AliasAnalysis.h 324
- V1023 [CWE-460] A pointer without owner is added to the 'Entries' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] A pointer without owner is added to the 'AllEdges' container by the 'emplace_back' method. A memory leak will occur in case of an exception. CFGMST.h 268
- V1023 [CWE-460] A pointer without owner is added to the 'VMaps' container by the 'emplace_back' method. A memory leak will occur in case of an exception. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] A pointer without owner is added to the 'Records' container by the 'emplace_back' method. A memory leak will occur in case of an exception. FDRLogBuilder.h 30
- V1023 [CWE-460] A pointer without owner is added to the 'PendingSubmodules' container by the 'emplace_back' method. A memory leak will occur in case of an exception. ModuleMap.cpp 810
- V1023 [CWE-460] A pointer without owner is added to the 'Objects' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DebugMap.cpp 88
- V1023 [CWE-460] A pointer without owner is added to the 'Strategies' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 685
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 686
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 688
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 689
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 690
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 691
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 692
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 693
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 694
- V1023 [CWE-460] A pointer without owner is added to the 'Operands' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] A pointer without owner is added to the 'Stash' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] A pointer without owner is added to the 'Matchers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2702
Conclusión
En total, escribí 60 advertencias, después de lo cual paré. ¿Hay otros defectos que el analizador PVS-Studio detecta en LLVM? Si lo hay Sin embargo, cuando escribí los fragmentos de código para el artículo, ya era tarde, o más bien, incluso de noche, y decidí que era hora de redondear.Espero que haya estado interesado y desee probar el analizador PVS-Studio.Puede descargar el analizador y obtener una clave de prueba en esta página .Lo más importante, utilice análisis estático regularmente. Las verificaciones únicas realizadas por nosotros para popularizar la metodología del análisis estático y PVS-Studio no son un escenario normal.¡Buena suerte en mejorar la calidad y confiabilidad del código!
Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Andrey Karpov. Encontrar errores en LLVM 8 con PVS-Studio .