Bug Summary

File:builds/wireshark/wireshark/ui/qt/extcap_argument_multiselect.cpp
Warning:line 122, column 20
Potential leak of memory pointed to by 'pane'

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 extcap_argument_multiselect.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -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-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/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-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -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 -fcxx-exceptions -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-01-25-100340-3605-1 -x c++ /builds/wireshark/wireshark/ui/qt/extcap_argument_multiselect.cpp
1/* extcap_argument_multiselect.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <[email protected]>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include <extcap_argument.h>
11#include <extcap_argument_file.h>
12
13#include <wsutil/utf8_entities.h>
14
15#include <QLabel>
16#include <QLineEdit>
17#include <QBoxLayout>
18#include <QVariant>
19#include <QPushButton>
20#include <QMenu>
21#include <QHeaderView>
22#include <QInputDialog>
23
24#include <epan/prefs.h>
25#include <ui/qt/widgets/stock_icon_tool_button.h>
26#include <ui/qt/utils/color_utils.h>
27
28#include <extcap.h>
29#include <extcap_parser.h>
30#include <extcap_argument_multiselect.h>
31#include "ui/capture_globals.h"
32
33ExtArgMultiSelect::ExtArgMultiSelect(extcap_arg * argument, QObject *parent) :
34 ExtcapArgument(argument, parent), viewModel(0), treeView(0) {}
35
36ExtArgMultiSelect::~ExtArgMultiSelect()
37{
38}
39
40// NOLINTNEXTLINE(misc-no-recursion)
41QList<QStandardItem *> ExtArgMultiSelect::valueWalker(ExtcapValueList list, QStringList &defaults)
42{
43 ExtcapValueList::iterator iter = list.begin();
44 QList<QStandardItem *> items;
45
46 while (iter != list.end())
47 {
48 QStandardItem * item = new QStandardItem((*iter).value());
49 if ((*iter).enabled() == false)
50 {
51 item->setCheckable(false);
52 }
53 else
54 {
55 item->setCheckable(true);
56 }
57
58 item->setData((*iter).call(), Qt::UserRole);
59 if ((*iter).isDefault())
60 defaults << (*iter).call();
61
62 displayNames[(*iter).call()] = (*iter).value();
63
64 item->setSelectable(false);
65 item->setEditable(false);
66 // We recurse here, but the tree is only two levels deep
67 QList<QStandardItem *> childs = valueWalker((*iter).children(), defaults);
68 if (childs.length() > 0)
69 item->appendRows(childs);
70
71 items << item;
72 ++iter;
73 }
74
75 return items;
76}
77
78// NOLINTNEXTLINE(misc-no-recursion)
79void ExtArgMultiSelect::checkItemsWalker(QStandardItem * item, QStringList defaults)
80{
81 QModelIndex index;
82
83 if (item->hasChildren())
84 {
85 for (int row = 0; row < item->rowCount(); row++)
86 {
87 QStandardItem * child = item->child(row);
88 if (child != 0)
89 {
90 // We recurse here, but the tree is only two levels deep
91 checkItemsWalker(child, defaults);
92 }
93 }
94 }
95
96 QString data = item->data(Qt::UserRole).toString();
97
98 if (defaults.contains(data))
99 {
100 item->setCheckState(Qt::Checked);
101 index = item->index();
102 while (index.isValid())
103 {
104 treeView->setExpanded(index, true);
105 index = index.parent();
106 }
107 } else if (item->isCheckable()) {
108 item->setCheckState(Qt::Unchecked);
109 }
110}
111
112QWidget * ExtArgMultiSelect::createEditor(QWidget * parent)
113{
114 QStringList checked;
115 QLineEdit* searchBox;
116 QVBoxLayout* layout;
117 TreeSortFilterProxyModel* tableProxyModel;
118 QWidget* pane = new QWidget(parent);
2
Memory is allocated
119
120 QList<QStandardItem *> items = valueWalker(values, checked);
121 if (items.length() == 0)
3
Assuming the condition is true
4
Taking true branch
122 return new QWidget();
5
Potential leak of memory pointed to by 'pane'
123
124 /* Value can be empty if no items are checked */
125 if (_argument->pref_valptr && (*_argument->pref_valptr))
126 {
127 checked = QString(*_argument->pref_valptr).split(",", Qt::SkipEmptyParts);
128 }
129
130 /* Source model : contains the data */
131 viewModel = new QStandardItemModel(this);
132 QList<QStandardItem *>::const_iterator iter = items.constBegin();
133 while (iter != items.constEnd())
134 {
135 viewModel->appendRow((*iter));
136 ++iter;
137 }
138
139 /* Proxy model : filters the data */
140 tableProxyModel = new TreeSortFilterProxyModel(this);
141 tableProxyModel->setSourceModel(viewModel);
142
143 searchBox = new QLineEdit();
144 searchBox->setClearButtonEnabled(true);
145 searchBox->setPlaceholderText("Search");
146
147 treeView = new QTreeView();
148 treeView->setModel(tableProxyModel);
149
150 /* Shows at minimum 6 entries at most desktops */
151 treeView->setMinimumHeight(100);
152 treeView->setHeaderHidden(true);
153 treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
154 treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
155 treeView->expandAll();
156
157 for (int row = 0; row < viewModel->rowCount(); row++)
158 checkItemsWalker(((QStandardItemModel*)viewModel)->item(row), checked);
159
160 connect(viewModel, &QStandardItemModel::itemChanged, this, &ExtArgMultiSelect::valueChanged);
161 connect(searchBox, &QLineEdit::textChanged, this, [tableProxyModel](const QString& text) {
162 tableProxyModel->setFilterRegularExpression(QRegularExpression(text, QRegularExpression::PatternOption::CaseInsensitiveOption));
163 });
164
165 layout = new QVBoxLayout(pane);
166 layout->addWidget(searchBox);
167 layout->addWidget(treeView);
168 pane->setLayout(layout);
169
170 return pane;
171}
172
173bool TreeSortFilterProxyModel::filterAcceptsRow(int sourceRow,
174 const QModelIndex& sourceParent) const
175{
176 /* Only apply filter on leaves. */
177 if (sourceModel()->hasChildren(sourceModel()->index(sourceRow, 0, sourceParent)))
178 return true;
179
180 return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
181}
182
183QStringList ExtArgMultiSelect::checkedValues()
184{
185 if (viewModel == 0)
186 return QStringList();
187
188 QStringList result;
189 QModelIndexList checked = viewModel->match(viewModel->index(0, 0), Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly | Qt::MatchRecursive);
190 if (checked.size() <= 0)
191 return QStringList();
192
193 QModelIndexList::const_iterator iter = checked.constBegin();
194 while (iter != checked.constEnd())
195 {
196 QModelIndex index = (QModelIndex)(*iter);
197
198 result << viewModel->data(index, Qt::UserRole).toString();
199
200 ++iter;
201 }
202
203 return result;
204}
205
206QString ExtArgMultiSelect::value()
207{
208 return checkedValues().join(QString(','));
209}
210
211bool ExtArgMultiSelect::isValid()
212{
213 bool valid = true;
214
215 if (isRequired())
216 {
217 if (viewModel == 0)
218 valid = false;
219 else
220 {
221 QModelIndexList checked = viewModel->match(viewModel->index(0, 0), Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly | Qt::MatchRecursive);
222 if (checked.size() <= 0)
223 valid = false;
224 }
225 }
226
227 QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_filter_invalid_bg).name();
228 QString txtStyle("QTreeView { background-color: %1; } ");
229 if (viewModel != 0)
230 treeView->setStyleSheet(txtStyle.arg(valid ? QString("") : lblInvalidColor));
231
232 return valid;
233}
234
235QString ExtArgMultiSelect::defaultValue()
236{
237 QStringList checked;
238
239 QList<QStandardItem *> items = valueWalker(values, checked);
240 qDeleteAll(items);
241
242 return checked.join(QString(','));
243}
244
245bool ExtArgMultiSelect::isSetDefaultValueSupported()
246{
247 return true;
248}
249
250void ExtArgMultiSelect::setDefaultValue()
251{
252 QStringList checked;
253
254 if (viewModel == 0)
255 return;
256
257 checked = defaultValue().split(",", Qt::SkipEmptyParts);
258 for (int row = 0; row < viewModel->rowCount(); row++)
259 checkItemsWalker(((QStandardItemModel*)viewModel)->item(row), checked);
260}
261
262ExtArgTable::ExtArgTable(extcap_arg* argument, QObject* parent) :
263 ExtArgMultiSelect(argument, parent), extcap_options_dialog(0), addDialog(0), tableViewModel(0), tableView(0), paneLayout(0), toolbar(0) {}
264
265ExtArgTable::~ExtArgTable()
266{
267 // dialog is freed by its parent QWidget
268 if (tableView != 0)
269 delete tableView;
270 if (tableViewModel != 0)
271 delete tableViewModel;
272 if (toolbar != 0)
273 delete toolbar;
274 if (paneLayout != 0)
275 delete paneLayout;
276}
277
278ExtArgTableAddDialog::ExtArgTableAddDialog(QWidget* parent, QWidget* selector) : QDialog(parent)
279{
280 setWindowTitle("Add element");
281 setMinimumWidth(400);
282
283 QVBoxLayout* layout = new QVBoxLayout(this);
284 QPushButton* okButton = new QPushButton("OK", this);
285 QPushButton* cancelButton = new QPushButton("Cancel", this);
286
287 layout->addWidget(selector);
288 layout->addWidget(okButton);
289 layout->addWidget(cancelButton);
290
291 connect(okButton, &QPushButton::clicked, this, &QDialog::accept);
292 connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
293}
294
295QWidget* ExtArgTable::createEditor(QWidget* parent)
296{
297 // The wrapper widget we return
298 QWidget* pane = new QWidget(parent);
299 paneLayout = new QVBoxLayout(pane);
300
301 // Create dialog
302 QWidget* treeViewWidget = ExtArgMultiSelect::createEditor(addDialog);
1
Calling 'ExtArgMultiSelect::createEditor'
303 addDialog = new ExtArgTableAddDialog(parent, treeViewWidget);
304
305 // Instantiate the menu bar
306 toolbar = new QToolBar(pane);
307 toolbar->setStyleSheet("QToolBar { border: 1px solid gray; border-radius: 3px; }");
308 QAction* addAction = toolbar->addAction("Add");
309 QAction* addActionCustom = toolbar->addAction("Add custom");
310 QAction* removeAction = toolbar->addAction("Remove");
311 paneLayout->addWidget(toolbar);
312
313 // Instantiate empty table
314 tableView = new QTableView(pane);
315 tableView->setMinimumHeight(200);
316 tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
317 tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
318 tableView->verticalHeader()->hide();
319 tableView->horizontalHeader()->hide();
320
321 tableViewModel = new QStandardItemModel(pane);
322 tableView->setModel(tableViewModel);
323 paneLayout->addWidget(tableView);
324
325 /* Value can be empty if no items are checked */
326 if (_argument->pref_valptr && (*_argument->pref_valptr))
327 {
328 QStringList checked;
329 QStringList options;
330
331 if (_argument->prefix)
332 {
333 QStringList splitted = QString(*_argument->pref_valptr).split(_argument->prefix, Qt::SkipEmptyParts);
334 QStringList parts;
335 for (QString item : splitted)
336 {
337 parts = item.split(" ", Qt::SkipEmptyParts);
338 checked << parts.takeFirst();
339 options << parts.join(" ");
340 }
341 }
342 else
343 {
344 checked = QString(*_argument->pref_valptr).split(" ", Qt::SkipEmptyParts);
345 }
346 addChecked(checked, options);
347 }
348
349 // Add handler for click
350 connect(addAction, &QAction::triggered, this, &ExtArgTable::addKnown);
351 connect(addActionCustom, &QAction::triggered, this, &ExtArgTable::addCustom);
352 connect(removeAction, &QAction::triggered, this, &ExtArgTable::removeSelected);
353 return pane;
354}
355
356QString ExtArgTable::value()
357{
358 QStringList results;
359 for (int row = 0; row < tableViewModel->rowCount(); row++) {
360 QString s = ((QStandardItemModel*)tableViewModel)->item(row)->data(Qt::UserRole).toString();
361 QString config = ((QStandardItemModel*)tableViewModel)->item(row)->data(Qt::UserRole + 1).toString();
362 if (_argument->prefix != NULL__null)
363 {
364 s.prepend(" ");
365 s.prepend(_argument->prefix);
366 }
367 if (!config.isEmpty())
368 {
369 s.append(" ");
370 s.append(config);
371 }
372 results << s;
373 }
374 return results.join(" ");
375}
376
377void ExtArgTable::addChecked(QStringList checked, QStringList options)
378{
379 for (int i = 0; i < checked.size(); ++i) {
380 QStandardItem* item = new QStandardItem(checked[i]);
381 item->setData(checked[i], Qt::UserRole);
382 if (displayNames.contains(checked[i]))
383 {
384 item->setText(displayNames[checked[i]]);
385 }
386
387 if (_argument->configurable)
388 {
389 if (options.size() > i)
390 {
391 item->setData(QString(options[i]), Qt::UserRole + 1); // Currently: has configuration
392 }
393 else
394 {
395 item->setData(QString(), Qt::UserRole + 1); // Currently: no configuration
396 }
397
398 QStandardItem* btnItem = new QStandardItem();
399 tableViewModel->appendRow({ item, btnItem });
400
401 // Add button
402 StockIconToolButton* settingsButton = new StockIconToolButton(tableView, "x-capture-options");
403 settingsButton->setFixedWidth(30);
404 tableView->setIndexWidget(tableViewModel->indexFromItem(btnItem), settingsButton);
405
406 connect(settingsButton, &StockIconToolButton::clicked, this, [=]() {
407 QString optionValue = checked[i];
408 this->showExtcapOptionsDialogForOptionValue(item, optionValue);
409 });
410 }
411 else
412 {
413 tableViewModel->appendRow(item);
414 }
415 }
416
417 if (tableView->horizontalHeader()->count() > 0)
418 {
419 // Qt <6.9 bug (fixed in 6.9): setting the ResizeMode of a column before it has at least 1 element
420 // will make a segfault.
421 tableView->resizeColumnToContents(1);
422 tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
423 if (_argument->configurable)
424 tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
425 }
426}
427
428void ExtArgTable::setDefaultValue()
429{
430 QStringList checked;
431
432 if (tableViewModel == 0)
433 return;
434
435 tableViewModel->clear();
436 checked = defaultValue().split(",", Qt::SkipEmptyParts);
437 addChecked(checked, QStringList());
438}
439
440void ExtArgTable::addKnown()
441{
442 // Un-select everything in the selector: it's a new day.
443 QStringList checked;
444 for (int row = 0; row < viewModel->rowCount(); row++)
445 checkItemsWalker(((QStandardItemModel*)viewModel)->item(row), checked);
446
447 // Display "add" popup
448 if (addDialog->exec() == QDialog::Accepted)
449 {
450 checked = ExtArgMultiSelect::checkedValues();
451 addChecked(checked, QStringList());
452 }
453}
454
455void ExtArgTable::addCustom()
456{
457 // Prompt for custom input
458 bool ok;
459 QString text = QInputDialog::getText(
460 tableView,
461 tr("Add custom data"),
462 tr("Custom:"),
463 QLineEdit::Normal,
464 "",
465 &ok
466 );
467 if (ok && !text.isEmpty())
468 {
469 addChecked({ text }, QStringList());
470 }
471}
472
473void ExtArgTable::removeSelected()
474{
475 // Get current selection
476 QModelIndexList selected = tableView->selectionModel()->selectedIndexes();
477
478 // Remove them from list
479 for (int row = 0; row < selected.size(); row++)
480 tableViewModel->removeRow(selected[row].row());
481}
482
483void ExtArgTable::showExtcapOptionsDialogForOptionValue(QStandardItem* item, QString& option_value)
484{
485 QString device_name(_argument->device_name);
486 QString option_name(_argument->call + 2); /* skip -- */
487 extcap_options_dialog = ExtcapOptionsDialog::createForDevice(device_name, false, tableView->parentWidget(),
488 &option_name, &option_value);
489 /* The dialog returns null, if the given device name is not a valid extcap device */
490 if (extcap_options_dialog) {
491 extcap_options_dialog->setModal(true);
492 extcap_options_dialog->setAttribute(Qt::WA_DeleteOnClose);
493 connect(extcap_options_dialog, &ExtcapOptionsDialog::finished, this, [=]() {
494 this->extcap_options_finished(item);
495 });
496 extcap_options_dialog->show();
497 }
498}
499
500QString
501extcap_format_external_arguments(GHashTable* extcap_args)
502{
503 QString command_line;
504
505 GHashTableIter iter;
506 void *key, *value;
507 g_hash_table_iter_init(&iter, extcap_args);
508 while (g_hash_table_iter_next(&iter, &key, &value)) {
509 if (key != NULL__null)
510 {
511 command_line.append(g_strdup((char*)key)g_strdup_inline ((char*)key));
512 command_line.append(" ");
513 }
514 if (value != NULL__null)
515 {
516 command_line.append(g_strdup((char*)value)g_strdup_inline ((char*)value));
517 command_line.append(" ");
518 }
519 }
520
521 return command_line;
522}
523
524void ExtArgTable::extcap_options_finished(QStandardItem* item)
525{
526 interface_t* device;
527 bool dev_found = false;
528 for (unsigned if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++)
529 {
530 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx)(((interface_t*) (void *) (global_capture_opts.all_ifaces)->
data) [(if_idx)])
;
531 if (g_strcmp0(_argument->device_name, device->name) == 0 && device->if_info.type == IF_EXTCAP)
532 {
533 dev_found = true;
534 break;
535 }
536 }
537
538 if (dev_found && device->external_cap_args_settings != NULL__null)
539 {
540 QString arguments = extcap_format_external_arguments(device->external_cap_args_settings);
541 item->setData(arguments, Qt::UserRole + 1);
542 }
543}