Bug Summary

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