Wireshark 3.x: análisis de código de macOS y revisión de errores

Imagen 1

La Fundación Wireshark ha lanzado la última versión estable del popular analizador de tráfico de red: Wireshark 3.0.0. La nueva versión corrigió varios errores, implementó la capacidad de analizar nuevos protocolos y reemplazó el controlador WinPcap con Npcap. Aquí termina la cita del anuncio y comienza nuestro artículo sobre errores en el proyecto. Antes del lanzamiento, se corrigieron claramente de manera insuficiente. Veamos algunas correcciones para que haya una razón para hacer una nueva versión :).

Introduccion


Wireshark es una herramienta bien conocida para capturar y analizar el tráfico de red. El programa funciona con la gran mayoría de los protocolos conocidos, tiene una interfaz gráfica clara y lógica, un potente sistema de filtro. Wireshark: multiplataforma, funciona en sistemas operativos como: Windows, Linux, macOS, Solaris, FreeBSD, NetBSD y muchos otros.

Para buscar errores, utilizamos el analizador estático PVS-Studio . Para analizar el código fuente, primero debe compilar el proyecto en algún sistema operativo. La elección fue excelente no solo por la naturaleza multiplataforma del proyecto, sino también por la naturaleza multiplataforma del analizador. Para el análisis del proyecto, elegí macOS. El analizador también se puede iniciar en Windows y Linux.

Me gustaría hablar sobre la calidad del código por separado. Lamentablemente, no puedo llamarlo bueno. Esta es una evaluación subjetiva, pero como revisamos regularmente muchos proyectos, tengo algo con lo que comparar. En este caso, una gran cantidad de advertencias de PVS-Studio en una pequeña cantidad de código son sorprendentes. En total, se han emitido más de 3.500 advertencias de todos los niveles para este proyecto. Esto es típico para proyectos que no utilizan herramientas de análisis estático, incluso las gratuitas. Otro factor que indica la calidad del proyecto son los errores repetidos identificados por el analizador. Los mismos ejemplos de código no se darán en el artículo, pero algunos errores idénticos están presentes en el código en cientos de lugares.

Además, estos insertos no agregan calidad al código:

/* Input file: packet-acse-template.c */ #line 1 "./asn1/acse/packet-acse-template.c" 

Hay más de 1000 de ellos en todo el proyecto. Tales insertos dificultan que el analizador compare las advertencias generadas con el archivo deseado. Pero estoy seguro de que los desarrolladores comunes no disfrutan del soporte de dicho código.

Errores tipográficos


Advertencia 1

V641 El tamaño del búfer de memoria asignado no es un múltiplo del tamaño del elemento. mate_setup.c 100

 extern mate_cfg_gog* new_gogcfg(mate_config* mc, gchar* name) { mate_cfg_gog* cfg = (mate_cfg_gog *)g_malloc(sizeof(mate_cfg_gop)); .... } 

Hay dos tipos de estructuras: mate_cfg_gog y mate_cfg_gop , son muy similares, pero no idénticas. Lo más probable es que las funciones se mezclaron en este fragmento de código, que está plagado de posibles errores en el programa durante el acceso a la memoria por puntero.

Los siguientes son fragmentos de estructuras de datos confusas:

 typedef struct _mate_cfg_gog { gchar* name; GHashTable* items; guint last_id; GPtrArray* transforms; LoAL* keys; AVPL* extra; float expiration; gop_tree_mode_t gop_tree_mode; gboolean show_times; .... } mate_cfg_gog; typedef struct _mate_cfg_gop { gchar* name; guint last_id; GHashTable* items; GPtrArray* transforms; gchar* on_pdu; AVPL* key; AVPL* start; AVPL* stop; AVPL* extra; float expiration; float idle_timeout; float lifetime; gboolean drop_unassigned; gop_pdu_tree_t pdu_tree_mode; gboolean show_times; .... } mate_cfg_gop; 

Advertencia 2

V519 La variable 'HDR_TCP.dest_port' tiene valores asignados dos veces sucesivamente. Quizás esto sea un error. Líneas de verificación: 495, 496. text_import.c 496

 void write_current_packet (void) { .... HDR_TCP.source_port =isOutbound ? g_htons(hdr_dest_port):g_htons(hdr_src_port); HDR_TCP.dest_port = isOutbound ? g_htons(hdr_src_port) :g_htons(hdr_dest_port); HDR_TCP.dest_port = g_htons(hdr_dest_port); .... } 

En la última línea, el valor calculado de la variable HDR_TCP.dest_port no está confirmado .

Errores lógicos


En esta sección daré algunos ejemplos de errores en las declaraciones condicionales, y todos ellos serán fundamentalmente diferentes entre sí.

Advertencia 1

V547 La expresión 'direction == 0' siempre es falsa. paquete-adb.c 291

 #define P2P_DIR_RECV 1 #define P2P_DIR_SENT 0 static void save_command(....) { .... if ( service_data && service_data->remote_id == 0 && direction == P2P_DIR_RECV) { if (direction == P2P_DIR_SENT) { service_data->remote_id = arg1; // unreachable code } else { service_data->remote_id = arg0; } .... } .... } 

En la condición externa, la variable de dirección se compara con la constante P2P_DIR_RECV . Escribir expresiones a través del operador AND significa que cuando se alcanza la condición interna, el valor de la variable de dirección no será exactamente igual a la otra constante P2P_DIR_SENT .

Advertencia 2

