[PATCH] glibcompat: Updating "backport" 'g_string_replace'

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Update the vir_g_string_replace with following commits from glib:
  c9e48947e gstring: Fix a heap buffer overflow in the new
                     g_string_replace() code
  e8517e777 remove quadratic behavior in g_string_replace

Signed-off-by: Adam Julis <ajulis@xxxxxxxxxx>
---
 src/util/glibcompat.c | 125 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 105 insertions(+), 20 deletions(-)

diff --git a/src/util/glibcompat.c b/src/util/glibcompat.c
index bcb666992a..47e3edef13 100644
--- a/src/util/glibcompat.c
+++ b/src/util/glibcompat.c
@@ -65,7 +65,7 @@
 
 /**
  * Adapted (to pass syntax check) from 'g_string_replace' from
- * glib-2.81.1. Drop once minimum glib is bumped to 2.68.
+ * glib-2.83.3. Drop once minimum glib is bumped to 2.68.
  *
  * g_string_replace:
  * @string: a #GString
@@ -94,35 +94,120 @@ vir_g_string_replace(GString *string,
                      const gchar *replace,
                      guint limit)
 {
-    gsize f_len, r_len, pos;
-    gchar *cur, *next;
-    guint n = 0;
+    GString *new_string = NULL;
+    gsize f_len, r_len, new_len;
+    gchar *cur, *next, *first, *dst;
+    guint n;
 
     g_return_val_if_fail(string != NULL, 0);
     g_return_val_if_fail(find != NULL, 0);
     g_return_val_if_fail(replace != NULL, 0);
 
+    first = strstr(string->str, find);
+
+    if (first == NULL)
+        return 0;
+
+    new_len = string->len;
     f_len = strlen(find);
     r_len = strlen(replace);
-    cur = string->str;
 
-    while ((next = strstr(cur, find)) != NULL) {
-        pos = next - string->str;
-        g_string_erase(string, pos, f_len);
-        g_string_insert(string, pos, replace);
-        cur = string->str + pos + r_len;
-        n++;
-        /* Only match the empty string once at any given position, to
-         * avoid infinite loops */
-        if (f_len == 0) {
-            if (cur[0] == '\0')
-                break;
-            else
-                cur++;
+    /* It removes a lot of branches and possibility for infinite loops if we
+     * handle the case of an empty @find string separately. */
+    if (G_UNLIKELY(f_len == 0)) {
+        size_t i;
+        if (limit == 0 || limit > string->len) {
+            if (string->len > G_MAXSIZE - 1)
+                g_error("inserting in every position in string would overflow");
+
+            limit = string->len + 1;
+        }
+
+        if (r_len > 0 &&
+            (limit > G_MAXSIZE / r_len ||
+             limit * r_len > G_MAXSIZE - string->len))
+            g_error("inserting in every position in string would overflow");
+
+        new_len = string->len + limit * r_len;
+        new_string = g_string_sized_new(new_len);
+        for (i = 0; i < limit; i++) {
+            g_string_append_len(new_string, replace, r_len);
+            if (i < string->len)
+                g_string_append_c(new_string, string->str[i]);
         }
-        if (n == limit)
-            break;
+        if (limit < string->len)
+            g_string_append_len(new_string, string->str + limit, string->len - limit);
+
+        g_free(string->str);
+        string->allocated_len = new_string->allocated_len;
+        string->len = new_string->len;
+        string->str = g_string_free(g_steal_pointer(&new_string), FALSE);
+
+        return limit;
     }
+    /* Potentially do two passes: the first to calculate the length of the new string,
+     * new_len, if it’s going to be longer than the original string; and the second to
+     * do the replacements. The first pass is skipped if the new string is going to be
+     * no longer than the original.
+     *
+     * The second pass calls various g_string_insert_len() (and similar) methods
+     * which would normally potentially reallocate string->str, and hence
+     * invalidate the cur/next/first/dst pointers. Because we’ve pre-calculated
+     * the new_len and do all the string manipulations on new_string, that
+     * shouldn’t happen. This means we scan `string` while modifying
+     * `new_string`. */
+    do {
+        dst = first;
+        cur = first;
+        n = 0;
+        while ((next = strstr(cur, find)) != NULL) {
+            n++;
+
+            if (r_len <= f_len) {
+                memmove(dst, cur, next - cur);
+                dst += next - cur;
+                memcpy(dst, replace, r_len);
+                dst += r_len;
+            } else {
+                if (new_string == NULL) {
+                    new_len += r_len - f_len;
+                } else {
+                    g_string_append_len(new_string, cur, next - cur);
+                    g_string_append_len(new_string, replace, r_len);
+                }
+            }
+            cur = next + f_len;
+
+            if (n == limit)
+                break;
+        }
+
+        /* Append the trailing characters from after the final instance of @find
+         * in the input string. */
+        if (r_len <= f_len) {
+            /* First pass skipped. */
+            gchar *end = string->str + string->len;
+            memmove(dst, cur, end - cur);
+            end = dst + (end - cur);
+            *end = 0;
+            string->len = end - string->str;
+            break;
+        } else {
+            if (new_string == NULL) {
+                /* First pass. */
+                new_string = g_string_sized_new(new_len);
+                g_string_append_len(new_string, string->str, first - string->str);
+            } else {
+                /* Second pass. */
+                g_string_append_len(new_string, cur, (string->str + string->len) - cur);
+                g_free(string->str);
+                string->allocated_len = new_string->allocated_len;
+                string->len = new_string->len;
+                string->str = g_string_free(g_steal_pointer(&new_string), FALSE);
+                break;
+            }
+        }
+    } while (1);
 
     return n;
 }
-- 
2.47.1




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux