Fix use-after-free in BM_FeedInputFetchOutput().
We were holding on to Node* in a graph whose ownership was transferred to
test::Benchmark(). This was not previously an issue, because the executor
implementation would hold on to a copy of the graph until the executor itself
was deleted, but it became an issue when the executor now that the executor no
longer stores a copy of the graph.
PiperOrigin-RevId: 273515081
diff --git a/tensorflow/core/common_runtime/executor_test.cc b/tensorflow/core/common_runtime/executor_test.cc
index 3167c91..e994512 100644
--- a/tensorflow/core/common_runtime/executor_test.cc
+++ b/tensorflow/core/common_runtime/executor_test.cc
@@ -479,12 +479,18 @@
Node* y = test::graph::Recv(g, "y", "float", ALICE, 1, BOB);
Node* sum = test::graph::Add(g, x, y);
Node* z = test::graph::Send(g, sum, "z", BOB, 1, ALICE);
+
+ string x_key = test::GetRendezvousKey(x);
+ string y_key = test::GetRendezvousKey(y);
+ string z_key = test::GetRendezvousKey(z);
+
Tensor val(DT_FLOAT, TensorShape({}));
val.scalar<float>()() = 3.14;
#ifdef PLATFORM_GOOGLE
SetBenchmarkItemsProcessed(static_cast<int64>(iters));
#endif // PLATFORM_GOOGLE
- test::Benchmark("cpu", g).RunWithArgs({{x, val}, {y, val}}, {z}, iters);
+ test::Benchmark("cpu", g).RunWithRendezvousArgs({{x_key, val}, {y_key, val}},
+ {z_key}, iters);
}
BENCHMARK(BM_FeedInputFetchOutput);
diff --git a/tensorflow/core/common_runtime/kernel_benchmark_testlib.cc b/tensorflow/core/common_runtime/kernel_benchmark_testlib.cc
index fe34083..fe70305 100644
--- a/tensorflow/core/common_runtime/kernel_benchmark_testlib.cc
+++ b/tensorflow/core/common_runtime/kernel_benchmark_testlib.cc
@@ -117,7 +117,7 @@
}
}
-void Benchmark::Run(int iters) { RunWithArgs({}, {}, iters); }
+void Benchmark::Run(int iters) { RunWithRendezvousArgs({}, {}, iters); }
string GetRendezvousKey(const Node* node) {
string send_device;
@@ -133,23 +133,12 @@
recv_device, tensor_name, FrameAndIter(0, 0));
}
-void Benchmark::RunWithArgs(
- const std::vector<std::pair<const Node*, Tensor>>& inputs,
- const std::vector<const Node*>& outputs, int iters) {
+void Benchmark::RunWithRendezvousArgs(
+ const std::vector<std::pair<string, Tensor>>& inputs,
+ const std::vector<string>& outputs, int iters) {
if (!device_ || iters == 0) {
return;
}
- // Gets inputs' and outputs' rendezvous keys.
- std::vector<std::pair<string, Tensor>> in;
- in.reserve(inputs.size());
- for (const auto& p : inputs) {
- in.push_back({GetRendezvousKey(p.first), p.second});
- }
- std::vector<string> out;
- out.reserve(outputs.size());
- for (const auto& n : outputs) {
- out.push_back(GetRendezvousKey(n));
- }
Tensor unused; // In benchmark, we don't care the return value.
bool is_dead;
@@ -161,13 +150,13 @@
};
static const int kWarmupRuns = 3;
for (int i = 0; i < kWarmupRuns; ++i) {
- for (const auto& p : in) {
+ for (const auto& p : inputs) {
Rendezvous::ParsedKey parsed;
TF_CHECK_OK(Rendezvous::ParseKey(p.first, &parsed));
TF_CHECK_OK(rendez_->Send(parsed, Rendezvous::Args(), p.second, false));
}
TF_CHECK_OK(exec_->Run(args));
- for (const string& key : out) {
+ for (const string& key : outputs) {
Rendezvous::ParsedKey parsed;
TF_CHECK_OK(Rendezvous::ParseKey(key, &parsed));
TF_CHECK_OK(rendez_->Recv(parsed, Rendezvous::Args(), &unused, &is_dead));
@@ -178,13 +167,13 @@
testing::StartTiming();
while (iters-- > 0) {
- for (const auto& p : in) {
+ for (const auto& p : inputs) {
Rendezvous::ParsedKey parsed;
TF_CHECK_OK(Rendezvous::ParseKey(p.first, &parsed));
TF_CHECK_OK(rendez_->Send(parsed, Rendezvous::Args(), p.second, false));
}
TF_CHECK_OK(exec_->Run(args));
- for (const string& key : out) {
+ for (const string& key : outputs) {
Rendezvous::ParsedKey parsed;
TF_CHECK_OK(Rendezvous::ParseKey(key, &parsed));
TF_CHECK_OK(rendez_->Recv(parsed, Rendezvous::Args(), &unused, &is_dead));
diff --git a/tensorflow/core/common_runtime/kernel_benchmark_testlib.h b/tensorflow/core/common_runtime/kernel_benchmark_testlib.h
index b1557c5..742f40d 100644
--- a/tensorflow/core/common_runtime/kernel_benchmark_testlib.h
+++ b/tensorflow/core/common_runtime/kernel_benchmark_testlib.h
@@ -46,12 +46,13 @@
void Run(int iters);
// If "g" contains send/recv nodes, before each execution, we send
- // inputs to the corresponding recv nodes in the graph, after each
- // execution, we recv outputs from the corresponding send nodes in
+ // inputs to the corresponding recv keys in the graph, after each
+ // execution, we recv outputs from the corresponding send keys in
// the graph. In the benchmark, we throw away values returned by the
// graph.
- void RunWithArgs(const std::vector<std::pair<const Node*, Tensor>>& inputs,
- const std::vector<const Node*>& outputs, int iters);
+ void RunWithRendezvousArgs(
+ const std::vector<std::pair<string, Tensor>>& inputs,
+ const std::vector<string>& outputs, int iters);
private:
thread::ThreadPool* pool_ = nullptr;
@@ -62,6 +63,9 @@
TF_DISALLOW_COPY_AND_ASSIGN(Benchmark);
};
+// Returns the rendezvous key associated with the given Send/Recv node.
+string GetRendezvousKey(const Node* node);
+
} // end namespace test
} // end namespace tensorflow