[Openvpn-devel,2/2] Replace realloc with new gc_realloc function

Message ID 20221227140249.3524943-2-arne@rfc2549.org
State Accepted
Headers show
Series None | expand

Commit Message

Arne Schwabe Dec. 27, 2022, 2:02 p.m. UTC
The realloc logic has the problem that it relies on the memory being
deallocated by uninit_options rather than by freeing the gc. This
does not always happen in all code path. Especially the crypto selftest
run by make check will not call uninit_options.

This introduces a gc_realloc function that ensures that the pointer is
instead freed when gc_free is called.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
 src/openvpn/buffer.c                   | 33 ++++++++++++++++++++++++++
 src/openvpn/buffer.h                   | 13 ++++++++++
 src/openvpn/options.c                  |  6 ++---
 tests/unit_tests/openvpn/test_buffer.c | 32 +++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 4 deletions(-)

Comments

Gert Doering Dec. 27, 2022, 7:08 p.m. UTC | #1
Acked-by: Gert Doering <gert@greenie.muc.de>

The initial tests were left to Github/ASAN builds, which are now 
"all green for real" - 
  https://github.com/cron2/openvpn/actions/runs/3789030448

Stare-at-code also looks good, though a bit complicated (and if I'm
reading this right, doing an initial allocation with gc_malloc() and
then calling gc_realloc() on it will double-free(), so *boom*) - sufficient
for the use case at hand (initial allocation also done with gc_realloc()),
but maybe it could be simplified and made robust against this case.

My manual "test one instance with 200 remotes" did what I expected
(and I discovered --management-query-remote ;-) ).

Your patch has been applied to the master and release/2.6 branch.

commit e7f2169772f90f9bf158a17f5656a6a985e74e31 (master)
commit 09267c1ccde6a8f0671c785413ca847e97bbc0a5 (release/2.6)
Author: Arne Schwabe
Date:   Tue Dec 27 15:02:45 2022 +0100

     Replace realloc with new gc_realloc function

     Signed-off-by: Arne Schwabe <arne@rfc2549.org>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Message-Id: <20221227140249.3524943-2-arne@rfc2549.org>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg25829.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
index 575d45a1f..e3d5ba241 100644
--- a/src/openvpn/buffer.c
+++ b/src/openvpn/buffer.c
@@ -412,6 +412,39 @@  gc_malloc(size_t size, bool clear, struct gc_arena *a)
     return ret;
 }
 
+void *
+gc_realloc(void *ptr, size_t size, struct gc_arena *a)
+{
+    void *ret = realloc(ptr, size);
+    check_malloc_return(ret);
+    if (a)
+    {
+        if (ptr && ptr != ret)
+        {
+            /* find the old entry and modify it if realloc changed
+             * the pointer */
+            struct gc_entry_special *e = NULL;
+            for (e = a->list_special; e != NULL; e = e->next)
+            {
+                if (e->addr == ptr)
+                {
+                    break;
+                }
+            }
+            ASSERT(e);
+            ASSERT(e->addr == ptr);
+            e->addr = ret;
+        }
+        else if (!ptr)
+        {
+            /* sets e->addr to newptr */
+            gc_addspecial(ret, free, a);
+        }
+    }
+
+    return ret;
+}
+
 void
 x_gc_free(struct gc_arena *a)
 {
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
index fece6336d..185226f7c 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -187,6 +187,19 @@  struct buffer string_alloc_buf(const char *str, struct gc_arena *gc);
 
 void gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a);
 
+/**
+ * allows to realloc a pointer previously allocated by gc_malloc or gc_realloc
+ *
+ * @note only use this function on pointers returned by gc_malloc or re_alloc
+ *       with the same gc_arena
+ *
+ * @param ptr   Pointer of the previously allocated memory
+ * @param size  New size
+ * @param a     gc_arena to use
+ * @return      new pointer
+ */
+void *
+gc_realloc(void *ptr, size_t size, struct gc_arena *a);
 
 #ifdef BUF_INIT_TRACKING
 #define buf_init(buf, offset) buf_init_debug(buf, offset, __FILE__, __LINE__)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 4e018fb84..e454b2ac0 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -918,12 +918,10 @@  uninit_options(struct options *o)
 {
     if (o->connection_list)
     {
-        free(o->connection_list->array);
         CLEAR(*o->connection_list);
     }
     if (o->remote_list)
     {
-        free(o->remote_list->array);
         CLEAR(*o->remote_list);
     }
     if (o->gc_owned)
@@ -2173,7 +2171,7 @@  alloc_connection_entry(struct options *options, const int msglevel)
     if (l->len == l->capacity)
     {
         int capacity = l->capacity + CONNECTION_LIST_SIZE;
-        struct connection_entry **ce = realloc(l->array, capacity*sizeof(struct connection_entry *));
+        struct connection_entry **ce = gc_realloc(l->array, capacity*sizeof(struct connection_entry *), &options->gc);
         if (ce == NULL)
         {
             msg(msglevel, "Unable to process more connection options: out of memory. Number of entries = %d", l->len);
@@ -2206,7 +2204,7 @@  alloc_remote_entry(struct options *options, const int msglevel)
     if (l->len == l->capacity)
     {
         int capacity = l->capacity + CONNECTION_LIST_SIZE;
-        struct remote_entry **re = realloc(l->array, capacity*sizeof(struct remote_entry *));
+        struct remote_entry **re = gc_realloc(l->array, capacity*sizeof(struct remote_entry *), &options->gc);
         if (re == NULL)
         {
             msg(msglevel, "Unable to process more remote options: out of memory. Number of entries = %d", l->len);
diff --git a/tests/unit_tests/openvpn/test_buffer.c b/tests/unit_tests/openvpn/test_buffer.c
index ac701669f..9e3b1d2e9 100644
--- a/tests/unit_tests/openvpn/test_buffer.c
+++ b/tests/unit_tests/openvpn/test_buffer.c
@@ -231,6 +231,37 @@  test_buffer_free_gc_two(void **state)
     gc_free(&gc);
 }
 
+
+static void
+test_buffer_gc_realloc(void **state)
+{
+    struct gc_arena gc = gc_new();
+
+    void *p1 = gc_realloc(NULL, 512, &gc);
+    void *p2 = gc_realloc(NULL, 512, &gc);
+
+    assert_ptr_not_equal(p1, p2);
+
+    memset(p1, '1', 512);
+    memset(p2, '2', 512);
+
+    p1 = gc_realloc(p1, 512, &gc);
+
+    /* allocate 512kB to ensure the pointer needs to change */
+    void *p1new = gc_realloc(p1, 512ul * 1024, &gc);
+    assert_ptr_not_equal(p1, p1new);
+
+    void *p2new = gc_realloc(p2, 512ul * 1024, &gc);
+    assert_ptr_not_equal(p2, p2new);
+
+    void *p3 = gc_realloc(NULL, 512, &gc);
+    memset(p3, '3', 512);
+
+
+    gc_free(&gc);
+}
+
+
 int
 main(void)
 {
@@ -259,6 +290,7 @@  main(void)
                                         test_buffer_list_teardown),
         cmocka_unit_test(test_buffer_free_gc_one),
         cmocka_unit_test(test_buffer_free_gc_two),
+        cmocka_unit_test(test_buffer_gc_realloc),
     };
 
     return cmocka_run_group_tests_name("buffer", tests, NULL, NULL);