Laszlo Ersek
2022-Sep-11 14:24 UTC
[Libguestfs] [p2v PATCH 0/4] fix crash in "v2v_version" string lifecycle management
We free the "v2v_version" string with a wrong function, which may cause a crash. The last patch fixes this problem. The first three patches work toward eliminating shadowed identifiers, because shadowing impeded my analysis of "v2v_version"'s life cycle. Laszlo Laszlo Ersek (4): miniexpect: upgrade to miniexpect with PR#1 eliminate shadowing of global variables p2v-c.m4: elicit a stricter set of warnings from gcc ssh.c: fix crash in "v2v_version" lifecycle management gui.c | 48 ++++++++++---------- main.c | 11 +++-- miniexpect/miniexpect.c | 2 +- ssh.c | 18 ++++---- m4/p2v-c.m4 | 2 +- miniexpect/README | 23 ++++++---- 6 files changed, 56 insertions(+), 48 deletions(-)
Laszlo Ersek
2022-Sep-11 14:24 UTC
[Libguestfs] [p2v PATCH 1/4] miniexpect: upgrade to miniexpect with PR#1
Incorporate miniexpect with
<https://github.com/rwmjones/miniexpect/pull/1> applied, so we can build
virt-p2v with "-Wall -Wextra -Wshadow" later.
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
miniexpect/miniexpect.c | 2 +-
miniexpect/README | 23 ++++++++++++--------
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/miniexpect/miniexpect.c b/miniexpect/miniexpect.c
index 77d781c0be9f..85335147034b 100644
--- a/miniexpect/miniexpect.c
+++ b/miniexpect/miniexpect.c
@@ -356,7 +356,7 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps,
if (match_data)
ovector = pcre2_get_ovector_pointer (match_data);
- if (ovector != NULL && ovector[1] >= 0)
+ if (ovector != NULL && ovector[1] != ~(PCRE2_SIZE)0)
h->next_match = ovector[1];
else
h->next_match = -1;
diff --git a/miniexpect/README b/miniexpect/README
index 828c449d5f33..030d26570f41 100644
--- a/miniexpect/README
+++ b/miniexpect/README
@@ -1,14 +1,19 @@
-miniexpect is a very simple expect-like library for C.
+Miniexpect is a very simple expect-like library
-It has a saner interface than libexpect, and doesn't depend on Tcl.
-It is also thread safe, const-correct and uses modern C standards.
+Expect (https://core.tcl-lang.org/expect/index) is a venerable Tcl
+program for automating interactive services, often for automating
+logins over telnet, ftp, ssh, etc.
-It is standalone, except that it requires the PCRE2 (Perl Compatible
-Regular Expressions) library from http://www.pcre.org/. The PCRE2
-dependency is fundamental because we want to offer the most powerful
-regular expression syntax to match on, but more importantly because
-PCRE2 has a convenient way to detect partial matches which made this
-library very simple to implement.
+Miniexpect is a C library which has a saner interface than libexpect,
+and doesn't depend on Tcl. It is also thread safe, const-correct and
+uses modern C standards.
+
+Miniexpect is standalone, except that it requires the PCRE2 (Perl
+Compatible Regular Expressions) library from http://www.pcre.org/.
+The PCRE2 dependency is fundamental because we want to offer the most
+powerful regular expression syntax to match on, but more importantly
+because PCRE2 has a convenient way to detect partial matches which
+made miniexpect very simple to implement.
License
-------
Laszlo Ersek
2022-Sep-11 14:24 UTC
[Libguestfs] [p2v PATCH 2/4] eliminate shadowing of global variables
Shadowing of global variables, especially if it's intentional and/or as
wide-spread in a codebase as it is in virt-p2v, is very harmful. Eliminate
it by renaming a number of function parameters, appending a "_p"
suffix.
(The shadowing of "v2v_version" interfered with the analysis of the
crash
that I originally meant to fix today. The fix is now the last patch in the
series.)
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
gui.c | 48 ++++++++++----------
main.c | 11 +++--
ssh.c | 14 +++---
3 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/gui.c b/gui.c
index 0e90235f7dfe..4b98a44fbbe9 100644
--- a/gui.c
+++ b/gui.c
@@ -680,12 +680,14 @@ connection_next_clicked (GtkWidget *w, gpointer data)
/*----------------------------------------------------------------------*/
/* Conversion dialog. */
-static void populate_disks (GtkTreeView *disks_list);
-static void populate_removable (GtkTreeView *removable_list);
-static void populate_interfaces (GtkTreeView *interfaces_list);
+static void populate_disks (GtkTreeView *disks_list_p);
+static void populate_removable (GtkTreeView *removable_list_p);
+static void populate_interfaces (GtkTreeView *interfaces_list_p);
static void toggled (GtkCellRendererToggle *cell, gchar *path_str, gpointer
data);
static void network_edited_callback (GtkCellRendererToggle *cell, gchar
*path_str, gchar *new_text, gpointer data);
-static gboolean maybe_identify_click (GtkWidget *interfaces_list,
GdkEventButton *event, gpointer data);
+static gboolean maybe_identify_click (GtkWidget *interfaces_list_p,
+ GdkEventButton *event,
+ gpointer data);
static void set_disks_from_ui (struct config *);
static void set_removable_from_ui (struct config *);
static void set_interfaces_from_ui (struct config *);
@@ -1093,7 +1095,7 @@ repopulate_output_combo (struct config *config)
* Populate the C<Fixed hard disks> treeview.
*/
static void
-populate_disks (GtkTreeView *disks_list)
+populate_disks (GtkTreeView *disks_list_p)
{
GtkListStore *disks_store;
GtkCellRenderer *disks_col_convert, *disks_col_device;
@@ -1136,11 +1138,11 @@ populate_disks (GtkTreeView *disks_list)
-1);
}
}
- gtk_tree_view_set_model (disks_list,
+ gtk_tree_view_set_model (disks_list_p,
GTK_TREE_MODEL (disks_store));
- gtk_tree_view_set_headers_visible (disks_list, TRUE);
+ gtk_tree_view_set_headers_visible (disks_list_p, TRUE);
disks_col_convert = gtk_cell_renderer_toggle_new ();
- gtk_tree_view_insert_column_with_attributes (disks_list,
+ gtk_tree_view_insert_column_with_attributes (disks_list_p,
-1,
_("Convert"),
disks_col_convert,
@@ -1148,7 +1150,7 @@ populate_disks (GtkTreeView *disks_list)
NULL);
gtk_cell_renderer_set_alignment (disks_col_convert, 0.5, 0.0);
disks_col_device = gtk_cell_renderer_text_new ();
- gtk_tree_view_insert_column_with_attributes (disks_list,
+ gtk_tree_view_insert_column_with_attributes (disks_list_p,
-1,
_("Device"),
disks_col_device,
@@ -1164,7 +1166,7 @@ populate_disks (GtkTreeView *disks_list)
* Populate the C<Removable media> treeview.
*/
static void
-populate_removable (GtkTreeView *removable_list)
+populate_removable (GtkTreeView *removable_list_p)
{
GtkListStore *removable_store;
GtkCellRenderer *removable_col_convert, *removable_col_device;
@@ -1187,11 +1189,11 @@ populate_removable (GtkTreeView *removable_list)
-1);
}
}
- gtk_tree_view_set_model (removable_list,
+ gtk_tree_view_set_model (removable_list_p,
GTK_TREE_MODEL (removable_store));
- gtk_tree_view_set_headers_visible (removable_list, TRUE);
+ gtk_tree_view_set_headers_visible (removable_list_p, TRUE);
removable_col_convert = gtk_cell_renderer_toggle_new ();
- gtk_tree_view_insert_column_with_attributes (removable_list,
+ gtk_tree_view_insert_column_with_attributes (removable_list_p,
-1,
_("Convert"),
removable_col_convert,
@@ -1199,7 +1201,7 @@ populate_removable (GtkTreeView *removable_list)
NULL);
gtk_cell_renderer_set_alignment (removable_col_convert, 0.5, 0.0);
removable_col_device = gtk_cell_renderer_text_new ();
- gtk_tree_view_insert_column_with_attributes (removable_list,
+ gtk_tree_view_insert_column_with_attributes (removable_list_p,
-1,
_("Device"),
removable_col_device,
@@ -1215,7 +1217,7 @@ populate_removable (GtkTreeView *removable_list)
* Populate the C<Network interfaces> treeview.
*/
static void
-populate_interfaces (GtkTreeView *interfaces_list)
+populate_interfaces (GtkTreeView *interfaces_list_p)
{
GtkListStore *interfaces_store;
GtkCellRenderer *interfaces_col_convert, *interfaces_col_device,
@@ -1257,11 +1259,11 @@ populate_interfaces (GtkTreeView *interfaces_list)
-1);
}
}
- gtk_tree_view_set_model (interfaces_list,
+ gtk_tree_view_set_model (interfaces_list_p,
GTK_TREE_MODEL (interfaces_store));
- gtk_tree_view_set_headers_visible (interfaces_list, TRUE);
+ gtk_tree_view_set_headers_visible (interfaces_list_p, TRUE);
interfaces_col_convert = gtk_cell_renderer_toggle_new ();
- gtk_tree_view_insert_column_with_attributes (interfaces_list,
+ gtk_tree_view_insert_column_with_attributes (interfaces_list_p,
-1,
_("Convert"),
interfaces_col_convert,
@@ -1269,7 +1271,7 @@ populate_interfaces (GtkTreeView *interfaces_list)
NULL);
gtk_cell_renderer_set_alignment (interfaces_col_convert, 0.5, 0.0);
interfaces_col_device = gtk_cell_renderer_text_new ();
- gtk_tree_view_insert_column_with_attributes (interfaces_list,
+ gtk_tree_view_insert_column_with_attributes (interfaces_list_p,
-1,
_("Device"),
interfaces_col_device,
@@ -1277,7 +1279,7 @@ populate_interfaces (GtkTreeView *interfaces_list)
NULL);
gtk_cell_renderer_set_alignment (interfaces_col_device, 0.0, 0.0);
interfaces_col_network = gtk_cell_renderer_text_new ();
- gtk_tree_view_insert_column_with_attributes (interfaces_list,
+ gtk_tree_view_insert_column_with_attributes (interfaces_list_p,
-1,
_("Connect to virtual
network"),
interfaces_col_network,
@@ -1337,7 +1339,7 @@ network_edited_callback (GtkCellRendererToggle *cell,
gchar *path_str,
* L<https://en.wikibooks.org/wiki/GTK%2B_By_Example/Tree_View/Events>
*/
static gboolean
-maybe_identify_click (GtkWidget *interfaces_list, GdkEventButton *event,
+maybe_identify_click (GtkWidget *interfaces_list_p, GdkEventButton *event,
gpointer data)
{
gboolean ret = FALSE; /* Did we handle this event? */
@@ -1352,14 +1354,14 @@ maybe_identify_click (GtkWidget *interfaces_list,
GdkEventButton *event,
gdouble event_x, event_y;
if (gdk_event_get_coords ((const GdkEvent *) event, &event_x,
&event_y)
- && gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW
(interfaces_list),
+ && gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW
(interfaces_list_p),
event_x, event_y,
&path, &column, NULL, NULL))
{
GList *cols;
gint column_index;
/* Get column index. */
- cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (interfaces_list));
+ cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (interfaces_list_p));
column_index = g_list_index (cols, (gpointer) column);
g_list_free (cols);
diff --git a/main.c b/main.c
index 6c44183b65b3..8a93f9eb402b 100644
--- a/main.c
+++ b/main.c
@@ -121,12 +121,13 @@ display_short_options (const char *format)
}
static void
-display_long_options (const struct option *long_options)
+display_long_options (const struct option *long_options_p)
{
- while (long_options->name) {
- if (STRNEQ (long_options->name, "long-options") &&
STRNEQ (long_options->name, "short-options"))
- printf ("--%s\n", long_options->name);
- long_options++;
+ while (long_options_p->name) {
+ if (STRNEQ (long_options_p->name, "long-options") &&
+ STRNEQ (long_options_p->name, "short-options"))
+ printf ("--%s\n", long_options_p->name);
+ long_options_p++;
}
exit (EXIT_SUCCESS);
}
diff --git a/ssh.c b/ssh.c
index 3b3e441b84ac..059f0e224cba 100644
--- a/ssh.c
+++ b/ssh.c
@@ -743,7 +743,7 @@ scp_file (struct config *config, const char *target, const
char *local, ...)
static void add_input_driver (const char *name);
static void add_output_driver (const char *name);
-static int compatible_version (const char *v2v_version);
+static int compatible_version (const char *v2v_version_p);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" /* WTF?
*/
@@ -1018,15 +1018,15 @@ add_output_driver (const char *name)
}
static int
-compatible_version (const char *v2v_version)
+compatible_version (const char *v2v_version_p)
{
unsigned v2v_major, v2v_minor;
/* The major version must always be 1 or 2. */
- if (!STRPREFIX (v2v_version, "1.") && !STRPREFIX
(v2v_version, "2.")) {
+ if (!STRPREFIX (v2v_version_p, "1.") && !STRPREFIX
(v2v_version_p, "2.")) {
set_ssh_error ("virt-v2v major version is neither 1 nor 2
(\"%s\"), "
"this version of virt-p2v is not compatible.",
- v2v_version);
+ v2v_version_p);
return 0;
}
@@ -1035,16 +1035,16 @@ compatible_version (const char *v2v_version)
* that we published during development, nor (b) using old virt-v2v.
* We should remain compatible with any virt-v2v after 1.28.
*/
- if (sscanf (v2v_version, "%u.%u", &v2v_major, &v2v_minor)
!= 2) {
+ if (sscanf (v2v_version_p, "%u.%u", &v2v_major, &v2v_minor)
!= 2) {
set_ssh_internal_error ("cannot parse virt-v2v version string
(\"%s\")",
- v2v_version);
+ v2v_version_p);
return 0;
}
if (v2v_major == 1 && v2v_minor < 28) {
set_ssh_error ("virt-v2v version is < 1.28 (\"%s\"),
"
"you must upgrade to virt-v2v >= 1.28 on "
- "the conversion server.", v2v_version);
+ "the conversion server.", v2v_version_p);
return 0;
}
Laszlo Ersek
2022-Sep-11 14:25 UTC
[Libguestfs] [p2v PATCH 3/4] p2v-c.m4: elicit a stricter set of warnings from gcc
Add "-Wextra" because it's good.
Add "-Wshadow" because it's good, but covered by neither
"-Wall" or
"-Wextra".
Exclude warnings about unused parameters (i.e., add
"-Wno-unused-parameter"); while normally it would be valuable, we have
a
whole bunch of GTK callbacks with fixed parameter lists but no actual use
for those params in our callback bodies. Littering the code with tens of
"__attribute__ ((unused))" decorations is arguably worse than
silencing
this one warning type.
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
m4/p2v-c.m4 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/m4/p2v-c.m4 b/m4/p2v-c.m4
index bbf48625ae05..d23e1977b921 100644
--- a/m4/p2v-c.m4
+++ b/m4/p2v-c.m4
@@ -38,7 +38,7 @@ AC_ARG_ENABLE([werror],
gcc_warnings=$enableval],
[gcc_warnings=no]
)
-WARN_CFLAGS="-Wall"
+WARN_CFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter"
AC_SUBST([WARN_CFLAGS])
if test "x$gcc_warnings" = "xyes"; then
WERROR_CFLAGS="-Werror"
Laszlo Ersek
2022-Sep-11 14:25 UTC
[Libguestfs] [p2v PATCH 4/4] ssh.c: fix crash in "v2v_version" lifecycle management
We only ever set "v2v_version" to non-NULL with pcre2_substring_get_bynumber(), as suggested by valgrind and then verified by code review. Substrings extracted with this function cannot be released with free(); we must call pcre2_substring_free(). http://www.pcre.org/current/doc/html/pcre2_substring_get_bynumber.html http://www.pcre.org/current/doc/html/pcre2_substring_free.html The symptom of this bug is that the second click on the "Test Connection" causes free() to print:> free(): invalid pointerand then to call abort(). Signed-off-by: Laszlo Ersek <lersek at redhat.com> --- ssh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssh.c b/ssh.c index 059f0e224cba..566121b172c5 100644 --- a/ssh.c +++ b/ssh.c @@ -765,7 +765,7 @@ test_connection (struct config *config) /* Clear any previous version information since we may be connecting * to a different server. */ - free (v2v_version); + pcre2_substring_free ((PCRE2_UCHAR *)v2v_version); v2v_version = NULL; /* Send 'virt-v2v --version' command and hope we get back a version string. @@ -788,7 +788,7 @@ test_connection (struct config *config) { 0 } }, match_data)) { case 100: /* Got version string. */ - free (v2v_version); + pcre2_substring_free ((PCRE2_UCHAR *)v2v_version); pcre2_substring_get_bynumber (match_data, 1, (PCRE2_UCHAR **) &v2v_version, &verlen); #if DEBUG_STDERR