mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-26 21:36:02 +08:00
Explicitly disallow reading input value from background processes (#2693)
This commit is contained in:
parent
d86c0f9cef
commit
6e2719e4b7
2 changed files with 57 additions and 8 deletions
|
|
@ -262,6 +262,7 @@ defmodule Livebook.Runtime.Evaluator.IOProxy do
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This request is used by Kino <= 0.13.1
|
||||||
defp io_request({:livebook_get_input_value, input_id}, state) do
|
defp io_request({:livebook_get_input_value, input_id}, state) do
|
||||||
input_cache =
|
input_cache =
|
||||||
Map.put_new_lazy(state.input_cache, input_id, fn ->
|
Map.put_new_lazy(state.input_cache, input_id, fn ->
|
||||||
|
|
@ -271,6 +272,25 @@ defmodule Livebook.Runtime.Evaluator.IOProxy do
|
||||||
{input_cache[input_id], %{state | input_cache: input_cache}}
|
{input_cache[input_id], %{state | input_cache: input_cache}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp io_request({:livebook_get_input_value, input_id, pid}, state) do
|
||||||
|
# We only allow reading the input value from the evaluator process,
|
||||||
|
# to make sure the cell is still evaluating. A different process
|
||||||
|
# could attempt to read the value after the cell evaluation was
|
||||||
|
# finished, in which case we could return a cached value from a
|
||||||
|
# different evaluation and we would not track the read properly.
|
||||||
|
|
||||||
|
if pid == state.evaluator do
|
||||||
|
input_cache =
|
||||||
|
Map.put_new_lazy(state.input_cache, input_id, fn ->
|
||||||
|
request_input_value(input_id, state)
|
||||||
|
end)
|
||||||
|
|
||||||
|
{input_cache[input_id], %{state | input_cache: input_cache}}
|
||||||
|
else
|
||||||
|
{{:error, :bad_process}, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp io_request({:livebook_get_file_path, file_id}, state) do
|
defp io_request({:livebook_get_file_path, file_id}, state) do
|
||||||
# We could cache forever, but we don't want the cache to pile up
|
# We could cache forever, but we don't want the cache to pile up
|
||||||
# indefinitely, so we just reuse the input cache which is cleared
|
# indefinitely, so we just reuse the input cache which is cleared
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ defmodule Livebook.Runtime.Evaluator.IOProxyTest do
|
||||||
|
|
||||||
io = Process.info(evaluator.pid)[:group_leader]
|
io = Process.info(evaluator.pid)[:group_leader]
|
||||||
IOProxy.before_evaluation(io, :ref, "cell")
|
IOProxy.before_evaluation(io, :ref, "cell")
|
||||||
%{io: io}
|
%{io: io, evaluator: evaluator}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ":stdio interoperability" do
|
describe ":stdio interoperability" do
|
||||||
|
|
@ -47,7 +47,19 @@ defmodule Livebook.Runtime.Evaluator.IOProxyTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "input" do
|
describe "input" do
|
||||||
test "responds to Livebook input request", %{io: io} do
|
test "responds to Livebook input request", %{io: io, evaluator: evaluator} do
|
||||||
|
pid =
|
||||||
|
spawn(fn ->
|
||||||
|
pid = evaluator.pid
|
||||||
|
assert livebook_get_input_value(io, "input1", pid) == {:ok, :value}
|
||||||
|
end)
|
||||||
|
|
||||||
|
reply_to_input_request(:ref, "input1", {:ok, :value}, 1)
|
||||||
|
|
||||||
|
await_termination(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "responds to Livebook input request (legacy)", %{io: io} do
|
||||||
pid =
|
pid =
|
||||||
spawn(fn ->
|
spawn(fn ->
|
||||||
assert livebook_get_input_value(io, "input1") == {:ok, :value}
|
assert livebook_get_input_value(io, "input1") == {:ok, :value}
|
||||||
|
|
@ -58,11 +70,12 @@ defmodule Livebook.Runtime.Evaluator.IOProxyTest do
|
||||||
await_termination(pid)
|
await_termination(pid)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "responds to subsequent requests with the same value", %{io: io} do
|
test "responds to subsequent requests with the same value", %{io: io, evaluator: evaluator} do
|
||||||
pid =
|
pid =
|
||||||
spawn(fn ->
|
spawn(fn ->
|
||||||
assert livebook_get_input_value(io, "input1") == {:ok, :value}
|
pid = evaluator.pid
|
||||||
assert livebook_get_input_value(io, "input1") == {:ok, :value}
|
assert livebook_get_input_value(io, "input1", pid) == {:ok, :value}
|
||||||
|
assert livebook_get_input_value(io, "input1", pid) == {:ok, :value}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
reply_to_input_request(:ref, "input1", {:ok, :value}, 1)
|
reply_to_input_request(:ref, "input1", {:ok, :value}, 1)
|
||||||
|
|
@ -70,13 +83,25 @@ defmodule Livebook.Runtime.Evaluator.IOProxyTest do
|
||||||
await_termination(pid)
|
await_termination(pid)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "before_evaluation/3 clears all cached input information", %{io: io} do
|
test "responds with error, when request does not come from the evaluator process", %{io: io} do
|
||||||
|
pid =
|
||||||
|
spawn(fn ->
|
||||||
|
pid = self()
|
||||||
|
assert livebook_get_input_value(io, "input1", pid) == {:error, :bad_process}
|
||||||
|
end)
|
||||||
|
|
||||||
|
await_termination(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "before_evaluation/3 clears all cached input information",
|
||||||
|
%{io: io, evaluator: evaluator} do
|
||||||
pid =
|
pid =
|
||||||
spawn_link(fn ->
|
spawn_link(fn ->
|
||||||
|
pid = evaluator.pid
|
||||||
IOProxy.before_evaluation(io, :ref, "cell")
|
IOProxy.before_evaluation(io, :ref, "cell")
|
||||||
assert livebook_get_input_value(io, "input1") == {:ok, :value1}
|
assert livebook_get_input_value(io, "input1", pid) == {:ok, :value1}
|
||||||
IOProxy.before_evaluation(io, :ref, "cell")
|
IOProxy.before_evaluation(io, :ref, "cell")
|
||||||
assert livebook_get_input_value(io, "input1") == {:ok, :value2}
|
assert livebook_get_input_value(io, "input1", pid) == {:ok, :value2}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
reply_to_input_request(:ref, "input1", {:ok, :value1}, 1)
|
reply_to_input_request(:ref, "input1", {:ok, :value1}, 1)
|
||||||
|
|
@ -177,6 +202,10 @@ defmodule Livebook.Runtime.Evaluator.IOProxyTest do
|
||||||
io_request(io, {:livebook_get_input_value, input_id})
|
io_request(io, {:livebook_get_input_value, input_id})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp livebook_get_input_value(io, input_id, pid) do
|
||||||
|
io_request(io, {:livebook_get_input_value, input_id, pid})
|
||||||
|
end
|
||||||
|
|
||||||
defp livebook_generate_token(io) do
|
defp livebook_generate_token(io) do
|
||||||
io_request(io, :livebook_generate_token)
|
io_request(io, :livebook_generate_token)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue