From bc4362edacedb40c6edb9e855aa234c066b8292f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Tue, 27 Sep 2011 08:51:51 +0200
Subject: beam_lib: Handle rare race in the crypto key server functionality

In rare circumstances, there can be a race when the crypto key server
is started by beam_lib:crypto_key_fun/1 shortly after stopping it
using beam_lib:clear_crypto_key_fun/0. The race occurs because
the crypto key server first sends back the reply to the calling
process, then terminates. (The race is probably more likely to happen
on CPUs with hyper threading.)
---
 lib/stdlib/src/beam_lib.erl        | 14 +++++++++-----
 lib/stdlib/test/beam_lib_SUITE.erl | 10 ++++++++++
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index d9c645d787..fdfbb2e998 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -893,13 +893,17 @@ call_crypto_server(Req) ->
 	gen_server:call(?CRYPTO_KEY_SERVER, Req, infinity)
     catch
 	exit:{noproc,_} ->
-	    start_crypto_server(),
-	    erlang:yield(),
-	    call_crypto_server(Req)
+	    %% Not started.
+	    call_crypto_server_1(Req);
+	exit:{normal,_} ->
+	    %% The process finished just as we called it.
+	    call_crypto_server_1(Req)
     end.
 
-start_crypto_server() ->
-    gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []).
+call_crypto_server_1(Req) ->
+    gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []),
+    erlang:yield(),
+    call_crypto_server(Req).
 
 -spec init([]) -> {'ok', #state{}}.
 
diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl
index 91fff3cee4..27520a5c88 100644
--- a/lib/stdlib/test/beam_lib_SUITE.erl
+++ b/lib/stdlib/test/beam_lib_SUITE.erl
@@ -571,8 +571,18 @@ do_encrypted_abstr(Beam, Key) ->
     ?line {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]),
 
     ?line {ok,cleared} = beam_lib:clear_crypto_key_fun(),
+
+    %% Try to force a stop/start race.
+    ?line start_stop_race(10000),
+
     ok.
 
+start_stop_race(0) ->
+    ok;
+start_stop_race(N) ->
+    {error,_} = beam_lib:crypto_key_fun(bad_fun),
+    undefined = beam_lib:clear_crypto_key_fun(),
+    start_stop_race(N-1).
 
 bad_fun(F) ->
     {error,E} = beam_lib:crypto_key_fun(F),
-- 
cgit v1.2.3