[Openvpn-devel] block-dns using iservice: fix a potential double free

Message ID 20230201041831.2259419-1-selva.nair@gmail.com
State Superseded
Headers show
Series [Openvpn-devel] block-dns using iservice: fix a potential double free | expand

Commit Message

Selva Nair Feb. 1, 2023, 4:18 a.m. UTC
From: Selva Nair <selva.nair@gmail.com>

- An item added to undo-list was not removed on error, causing
  attempt to free again in Undo().
  Also fix a memory leak possibility in the same context.

Github: fixes OpenVPN/openvpn#232

Signed-off-by: Selva Nair <selva.nair@gmail.com>
---
 src/openvpnserv/interactive.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

Comments

Lev Stipakov Feb. 1, 2023, 9:36 a.m. UTC | #1
Hi,

I made a slightly different fix but then noticed your mail.

Indeed the problem is that get/set_interface_metric fails,
and we call FwpmEngineClose0 after updating the undo list. When
openvpn process exits, we execute commands in undo list,
and second call to FwpmEngineClose0 causes Access Violation
inside WFP.

Note that since get_interface_metric() fails, it sets interface_data->metric_v4
to -1. In this case we should not restore metrics back (which will
fail in any case).
We have a very similar code (including metrics value check) just below which
handles msg_del_block_dns. Can we factor it out into a separate function?
Selva Nair Feb. 1, 2023, 3:29 p.m. UTC | #2
Hi,

On Wed, Feb 1, 2023 at 4:37 AM Lev Stipakov <lstipakov@gmail.com> wrote:

> Hi,
>
> I made a slightly different fix but then noticed your mail.
>
> Indeed the problem is that get/set_interface_metric fails,
> and we call FwpmEngineClose0 after updating the undo list. When
> openvpn process exits, we execute commands in undo list,
> and second call to FwpmEngineClose0 causes Access Violation
> inside WFP.
>
> Note that since get_interface_metric() fails, it sets
> interface_data->metric_v4
> to -1. In this case we should not restore metrics back (which will
> fail in any case).
> We have a very similar code (including metrics value check) just below
> which
> handles msg_del_block_dns. Can we factor it out into a separate function?
>

Good point.  I have a version that splits "add" and "delete" actions into
separate functions and does something like this.

Please take a look here:
https://github.com/selvanair/openvpn/tree/block-dns-fix
The add and delete functions are in that order (with a forward declaration)
only to make review easier (less changes with ignore-all-spaces), will move
the latter up if it looks okay.

That said, if you have a better fix, I'm ready to review it in lieu of this.

Thanks,

Selva
Lev Stipakov Feb. 1, 2023, 4:16 p.m. UTC | #3
Hi,

> Good point.  I have a version that splits "add" and "delete" actions into separate functions and does something like this.
>
> Please take a look here: https://github.com/selvanair/openvpn/tree/block-dns-fix
> The add and delete functions are in that order (with a forward declaration) only to make review easier (less changes with ignore-all-spaces), will move the latter up if it looks okay.

Looks good to me, especially without whitespaces
https://github.com/OpenVPN/openvpn/pull/235/files?diff=unified&w=1

> That said, if you have a better fix, I'm ready to review it in lieu of this.

My change is more intrusive, idea is to bail out if
get_interface_metrics fails and update undo_list only if both filters
and metrics are applied,

https://github.com/lstipakov/openvpn/commit/8c2d0de178e0771295764ea773596c13ff5f2aa6

It is a bit sloppy on metrics cleanup, though - for example if
get_interface_metric for ipv6 fails but ipv4 is not - it won't restore
ipv4 metrics. On the other hand this is unlikely to happen.

Also I replaced 0x%x with %u in win_block_dns_service() for
consistency. You may want to do it in your patch too :)
Selva Nair Feb. 1, 2023, 4:54 p.m. UTC | #4
>
>
> Also I replaced 0x%x with %u in win_block_dns_service() for
> consistency. You may want to do it in your patch too :)
>

We have at least another place where it's %x, so will leave that for
another day. btw, shouldn't it be %d?

Selva

Patch

diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c
index 03361d66..636f32e9 100644
--- a/src/openvpnserv/interactive.c
+++ b/src/openvpnserv/interactive.c
@@ -780,7 +780,7 @@  static DWORD
 HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists)
 {
     DWORD err = 0;
-    block_dns_data_t *interface_data;
+    block_dns_data_t *interface_data = NULL;
     HANDLE engine = NULL;
     LPCWSTR exe_path;
 
@@ -794,6 +794,7 @@  HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists)
             interface_data = malloc(sizeof(block_dns_data_t));
             if (!interface_data)
             {
+                delete_block_dns_filters(engine);
                 return ERROR_OUTOFMEMORY;
             }
             interface_data->engine = engine;
@@ -818,8 +819,17 @@  HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists)
                                            BLOCK_DNS_IFACE_METRIC);
                 if (!err)
                 {
-                    set_interface_metric(msg->iface.index, AF_INET6,
-                                         BLOCK_DNS_IFACE_METRIC);
+                    err = set_interface_metric(msg->iface.index, AF_INET6,
+                                               BLOCK_DNS_IFACE_METRIC);
+                }
+                if (err)
+                {
+                    /* try to restore the interface metric values in case changed */
+                    set_interface_metric(msg->iface.index, AF_INET,
+                                         interface_data->metric_v4);
+                    set_interface_metric(msg->iface.index, AF_INET,
+                                         interface_data->metric_v4);
+                    RemoveListItem(&(*lists)[block_dns], CmpAny, NULL);
                 }
             }
         }
@@ -853,6 +863,7 @@  HandleBlockDNSMessage(const block_dns_message_t *msg, undo_lists_t *lists)
     if (err && engine)
     {
         delete_block_dns_filters(engine);
+        free(interface_data);
     }
 
     return err;