Promptly close the netcmd_semaphore handle after use
PATCH 1/1 Promptly close the netcmd_semaphore handle after use
If more than one openvpn processes are running and one aborts without releasing the semaphore, subsequent processes fail to get a lock for the semaphore. This may be avoided by not keeping open handles to the semaphore so that Windows can destroy it when no open handles remain.
See also: http://article.gmane.org/gmane.network.openvpn.devel/11913
--- src/openvpn/win32.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
src/openvpn/win32.c +6 -1
@@ -105,7 +105,6 @@ init_win32 (void) }window_title_clear (&window_title);win32_signal_clear (&win32_signal); -netcmd_semaphore_init (); }void
@@ -751,6 +750,10 @@ voidnetcmd_semaphore_lock (void) {const int timeout_seconds =600 ; + +if (!netcmd_semaphore.hand) +netcmd_semaphore_init (); +if (!semaphore_lock (&netcmd_semaphore, timeout_seconds *1000 ))msg (M_FATAL ,"Cannot lock net command semaphore" ); }
@@ -759,6 +762,8 @@ voidnetcmd_semaphore_release (void) {semaphore_release (&netcmd_semaphore); +/* netcmd_semaphore has max count of 1 - safe to close after release */ +semaphore_close (&netcmd_semaphore); }/*
Signature · 1 line
1.7.10.4
Hi,
src/openvpn/win32.c void · line 767
Quoted 7 lines · Selva Nair
src/openvpn/win32.c +2
-0@@ -759,6 +762,8 @@ voidnetcmd_semaphore_release (void) {semaphore_release (&netcmd_semaphore); +/* netcmd_semaphore has max count of 1 - safe to close after release */ +semaphore_close (&netcmd_semaphore); }
This should work, but it is more "aggressive" than I had in mind - releasing the semaphore afer every single call, so when setting up lots of routes with netsh.exe, we might end up opening and closing the semaphore "dozens" of times.
How expensive / slow is this? Has this significant impact?
My idea was to close the semaphore when the initialization phase is over, like in init.c, initialization_sequence_completed(), in the existing WIN32 block
#ifdef WIN32
netcmd_semaphore_close(); /* release handles */
fork_register_dns_action (c->c1.tuntap);
#endif
or something like that. Keeping your change to the "lock" call so it is then reopened at tunnel teardown, but is not open while openvpn is running in steady state.
But I'm open to "semaphores are cheap, we can just open/lock/release/close it on every call" arguments :-)
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...Hi,
Quoted 19 lines · Gert Doering
src/openvpn/win32.c void · line 765
Quoted 5 lines · Selva Nair
src/openvpn/win32.c +1
-0@@ -759,6 +762,8 @@ voidnetcmd_semaphore_release (void) {semaphore_release (&netcmd_semaphore); +/* netcmd_semaphore has max count of 1 - safe to close after release */
src/openvpn/win32.c void · line 767
Quoted 2 lines
src/openvpn/win32.c +1
-0+semaphore_close (&netcmd_semaphore); }This should work, but it is more "aggressive" than I had in mind - releasing
the semaphore afer every single call, so when setting up lots of routes with
netsh.exe, we might end up opening and closing the semaphore "dozens" of times.
Quoted 2 lines
How expensive / slow is this? Has this significant impact?
My idea was to close the semaphore when the initialization phase is over,
Quoted 12 lines
like in
init.c,initialization_sequence_completed(), in the existing WIN32 block#ifdef WIN32
netcmd_semaphore_close(); /* release handles */
fork_register_dns_action(c->c1.tuntap);
#endifor something like that. Keeping your change to the "lock" call so it is then reopened at tunnel teardown, but is not open while openvpn is running in steady state.
If we go this route we'll also need a netcmd_semaphore_close() when the SIGUSR1 loop exits, as a restart before initialization_completed could wait at management-hold with an open handle.
Or a SIGHUP will open the semaphore to delete routes and the restart could end up in management hold.
Quoted 3 lines
But I'm open to "semaphores are cheap, we can just open/lock/release/close it on every call" arguments :-)
Very unlikely to be a drag on performance -- I think createprocess will be where time is spent [*].
That said, calling semaphore_close() inside netcmd_semaphore_unlock() is not a style to be proud of. So I'm kind of inclined to do a version 2 along the lines you suggested but some testing will be needed to get it right.
Selva
P.S:
[*] FWIW, I did a timing test: Run using a modified openvpn that just does openvpn_execve of a route add and route delete inside a for loop. The times below are for a loop count of 100.
No semaphore: 2.45 to 2.49 seconds
With semaphore lock/unlock : 2.52 to 2.53 seconds With semaphore closed after reach unlock: 2.52 to 2.54 seconds
(Not enough statistics to give a precise number, but the overhead of using the semaphore is small and the open/close appears to make little difference). I'm surprised that it takes 2.5 seconds to add and delete a route just 100 times. Never use --route-method exe
Note that all times are real time, not CPU time. And the times are for just the for loop and scales linearly -- tested with 200 and 300 counts as well.
Quoted 7 lines · Selva Nair
Quoted 6 lines
But I'm open to "semaphores are cheap, we can just open/lock/release/ close
it on every call" arguments :-)
Quoted 7 lines
Very unlikely to be a drag on performance -- I think createprocess will be where time is spent [*].
That said, calling
semaphore_close()insidenetcmd_semaphore_unlock()is not a style to be proud of. So I'm kind of inclined to do a version 2 along the lines you suggested but some testing will be needed to get it right.
After looking into this further, I find redoing the patch along those lines is not ideal. A semaphore_close() has to be inserted before any possible long waits such as management-hold, pull-filter reject, or any other restart loops, which is going to be ugly and fragile.
The cleanest way is to close the semaphore soon after releasing it. Version 1 does that and I do not think there is any performance penalty involved. Logically, a more correct approach may be to replace all calls to netcmd_semaphore_release()
by netcmd_semaphore_close() but that involves more pervasive edits.
Having said that, I'm open to other suggestions.
Selva
ACK. Reviewed the patch before - performance impact is not high (compared to exec()'ing netsh.exe), and I can see the argument that the approach I suggested would be changing too many different places to be more "clean". Compile-tested, not run.
Your patch has been applied to the master and release/2.3 branch (bugfix).
commit 6aa4c9091300f62fae0bf7a9198de0edd2d8b7c7 (master) commit 13f3526a9b05bed63c50c93c5903b0cde0dcc7f4 (release/2.3)
Author: Selva Nair Date: Mon Jun 13 22:34:49 2016 -0400
Promptly close the netcmd_semaphore handle after use
Signed-off-by: Selva Nair <[email protected]...>
Acked-by: Gert Doering <[email protected]...>
Message-Id: <[email protected]...>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11919
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering