Bug Summary

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