Wireshark Foundation has released the final stable-version of the popular network traffic analyzer - Wireshark 3.0.0. The new release eliminated several bugs, implemented the ability to analyze new protocols and replaced the WinPcap driver with Npcap. Here ends the citation of the announcement and begins our note about the bugs in the project. Before the release of them was fixed clearly not enough. Let's collect fixes so that there is a reason to make a new release :).
Introduction
Wireshark is a well-known tool for capturing and analyzing network traffic. The program works with the vast majority of known protocols, has a clear and logical graphical interface, a powerful filter system. Wireshark - cross-platform, works in such OS as: Windows, Linux, macOS, Solaris, FreeBSD, NetBSD and many others.
For searching errors, the
PVS-Studio static analyzer was used. To analyze the source code, you must first compile the project in any operating system. The choice was great not only because of the cross-platform project, but also because of the cross-platform analyzer. To analyze the project, I chose macOS. Also, the launch of the analyzer is possible in Windows and Linux.
')
About the quality of the code I want to tell separately. Unfortunately, I can not call it good. This is a subjective assessment, but since we regularly
check many projects, I have something to compare with. In this case, a large number of PVS-Studio warnings catches the eye on a small amount of code. In total, more than 3,500 warnings of all levels have been issued for this project. This is typical for projects in which static analysis tools are not used at all, even free ones. Another factor indicating the quality of the project is repeated errors detected by the analyzer. In the article, the same type of code examples will not be given, but some identical errors are present in the code in a hundred places.
More quality code does not add such inserts:
#line 1 "./asn1/acse/packet-acse-template.c"
There are more than 1000 of them in the whole project. Such inserts make it difficult for the analyzer to match the issued warnings with the desired file. But I am sure that ordinary developers do not enjoy the support of such code.
Typos
Warning 1V641 is a multiple of the size of the element size. 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)); .... }
There are two types of structures:
mate_cfg_gog and
mate_cfg_gop , they are very similar, but not identical. Most likely, in this code snippet, the functions are confused, which is fraught with potential errors in the program during memory access by the pointer.
The following are fragments of confused data structures:
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;
Warning 2V519 The 'HDR_TCP.dest_port' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 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); .... }
The last line of course just
overwrites the newly calculated value of the variable
HDR_TCP.dest_port .
Logical errors
In this section I will give several examples of errors in conditional operators, and all of them will be fundamentally different from each other.
Warning 1V547 Expression 'direction == 0' is always false. packet-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;
In the external condition, the
direction variable is compared with the constant
P2P_DIR_RECV . Writing expressions through the AND operator means that when the internal condition is reached, the value of the
direction variable will definitely not be equal to another constant
P2P_DIR_SENT .
Warning 2V590 Consider inspecting the '(type == 0x1) || (type! = 0x4) 'expression. The expression is misprint. packet-fcsb3.c 686
static int dissect_fc_sbccs (....) { .... else if ((type == FC_SBCCS_IU_CMD_HDR) || (type != FC_SBCCS_IU_CMD_DATA)) { .... }
The error of this code snippet is that the result of the condition depends on only one expression:
(type != FC_SBCCS_IU_CMD_DATA)
Warning 3V590 Consider inspecting this expression. The expression is misprint. snort-config.c 40
static char *skipWhiteSpace(char *source, int *accumulated_offset) { int offset = 0; while (source[offset] != '\0' && source[offset] == ' ') { offset++; } *accumulated_offset += offset; return source + offset; }
The result of the conditional operator will depend only on this part of the expression
(source [offset] == '') . The check
(source [offset]! = '\ 0') is redundant and can be safely removed. This is not a real mistake, but redundant code makes it difficult to read and understand the program, so it’s best to simplify it.
Warning 4V547 Expression 'eras_pos! = NULL' is always true. 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]); } } .... }
Perhaps we are dealing with an extra check, and possibly with a typo, and one of the if-s should be checked for something completely different.
Strange asserts
Warning 1V547 Expression 'sub_dissectors! = NULL' is always true. 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);
Checking the pointer in
g_assert in this place is superfluous, since the pointer is checked before this. Perhaps, in this function there used to be only
g_assert , and it was forgotten to be deleted, but perhaps there should have been some field structure checked.
Warning 2V547 Expression 'i <count' is always true. 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);
It is not entirely clear why the function contains
assert , which duplicates the condition from the loop. The loop counter in the body does not change.
Pointer errors
Warning 1V595 The 'si-> conv' pointer was used before it was verified against nullptr. Check lines: 2135, 2144. packet-smb2.c 2135
static int dissect_smb2_fid(....) { .... g_hash_table_insert(si->conv->fids, sfi, sfi);
The pointer
si-> conv is dereferenced several lines earlier than it is checked whether it is equal to zero or not.
Warning 2V774 The 'protos' pointer was used after the memory was released. packet-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 is an array of strings. During the processing of a special situation in the program, this array is first cleared by the
g_strfreev function, and then one of the rows of this array is used in the error message. Most likely, these lines in the code should be swapped:
*err = g_strdup_printf("Could not find dissector for: '%s'", protos[i]); g_strfreev(protos);
Memory leaks
V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. 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); } .... }
After the
g_strdup function,
you need to call the
g_free function at some point. In the presented code snippet this is not done, and in the loop at each iteration a new section of RAM is allocated. Multiple memory leaks occur.
A few more warnings for similar code snippets:
- V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. idl2wrs.c 2447
- V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. idl2wrs.c 2713
- V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. idl2wrs.c 2728
- V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. idl2wrs.c 2732
- V773 The 'ptmpstr' pointer was assigned values. A memory leak is possible. idl2wrs.c 2745
Unfortunately, there are many other similar places in the code where memory is not released.
miscellanea
Warning 1V535 The variable 'i' is being used for the loop. Check lines: 7716, 7798. packet-opa-mad.c 7798
static gint parse_GetVFInfo(....) { .... for (i = 0; i < records; i++) {
In a very long function, developers boldly change the value of the loop counter, and they do it several times. It is difficult to say whether this is a mistake or not, but there are about 10 similar cycles in the project.
Warning 2Parameter 'item' is always rewritten in function body before being used. packet-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, ....);
The
item pointer that the function accepts is immediately overwritten by another value. This is very suspicious. Moreover, there are several dozens of such places in the code, so it’s hard to say if this is a mistake or not. I had previously encountered a similar code in another large project; there it was the correct code, just no one dared to change the interface of the function.
Warning 3V762 It was possible a virtual function was overridden incorrectly. See the third argument of the function 'headerData' in the derived class 'PacketListModel' and the base class 'QAbstractItemModel'. packet_list_model.h 48
QVariant QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
The analyzer detected incorrect overloading of the
headerData function. Functions have a different default value for the
role parameter. This may not lead to the behavior that the programmer expected.
Warning 4V610 Undefined behavior. Check the shift operator '>>'. The right operand ('bitshift' = [0..64]) is the operand. 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; .... }
Shifting the number to 64 bits will result in undefined behavior according to the language standard.
Rather, the correct code should be:
if (bitshift == 64) available_bits = 0; else available_bits = G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF) >> bitshift;
Conclusion
It may seem that there are few examples of errors in the review, but in the full report the cases presented are repeated tens and hundreds of times. The PVS-Studio warning reviews are of a demo nature. This is a contribution to the quality of open source projects, but one-time checks are the most inefficient way to use the static analysis methodology.
You can get and analyze the full report yourself. For this, you just need to
download and run the PVS-Studio analyzer.

If you want to share this article with an English-speaking audience, then please use the link to the translation: Svyatoslav Razmyslov.
Wireshark 3.x: code analysis under macOS and errors review