Bug Summary

File:wsutil/filter_files.c
Warning:line 200, column 21
Read function called when stream is in EOF state. Function has no effect

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name filter_files.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-20/lib/clang/20 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D BUILD_WSUTIL -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D wsutil_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/build/wsutil -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-truncation -Wno-format-nonliteral -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-09-10-100354-3933-1 -x c /builds/wireshark/wireshark/wsutil/filter_files.c
1/* filter_files.c
2 * Code for reading and writing the filters file.
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <[email protected]>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11#include <config.h>
12#define WS_LOG_DOMAIN"WSUtil" LOG_DOMAIN_WSUTIL"WSUtil"
13#include "filter_files.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <errno(*__errno_location ()).h>
18
19#include <wsutil/file_util.h>
20#include <wsutil/filesystem.h>
21#include <wsutil/report_message.h>
22#include <wsutil/wslog.h>
23#include <wsutil/ws_assert.h>
24
25/*
26 * Read in a list of filters.
27 *
28 * On error, report the error via the UI.
29 */
30
31#define INIT_BUF_SIZE128 128
32
33static GList *
34add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr)
35{
36 filter_def *filt;
37
38 filt = g_new(filter_def, 1)((filter_def *) g_malloc_n ((1), sizeof (filter_def)));
39 filt->name = g_strdup(filt_name)g_strdup_inline (filt_name);
40 filt->strval = g_strdup(filt_expr)g_strdup_inline (filt_expr);
41 return g_list_prepend(fl, filt);
42}
43
44static void
45free_filter_entry(void * data)
46{
47 filter_def *filt = (filter_def*)data;
48 g_free(filt->name);
49 g_free(filt->strval);
50 g_free(filt);
51}
52
53void ws_filter_list_free(filter_list_t *fl)
54{
55 g_list_free_full(fl->list, free_filter_entry);
56 g_free(fl);
57}
58
59static GList *
60remove_filter_entry(GList *fl, GList *fl_entry)
61{
62 filter_def *filt;
63
64 filt = (filter_def *) fl_entry->data;
65 g_free(filt->name);
66 g_free(filt->strval);
67 g_free(filt);
68 return g_list_remove_link(fl, fl_entry);
69}
70
71static int
72skip_whitespace(FILE *ff)
73{
74 int c;
75
76 while ((c = getc(ff)) != EOF(-1) && c != '\n' && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0))
77 ;
78 return c;
79}
80
81static int
82getc_crlf(FILE *ff)
83{
84 int c;
85
86 c = getc(ff);
87 if (c == '\r') {
88 /* Treat CR-LF at the end of a line like LF, so that if we're reading
89 * a Windows-format file on UN*X, we handle it the same way we'd handle
90 * a UN*X-format file. */
91 c = getc(ff);
92 if (c != EOF(-1) && c != '\n') {
93 /* Put back the character after the CR, and process the CR normally. */
94 ungetc(c, ff);
95 c = '\r';
96 }
97 }
98 return c;
99}
100
101filter_list_t *
102ws_filter_list_read(filter_list_type_t list_type)
103{
104 const char *ff_name, *ff_description;
105 char *ff_path;
106 FILE *ff;
107 GList *flp = NULL((void*)0);
108 int c;
109 char *filt_name, *filt_expr;
110 int filt_name_len, filt_expr_len;
111 int filt_name_index, filt_expr_index;
112 int line = 1;
113
114 filter_list_t *list = g_new(filter_list_t, 1)((filter_list_t *) g_malloc_n ((1), sizeof (filter_list_t)));
115 list->type = list_type;
116 list->list = NULL((void*)0);
117
118 switch (list_type) {
1
Control jumps to 'case DMACROS_LIST:' at line 130
119
120 case CFILTER_LIST:
121 ff_name = CFILTER_FILE_NAME"cfilters";
122 ff_description = "capture filter";
123 break;
124
125 case DFILTER_LIST:
126 ff_name = DFILTER_FILE_NAME"dfilters";
127 ff_description = "display filter";
128 break;
129
130 case DMACROS_LIST:
131 ff_name = DMACROS_FILE_NAME"dmacros";
132 ff_description = "display filter macro";
133 break;
2
Execution continues on line 140
134
135 default:
136 ws_assert_not_reached()ws_log_fatal_full("WSUtil", LOG_LEVEL_ERROR, "wsutil/filter_files.c"
, 136, __func__, "assertion \"not reached\" failed")
;
137 }
138
139 /* try to open personal "cfilters"/"dfilters" file */
140 ff_path = get_persconffile_path(ff_name, true1);
141 if ((ff = ws_fopenfopen(ff_path, "r")) == NULL((void*)0)) {
3
Taking false branch
142 /*
143 * Did that fail because the file didn't exist?
144 */
145 if (errno(*__errno_location ()) != ENOENT2) {
146 /*
147 * No. Just give up.
148 */
149 report_warning("Could not open your %s file\n\"%s\": %s.",
150 ff_description, ff_path, g_strerror(errno(*__errno_location ())));
151 g_free(ff_path);
152 return list;
153 }
154
155 /*
156 * Yes. Try to open the global "cfilters/dfilters" file.
157 */
158 g_free(ff_path);
159 ff_path = get_datafile_path(ff_name);
160 if ((ff = ws_fopenfopen(ff_path, "r")) == NULL((void*)0)) {
161 /*
162 * Well, that didn't work, either. Just give up.
163 * Report an error if the file existed but we couldn't open it.
164 */
165 if (errno(*__errno_location ()) != ENOENT2) {
166 report_warning("Could not open your %s file\n\"%s\": %s.",
167 ff_description, ff_path, g_strerror(errno(*__errno_location ())));
168 }
169 g_free(ff_path);
170 return list;
171 }
172 }
173
174 /* Allocate the filter name buffer. */
175 filt_name_len = INIT_BUF_SIZE128;
176 filt_name = (char *)g_malloc(filt_name_len + 1);
177 filt_expr_len = INIT_BUF_SIZE128;
178 filt_expr = (char *)g_malloc(filt_expr_len + 1);
179
180 for (line = 1; ; line++) {
4
Loop condition is true. Entering loop body
181 /* Lines in a filter file are of the form
182
183 "name" expression
184
185 where "name" is a name, in quotes - backslashes in the name
186 escape the next character, so quotes and backslashes can appear
187 in the name - and "expression" is a filter expression, not in
188 quotes, running to the end of the line. */
189
190 /* Skip over leading white space, if any. */
191 c = skip_whitespace(ff);
192
193 if (c == EOF(-1))
5
Taking false branch
194 break; /* Nothing more to read */
195 if (c == '\n')
6
Taking false branch
196 continue; /* Blank line. */
197 if (c == '#') {
7
Assuming the condition is true
8
Taking true branch
198 /* Comment. */
199 while (c != '\n')
9
Loop condition is true. Entering loop body
11
Loop condition is true. Entering loop body
200 c = getc(ff); /* skip to the end of the line */
10
Assuming stream reaches end-of-file here
12
Read function called when stream is in EOF state. Function has no effect
201 continue;
202 }
203
204 /* "c" is the first non-white-space character.
205 If it's not a quote, it's an error. */
206 if (c != '"') {
207 ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 208, __func__, "'%s' line %d doesn't have a quoted filter name."
, ff_path, line); } } while (0)
208 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 208, __func__, "'%s' line %d doesn't have a quoted filter name."
, ff_path, line); } } while (0)
;
209 while (c != '\n')
210 c = getc(ff); /* skip to the end of the line */
211 continue;
212 }
213
214 /* Get the name of the filter. */
215 filt_name_index = 0;
216 for (;;) {
217 c = getc_crlf(ff);
218 if (c == EOF(-1) || c == '\n')
219 break; /* End of line - or end of file */
220 if (c == '"') {
221 /* Closing quote. */
222 if (filt_name_index >= filt_name_len) {
223 /* Filter name buffer isn't long enough; double its length. */
224 filt_name_len *= 2;
225 filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
226 }
227 filt_name[filt_name_index] = '\0';
228 break;
229 }
230 if (c == '\\') {
231 /* Next character is escaped */
232 c = getc_crlf(ff);
233 if (c == EOF(-1) || c == '\n')
234 break; /* End of line - or end of file */
235 }
236 /* Add this character to the filter name string. */
237 if (filt_name_index >= filt_name_len) {
238 /* Filter name buffer isn't long enough; double its length. */
239 filt_name_len *= 2;
240 filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
241 }
242 filt_name[filt_name_index] = c;
243 filt_name_index++;
244 }
245
246 if (c == EOF(-1)) {
247 if (!ferror(ff)) {
248 /* EOF, not error; no newline seen before EOF */
249 ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 250, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
250 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 250, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
;
251 }
252 break; /* nothing more to read */
253 }
254
255 if (c != '"') {
256 /* No newline seen before end-of-line */
257 ws_warning("'%s' line %d doesn't have a closing quote.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 258, __func__, "'%s' line %d doesn't have a closing quote."
, ff_path, line); } } while (0)
258 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 258, __func__, "'%s' line %d doesn't have a closing quote."
, ff_path, line); } } while (0)
;
259 continue;
260 }
261
262 /* Skip over separating white space, if any. */
263 c = skip_whitespace(ff);
264
265 if (c == EOF(-1)) {
266 if (!ferror(ff)) {
267 /* EOF, not error; no newline seen before EOF */
268 ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 269, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
269 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 269, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
;
270 }
271 break; /* nothing more to read */
272 }
273
274 if (c == '\n') {
275 /* No filter expression */
276 ws_warning("'%s' line %d doesn't have a filter expression.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 277, __func__, "'%s' line %d doesn't have a filter expression."
, ff_path, line); } } while (0)
277 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 277, __func__, "'%s' line %d doesn't have a filter expression."
, ff_path, line); } } while (0)
;
278 continue;
279 }
280
281 /* "c" is the first non-white-space character; it's the first
282 character of the filter expression. */
283 filt_expr_index = 0;
284 for (;;) {
285 /* Add this character to the filter expression string. */
286 if (filt_expr_index >= filt_expr_len) {
287 /* Filter expression buffer isn't long enough; double its length. */
288 filt_expr_len *= 2;
289 filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
290 }
291 filt_expr[filt_expr_index] = c;
292 filt_expr_index++;
293
294 /* Get the next character. */
295 c = getc_crlf(ff);
296 if (c == EOF(-1) || c == '\n')
297 break;
298 }
299
300 if (c == EOF(-1)) {
301 if (!ferror(ff)) {
302 /* EOF, not error; no newline seen before EOF */
303 ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 304, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
304 line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c"
, 304, __func__, "'%s' line %d doesn't have a newline.", ff_path
, line); } } while (0)
;
305 }
306 break; /* nothing more to read */
307 }
308
309 /* We saw the ending newline; terminate the filter expression string */
310 if (filt_expr_index >= filt_expr_len) {
311 /* Filter expression buffer isn't long enough; double its length. */
312 filt_expr_len *= 2;
313 filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
314 }
315 filt_expr[filt_expr_index] = '\0';
316
317 /* Add the new filter to the list of filters */
318 flp = add_filter_entry(flp, filt_name, filt_expr);
319 }
320 if (ferror(ff)) {
321 report_warning("Error reading your %s file\n\"%s\": %s.",
322 ff_description, ff_path, g_strerror(errno(*__errno_location ())));
323 }
324 g_free(ff_path);
325 fclose(ff);
326 g_free(filt_name);
327 g_free(filt_expr);
328 list->list = flp;
329 return list;
330}
331
332/*
333 * Add a new filter to the end of a list.
334 */
335void
336ws_filter_list_add(filter_list_t *fl, const char *name,
337 const char *expression)
338{
339 fl->list = add_filter_entry(fl->list, name, expression);
340}
341
342static int
343compare_def(const void *def, const void *name)
344{
345 return g_strcmp0(((filter_def *)def)->name, name);
346}
347
348GList *ws_filter_list_find(filter_list_t *list, const char *name)
349{
350 return g_list_find_custom(list->list, name, compare_def);
351}
352
353/*
354 * Remove a filter from a list.
355 */
356bool_Bool
357ws_filter_list_remove(filter_list_t *list, const char *name)
358{
359 GList *p;
360
361 p = g_list_find_custom(list->list, name, compare_def);
362 if (p == NULL((void*)0))
363 return false0;
364 list->list = remove_filter_entry(list->list, p);
365 return true1;
366}
367
368/*
369 * Write out a list of filters.
370 *
371 * On error, report the error via the UI.
372 */
373void
374ws_filter_list_write(filter_list_t *list)
375{
376 char *pf_dir_path;
377 const char *ff_name, *ff_description;
378 char *ff_path, *ff_path_new;
379 GList *fl;
380 GList *flpp;
381 filter_def *filt;
382 FILE *ff;
383 unsigned char *p, c;
384
385 switch (list->type) {
386
387 case CFILTER_LIST:
388 ff_name = CFILTER_FILE_NAME"cfilters";
389 ff_description = "capture filter";
390 break;
391
392 case DFILTER_LIST:
393 ff_name = DFILTER_FILE_NAME"dfilters";
394 ff_description = "display filter";
395 break;
396
397 case DMACROS_LIST:
398 ff_name = DMACROS_FILE_NAME"dmacros";
399 ff_description = "display filter macro";
400 break;
401
402 default:
403 ws_assert_not_reached()ws_log_fatal_full("WSUtil", LOG_LEVEL_ERROR, "wsutil/filter_files.c"
, 403, __func__, "assertion \"not reached\" failed")
;
404 return;
405 }
406 fl = list->list;
407
408 /* Create the directory that holds personal configuration files,
409 if necessary. */
410 if (create_persconffile_dir(&pf_dir_path) == -1) {
411 report_failure("Can't create directory\n\"%s\"\nfor filter files: %s.",
412 pf_dir_path, g_strerror(errno(*__errno_location ())));
413 g_free(pf_dir_path);
414 return;
415 }
416
417 ff_path = get_persconffile_path(ff_name, true1);
418
419 /* Write to "XXX.new", and rename if that succeeds.
420 That means we don't trash the file if we fail to write it out
421 completely. */
422 ff_path_new = ws_strdup_printf("%s.new", ff_path)wmem_strdup_printf(((void*)0), "%s.new", ff_path);
423
424 if ((ff = ws_fopenfopen(ff_path_new, "w")) == NULL((void*)0)) {
425 /* We had an error saving the filter. */
426 report_failure("Error saving your %s file\nCouldn't open \"%s\": %s.",
427 ff_description, ff_path_new, g_strerror(errno(*__errno_location ())));
428 g_free(ff_path_new);
429 g_free(ff_path);
430 return;
431 }
432 flpp = g_list_first(fl);
433 while (flpp) {
434 filt = (filter_def *) flpp->data;
435
436 /* Write out the filter name as a quoted string; escape any quotes
437 or backslashes. */
438 putc('"', ff);
439 for (p = (unsigned char *)filt->name; (c = *p) != '\0'; p++) {
440 if (c == '"' || c == '\\')
441 putc('\\', ff);
442 putc(c, ff);
443 }
444 putc('"', ff);
445
446 /* Separate the filter name and value with a space. */
447 putc(' ', ff);
448
449 /* Write out the filter expression and a newline. */
450 fprintf(ff, "%s\n", filt->strval);
451 if (ferror(ff)) {
452 report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.",
453 ff_description, ff_path_new, g_strerror(errno(*__errno_location ())));
454 fclose(ff);
455 ws_unlinkunlink(ff_path_new);
456 g_free(ff_path_new);
457 g_free(ff_path);
458 return;
459 }
460 flpp = flpp->next;
461 }
462 if (fclose(ff) == EOF(-1)) {
463 report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.",
464 ff_description, ff_path_new, g_strerror(errno(*__errno_location ())));
465 ws_unlinkunlink(ff_path_new);
466 g_free(ff_path_new);
467 g_free(ff_path);
468 return;
469 }
470
471#ifdef _WIN32
472 /* ANSI C doesn't say whether "rename()" removes the target if it
473 exists; the Win32 call to rename files doesn't do so, which I
474 infer is the reason why the MSVC++ "rename()" doesn't do so.
475 We must therefore remove the target file first, on Windows.
476
477 XXX - ws_rename() should be ws_stdio_rename() on Windows,
478 and ws_stdio_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING,
479 so it should remove the target if it exists, so this stuff
480 shouldn't be necessary. Perhaps it dates back to when we were
481 calling rename(), with that being a wrapper around Microsoft's
482 _rename(), which didn't remove the target. */
483 if (ws_removeremove(ff_path) < 0 && errno(*__errno_location ()) != ENOENT2) {
484 /* It failed for some reason other than "it's not there"; if
485 it's not there, we don't need to remove it, so we just
486 drive on. */
487 report_failure("Error saving your %s file\nCouldn't remove \"%s\": %s.",
488 ff_description, ff_path, g_strerror(errno(*__errno_location ())));
489 ws_unlinkunlink(ff_path_new);
490 g_free(ff_path_new);
491 g_free(ff_path);
492 return;
493 }
494#endif
495
496 if (ws_renamerename(ff_path_new, ff_path) < 0) {
497 report_failure("Error saving your %s file\nCouldn't rename \"%s\" to \"%s\": %s.",
498 ff_description, ff_path_new, ff_path, g_strerror(errno(*__errno_location ())));
499 ws_unlinkunlink(ff_path_new);
500 g_free(ff_path_new);
501 g_free(ff_path);
502 return;
503 }
504 g_free(ff_path_new);
505 g_free(ff_path);
506}