V590 Considere inspeccionar el '(tipo == 0x1) || (tipo! = 0x4) 'expresión. La expresión es excesiva o contiene un error de imprenta. paquete-fcsb3.c 686

 static int dissect_fc_sbccs (....) { .... else if ((type == FC_SBCCS_IU_CMD_HDR) || (type != FC_SBCCS_IU_CMD_DATA)) { .... } 

El error de este fragmento de código es que el resultado de la condición depende de una sola expresión:

 (type != FC_SBCCS_IU_CMD_DATA) 

Advertencia 3

V590 Considere inspeccionar esta expresión. La expresión es excesiva o contiene un error de imprenta. snort-config.c 40

 static char *skipWhiteSpace(char *source, int *accumulated_offset) { int offset = 0; /* Skip any leading whitespace */ while (source[offset] != '\0' && source[offset] == ' ') { offset++; } *accumulated_offset += offset; return source + offset; } 

El resultado de la declaración condicional dependerá solo de esta parte de la expresión (fuente [desplazamiento] == '') . La comprobación (fuente [desplazamiento]! = '\ 0') es redundante y se puede eliminar de forma segura. Esto no es un error real, pero el código redundante dificulta la lectura y comprensión del programa, por lo que es mejor simplificarlo.

Advertencia 4

V547 La expresión 'eras_pos! = NULL' siempre es verdadera. reedsolomon.c 659

 int eras_dec_rs(dtype data[NN], int eras_pos[NN-KK], int no_eras) { .... if(eras_pos != NULL){ for(i=0;i<count;i++){ if(eras_pos!= NULL) eras_pos[i] = INDEX_TO_POS(loc[i]); } } .... } 

Quizás estamos lidiando con una verificación innecesaria, y posiblemente con un error tipográfico, y en uno de los ifs, algo más debería verificarse.

Activos extraños


Advertencia 1

V547 La expresión 'sub_dissectors! = NULL' siempre es verdadera. capture_dissectors.c 129

 void capture_dissector_add_uint(....) { .... sub_dissectors = (struct capture_dissector_table*)g_hash_table_lookup(....); if (sub_dissectors == NULL) { fprintf(stderr, "OOPS: Subdissector \"%s\" not found ... \n", name); if (getenv("WIRESHARK_ABORT_ON_DISSECTOR_BUG") != NULL) abort(); return; } g_assert(sub_dissectors != NULL); // <= .... } 

Verificar el puntero en g_assert en este punto es superfluo, ya que el puntero se verifica antes de eso. Quizás en esta función solo había g_assert antes , y se olvidaron de eliminarlo, pero tal vez aquí debería verificar algún campo de estructura.

Advertencia 2

V547 La expresión 'i <count' siempre es verdadera. packet-netflow.c 10363

 static int dissect_v9_v10_template_fields(....) { .... count = tmplt_p->field_count[fields_type]; for(i=0; i<count; i++) { .... if (tmplt_p->fields_p[fields_type] != NULL) { DISSECTOR_ASSERT (i < count); // <= tmplt_p->fields_p[fields_type][i].type = type; tmplt_p->fields_p[fields_type][i].length = length; tmplt_p->fields_p[fields_type][i].pen = pen; tmplt_p->fields_p[fields_type][i].pen_str = pen_str; if (length != VARIABLE_LENGTH) {/ tmplt_p->length += length; } } .... } .... } 

No está del todo claro por qué una aserción está presente en la función, que duplica la condición del bucle. El contador de ciclos en el cuerpo no cambia.

Errores con punteros


Advertencia 1

V595 El puntero 'si-> conv' se utilizó antes de que se verificara contra nullptr. Líneas de verificación: 2135, 2144. packet-smb2.c 2135

 static int dissect_smb2_fid(....) { .... g_hash_table_insert(si->conv->fids, sfi, sfi); // <= si->file = sfi; if (si->saved) { si->saved->file = sfi; si->saved->policy_hnd = policy_hnd; } if (si->conv) { // <= eo_file_info = (.... *)g_hash_table_lookup(si->conv->files,&policy_hnd); .... } .... } 

El puntero si-> conv se desreferencia varias líneas antes de que se verifique si es igual a cero o no.

Advertencia 2

V774 El puntero 'protos' se usó después de liberar la memoria. paquete-k12.c 311

 static gboolean k12_update_cb(void* r, char** err) { gchar** protos; .... for (i = 0; i < num_protos; i++) { if ( ! (h->handles[i] = find_dissector(protos[i])) ) { h->handles[i] = data_handle; h->handles[i+1] = NULL; g_strfreev(protos); *err = g_strdup_printf("Could not find dissector for: '%s'", protos[i]); return FALSE; } } .... } 

protos es una serie de cadenas. Durante el procesamiento de una excepción en el programa, esta matriz primero se borra mediante la función g_strfreev , y luego se usa una de las líneas de esta matriz en el mensaje de error. Lo más probable es que estas líneas en el código se intercambien:

 *err = g_strdup_printf("Could not find dissector for: '%s'", protos[i]); g_strfreev(protos); 

Fugas de memoria


V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2436

 static void parsetypedefunion(int pass) { char tmpstr[BASE_BUFFER_SIZE], *ptmpstr; .... while(num_pointers--){ g_snprintf(tmpstr, BASE_BUFFER_SIZE, "%s_%s", ptmpstr, "unique"); FPRINTF(eth_code, "static int\n"); FPRINTF(eth_code, "....", tmpstr); FPRINTF(eth_code, "{\n"); FPRINTF(eth_code, " ....", ptmpstr, ti->str); FPRINTF(eth_code, " return offset;\n"); FPRINTF(eth_code, "}\n"); FPRINTF(eth_code, "\n"); ptmpstr=g_strdup(tmpstr); } .... } 

Después de la función g_strdup , debe llamar a la función g_free en algún momento. En el fragmento de código presentado, esto no se hace, y en el ciclo en cada iteración se asigna una nueva pieza de RAM. Se producen múltiples pérdidas de memoria.

Algunas advertencias más para fragmentos de código similares:

  • V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2447
  • V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2713
  • V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2728
  • V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2732
  • V773 Al puntero 'ptmpstr' se le asignaron valores dos veces sin liberar la memoria. Una pérdida de memoria es posible. idl2wrs.c 2745

Desafortunadamente, hay muchos otros lugares similares en el código donde no se libera memoria.

Misceláneo


Advertencia 1

V535 La variable 'i' se está utilizando para este bucle y para el bucle externo. Líneas de verificación: 7716, 7798. packet-opa-mad.c 7798

 /* Parse GetVFInfo MAD from the Performance Admin class. */ static gint parse_GetVFInfo(....) { .... for (i = 0; i < records; i++) { // <= line 7716 .... for (i = 0; i < PM_UTIL_BUCKETS; i++) { // <= line 7748 GetVFInfo_Util_Stats_Bucket_item = proto_tree_add_item(....); proto_item_set_text(....); local_offset += 4; } .... for (i = 0; i < PM_ERR_BUCKETS; i++) { // <= line 7798 GetVFInfo_Error_Stats_Bucket_item = proto_tree_add_item(....); proto_item_set_text(....); local_offset += 4; .... } .... } .... } 

En una función muy larga, los desarrolladores cambian audazmente el valor del contador de bucle y lo hacen varias veces. Es difícil decir si esto es un error o no, pero hay unos 10 ciclos de este tipo en el proyecto.

Advertencia 2

El 'elemento' del parámetro V763 siempre se reescribe en el cuerpo de la función antes de ser utilizado. paquete-cdma2k.c 1324

 static void cdma2k_message_ORDER_IND(proto_item *item, ....) { guint16 addRecLen = -1, ordq = -1, rejectedtype = -1; guint16 l_offset = -1, rsc_mode_ind = -1, ordertype = -1; proto_tree *subtree = NULL, *subtree1 = NULL; item = proto_tree_add_item(tree,hf_cdma2k_OrderIndMsg, tvb, ....); // <= subtree = proto_item_add_subtree(item, ett_cdma2k_subtree1); .... } 

El puntero del elemento que toma la función se deshilacha inmediatamente por otro valor. Esto es muy sospechoso. Además, el código contiene varias docenas de dichos lugares, por lo que es difícil decir si esto es un error o no. Conocí un código similar anteriormente en otro gran proyecto, allí estaba el código correcto, pero nadie se atrevió a cambiar la interfaz de la función.

Advertencia 3

V762 Es posible que una función virtual se haya anulado incorrectamente. Vea el tercer argumento de la función 'headerData' en la clase derivada 'PacketListModel' y la clase base 'QAbstractItemModel'. packet_list_model.h 48

 QVariant QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const // <= class PacketListModel : public QAbstractItemModel { Q_OBJECT public: .... QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole | Qt::ToolTipRole) const; // <= .... }; 

El analizador detectó una sobrecarga incorrecta de la función headerData . Las funciones tienen un valor predeterminado diferente para el parámetro de rol . Esto puede no conducir al comportamiento que el programador esperaba.

Advertencia 4

V610 Comportamiento indefinido. Verifique el operador de turno '>>'. El operando derecho ('bithift' = [0..64]) es mayor o igual que la longitud en bits del operando izquierdo promovido. proto.c 10941

 static gboolean proto_item_add_bitmask_tree(...., const int len, ....) { .... if (len < 0 || len > 8) g_assert_not_reached(); bitshift = (8 - (guint)len)*8; available_bits = G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF) >> bitshift; .... } 

Un cambio de 64 bits dará como resultado un comportamiento indefinido de acuerdo con el estándar del idioma.

Más bien, el código correcto debería ser así:

 if (bitshift == 64) available_bits = 0; else available_bits = G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF) >> bitshift; 

Conclusión


Puede parecer que hay pocos ejemplos de errores en la revisión, pero en el informe completo los casos presentados se repiten docenas y cientos de veces. Las descripciones generales de advertencia de PVS-Studio son solo para fines de demostración. Esta es una contribución a la calidad de los proyectos de código abierto, pero los controles únicos son la forma más ineficiente de aplicar la metodología de análisis estático.

Puede obtener y analizar el informe completo usted mismo. Para hacer esto, solo necesita descargar y ejecutar el analizador PVS-Studio.



Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Svyatoslav Razmyslov. Wireshark 3.x: análisis de código bajo macOS y revisión de errores

Source: https://habr.com/ru/post/447158/


All Articles