
Hoy tenemos otro proyecto de Microsoft de alta calidad para verificar, que heroicamente profundizaremos en tratar de encontrar errores con PVS-Studio. SARIF, un acrónimo de Static Analysis Interchange Format, que es un estándar (formato de archivo), diseñado para interactuar y compartir los resultados de los analizadores estáticos con otras herramientas: IDE, herramientas complejas de verificación y análisis de código (por ejemplo, SonarQube), sistemas de integración continua, etc. El SDK de SARIF, respectivamente, contiene herramientas de desarrollo .NET para admitir SARIF, así como archivos adicionales.
SARIF se originó en Microsoft y ahora es un estándar desarrollado por OASIS (un consorcio sin fines de lucro que se ocupa de los estándares abiertos). SARIF está destinado a transmitir no solo los resultados del analizador, sino también los metadatos sobre la herramienta, así como los datos sobre cómo se lanzó, las etiquetas de tiempo, etc. Para obtener más información, visite el sitio web de
OASIS . El código fuente de SARIF SDK se puede descargar del repositorio en
GiHub . La página de inicio del proyecto está disponible por
enlace .
Sobre el proyecto
El proyecto del SDK de SARIF resultó ser pequeño: 799 archivos .cs (aproximadamente 98,000 líneas de código no vacías). El proyecto contiene pruebas que siempre excluyo de la verificación. Por lo tanto, la parte del código que nos interesaba era 642 archivos .cs (aproximadamente 79,000 líneas de código no vacías). Ciertamente no es suficiente. En el lado positivo, la verificación y el análisis fueron fáciles y rápidos, entre esto y entonces, que traté de reflejar en la imagen al principio. No obstante, logré localizar algunos casos extraños. Echemos un vistazo a ellos.
Errores
V3070 [CWE-457] La variable no inicializada 'Binary' se usa al inicializar la variable 'Default'. MimeType.cs 90
public static class MimeType { ....
El campo se inicializa por el valor de otro campo, que aún no ha recibido un valor. Como resultado,
Default recibirá el valor nulo por defecto para el tipo de
cadena . Lo más probable es que el error haya pasado desapercibido, ya que el campo
Predeterminado no se usa en ningún lado. Pero las cosas pueden cambiar, y luego el desarrollador enfrentará un resultado indebido o el bloqueo del programa.
V3061 El parámetro 'logicalLocationToIndexMap' siempre se reescribe en el cuerpo del método antes de usarse. PrereleaseCompatibilityTransformer.cs 1963
private static JArray ConvertLogicalLocationsDictionaryToArray( .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap, ....) { .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); .... }
El autor del código no utiliza el parámetro
logicalLocationToIndexMap de ninguna manera, pero escribe un valor diferente en él. Curiosamente, el valor anterior es exactamente el mismo diccionario vacío, creado en el código de la persona que llama:
private static bool ApplyChangesFromTC25ThroughTC30(....) { .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap = null; .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); run["logicalLocations"] = ConvertLogicalLocationsDictionaryToArray( ...., logicalLocationToIndexMap, ....); }
Código extraño y sospechoso.
V3008 [CWE-563] La variable 'run.Tool' recibe valores asignados dos veces seguidas. Quizás esto sea un error. Líneas de verificación: 116, 114. ExportRulesMetadataCommandBase.cs 116
public partial class Run { .... public Tool Tool { get; set; } .... } public partial class Tool : .... { .... public Tool() { } .... } private void OutputSarifRulesMetada(....) { .... var run = new Run(); run.Tool = new Tool(); run.Tool = Tool.CreateFromAssemblyData(....);
La propiedad
run.Tool tiene asignado un valor dos veces. Tanto al crear el objeto
Herramienta como al escribir un valor en la propiedad
Herramienta , no se requiere trabajo adicional. Por lo tanto, la reasignación huele a pescado.
V3042 [CWE-476] Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'loc' WhereComparer.cs 152
private static Uri ArtifactUri(ArtifactLocation loc, Run run) { return loc?.Uri ?? loc.Resolve(run)?.Uri; }
Si el valor de la variable
loc es
nulo , se intentará devolver el valor de la parte derecha de ?? operador, lo que resulta en el acceso por referencia nula.
V3042 [CWE-476] Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'formatString' InsertOptionalDataVisitor.cs 194
public override Message VisitMessage(Message node) { .... node.Text = node.Arguments?.Count > 0 ? string.Format(...., formatString.Text, ....) : formatString?.Text; .... }
Los desarrolladores utilizan opciones de acceso seguro y no seguro mediante un
formato potencialmente nulo
¿ Referencia de
cadena en dos ramas paralelas del condicional ?: Operador.
V3042 [CWE-476] Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'messageText' FortifyFprConverter.cs 1210
V3042 [CWE-476] Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'messageText' FortifyFprConverter.cs 1216
private void AddMessagesToResult(Result result) { .... string messageText = (rule.ShortDescription ?? rule.FullDescription)?.Text; .... if (....) {
Aquí el analizador ya emitió dos advertencias sobre el posible acceso por parte de la referencia nula de
mensaje de texto . Parece bastante amenazante, pero sigue siendo un error.
V3080 [CWE-476] Posible desreferencia nula. Considere inspeccionar 'fileDataVersionOne.Uri'. SarifCurrentToVersionOneVisitor.cs 1030
private IDictionary<string, FileDataVersionOne> CreateFileDataVersionOneDictionary() { .... FileDataVersionOne fileDataVersionOne = CreateFileDataVersionOne(v2File); if (fileDataVersionOne.Uri.OriginalString.Equals(key)) { .... } .... }
El analizador sospecha que
NullReferenceException es posible cuando se trabaja con la referencia
fileDataVersionOne.Uri . Veamos de dónde proviene esta variable y descubramos si el analizador es correcto. Para hacer esto, echemos un vistazo al cuerpo del método
CreateFileDataVersionOne :
private FileDataVersionOne CreateFileDataVersionOne(Artifact v2FileData) { FileDataVersionOne fileData = null; if (v2FileData != null) { .... fileData = new FileDataVersionOne { .... Uri = v2FileData.Location?.Uri, .... }; .... } return fileData; } public partial class FileDataVersionOne { .... public Uri Uri { get; set; } .... }
De hecho, al crear el objeto de la clase
FileDataVersionOne , la propiedad
Uri podría recibir el valor
nulo . Este es un gran ejemplo de análisis de flujo de datos y mecanismos de análisis interprocedimiento trabajando juntos.
V3080 [CWE-476] Posible desreferencia nula. Considere la posibilidad de inspeccionar '_jsonTextWriter'. SarifLogger.cs 242
public virtual void Dispose() { .... if (_closeWriterOnDispose) { if (_textWriter != null) { _textWriter.Dispose(); } if (_jsonTextWriter == null) { _jsonTextWriter.Close(); }
Hay un error tipográfico en este fragmento. Está claro que
_jsonTextWriter! = Null tiene que estar en la condición del segundo bloque. Este código pone en peligro ya que, muy probablemente, no se
bloquea , debido a que
_jsonTextWriter no es
nulo . Además, la corriente permanece abierta.
V3083 [CWE-367] La invocación insegura del evento 'RuleRead', NullReferenceException es posible. Considere asignar un evento a una variable local antes de invocarlo. FxCopConverter.cs 897
private void ReadRule(....) { .... if (RuleRead != null) { RuleRead(....); } .... }
Los eventos se manejan de manera insegura. Es un error no crítico que se puede solucionar fácilmente, por ejemplo, siguiendo el consejo de Visual Studio. Aquí está el reemplazo sugerido por el IDE:
private void ReadRule(....) { .... RuleRead?.Invoke(....); .... }
Solo toma unos segundos arreglarlo, pero el analizador ya no se quejará y el IDE no resaltará el código. Otro error similar.
- V3083 [CWE-367] La invocación insegura del evento 'ResultRead', NullReferenceException es posible. Considere asignar un evento a una variable local antes de invocarlo. FxCopConverter.cs 813
V3095 [CWE-476] El objeto 'v1Location' se usó antes de que se verificara como nulo. Líneas de verificación: 333, 335. SarifVersionOneToCurrentVisitor.cs 333
internal Location CreateLocation(LocationVersionOne v1Location) { .... string key = v1Location.LogicalLocationKey ?? v1Location.FullyQualifiedLogicalName; if (v1Location != null) { .... } .... }
El autor pensó que la referencia de
v1Location puede ser nula y agregó una verificación adecuada. Mientras que arriba podemos ver que esta referencia se maneja sin ninguna verificación. ¿Refactorización desatendida? Bueno, nunca se sabe.
V3125 [CWE-476] El objeto 'v1StackFrame' se usó después de que se verificó contra nulo. Líneas de verificación: 1182, 1171. SarifVersionOneToCurrentVisitor.cs 1182
internal StackFrame CreateStackFrame(StackFrameVersionOne v1StackFrame) { StackFrame stackFrame = null; if (v1StackFrame != null) { stackFrame = new StackFrame { .... }; } stackFrame.Location = CreateLocation(v1StackFrame.FullyQualifiedLogicalName, v1StackFrame.LogicalLocationKey, ....); return stackFrame; }
Como siempre, aquí viene un caso inverso. Primero se verifica que la referencia
v1StackFrame sea
nula , y luego la verificación se ha extraviado. Pero este caso tiene una advertencia importante: las variables
v1StackFrame y
stackFrame están relacionadas lógicamente. Vea, si
v1StackFrame es
nulo , el objeto
StackFrame no se creará, mientras que
stackFrame seguirá siendo
nulo. Seguido por el bloqueo del programa debido a una llamada de
stackFrame.Location , ya que no hay verificaciones aquí. Por lo tanto, ni siquiera llegará al uso peligroso
v1StackFrame , indicado por el analizador. Este código solo funciona si pasa valores no nulos de
v1StackFrame al método
CreateStackFrame . Sospeché que el código de llamada de alguna manera lo controla.
Las llamadas
CreateStackFrame se ven así:
Frames = v1Stack.Frames?.Select(CreateStackFrame).ToList()
CreateStackFrame se usa como un selector. Las referencias aprobadas no se verifican aquí como
nulas . Quizás, al llenar la colección
Frames, se controla (escribir referencias nulas), pero no fui a cavar demasiado profundo. La conclusión ya es obvia: el código requiere la atención de los autores.
Conclusión
Como puede ver, el artículo no es largo, pero espero que haya disfrutado esta lectura ligera :) Por si acaso, siempre puede
descargar nuestro analizador para buscar errores en sus proyectos o los de usted mismo.
Y finalmente, un pequeño anuncio: mi próximo artículo será sobre los errores más interesantes que mis colegas y yo encontramos en los proyectos en 2019. Siga nuestro
blog . Nos vemos
Para obtener más información sobre nuevas publicaciones de blog, puede suscribirse a los siguientes canales: