|  | // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <fuchsia/posix/socket/llcpp/fidl.h> | 
|  | #include <lib/async-loop/cpp/loop.h> | 
|  | #include <lib/async-loop/default.h> | 
|  | #include <lib/fdio/spawn.h> | 
|  | #include <lib/fidl-async/cpp/bind.h> | 
|  | #include <lib/sync/completion.h> | 
|  | #include <lib/zx/process.h> | 
|  | #include <zircon/processargs.h> | 
|  |  | 
|  | #include <zxtest/zxtest.h> | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class Server final : public llcpp::fuchsia::posix::socket::StreamSocket::Interface { | 
|  | public: | 
|  | Server(zx_handle_t channel, zx::socket peer) : channel_(channel), peer_(std::move(peer)) {} | 
|  |  | 
|  | void Clone(uint32_t flags, ::zx::channel object, CloneCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Close(CloseCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Describe(DescribeCompleter::Sync& completer) override { | 
|  | llcpp::fuchsia::io::StreamSocket stream_socket; | 
|  | zx_status_t status = | 
|  | peer_.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_WRITE, &stream_socket.socket); | 
|  | if (status != ZX_OK) { | 
|  | return completer.Close(status); | 
|  | } | 
|  | llcpp::fuchsia::io::NodeInfo info; | 
|  | info.set_stream_socket(fidl::unowned_ptr(&stream_socket)); | 
|  | completer.Reply(std::move(info)); | 
|  | } | 
|  |  | 
|  | void Sync(SyncCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void GetAttr(GetAttrCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void SetAttr(uint32_t flags, ::llcpp::fuchsia::io::NodeAttributes attributes, | 
|  | SetAttrCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Bind(::llcpp::fuchsia::net::SocketAddress addr, BindCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Bind2(::llcpp::fuchsia::net::SocketAddress addr, Bind2Completer::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Connect(::llcpp::fuchsia::net::SocketAddress addr, | 
|  | ConnectCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Connect2(::llcpp::fuchsia::net::SocketAddress addr, | 
|  | Connect2Completer::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Listen(int16_t backlog, ListenCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Accept(bool want_addr, AcceptCompleter::Sync& completer) override { | 
|  | zx_status_t status = zx_object_signal_peer(channel_, 0, ZX_USER_SIGNAL_0); | 
|  | if (status != ZX_OK) { | 
|  | return completer.Close(status); | 
|  | } | 
|  | return completer.Close(sync_completion_wait(&accept_end_, ZX_TIME_INFINITE)); | 
|  | } | 
|  |  | 
|  | void Accept2(Accept2Completer::Sync& completer) override { | 
|  | zx_status_t status = zx_object_signal_peer(channel_, 0, ZX_USER_SIGNAL_0); | 
|  | if (status != ZX_OK) { | 
|  | return completer.Close(status); | 
|  | } | 
|  | return completer.Close(sync_completion_wait(&accept_end_, ZX_TIME_INFINITE)); | 
|  | } | 
|  |  | 
|  | void GetSockName(GetSockNameCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void GetSockName2(GetSockName2Completer::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void GetPeerName(GetPeerNameCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void GetPeerName2(GetPeerName2Completer::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void Disconnect(DisconnectCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_CONNECTED); | 
|  | } | 
|  |  | 
|  | void SetSockOpt(int16_t level, int16_t optname, fidl::VectorView<uint8_t> optval, | 
|  | SetSockOptCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | void GetSockOpt(int16_t level, int16_t optname, GetSockOptCompleter::Sync& completer) override { | 
|  | return completer.Close(ZX_ERR_NOT_SUPPORTED); | 
|  | } | 
|  |  | 
|  | sync_completion_t accept_end_; | 
|  |  | 
|  | private: | 
|  | zx_handle_t channel_; | 
|  | zx::socket peer_; | 
|  | }; | 
|  |  | 
|  | TEST(AtExit, ExitInAccept) { | 
|  | zx::channel client_channel, server_channel; | 
|  | ASSERT_OK(zx::channel::create(0, &client_channel, &server_channel)); | 
|  |  | 
|  | zx::socket client_socket, server_socket; | 
|  | ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &client_socket, &server_socket)); | 
|  |  | 
|  | // We're going to need the raw handle so we can signal on it and close it. | 
|  | zx_handle_t server_handle = server_channel.get(); | 
|  |  | 
|  | Server server(server_handle, std::move(server_socket)); | 
|  | async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); | 
|  | ASSERT_OK(fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(server_channel), &server)); | 
|  | ASSERT_OK(loop.StartThread("fake-socket-server")); | 
|  |  | 
|  | const char* argv[] = {"/pkg/bin/accept-child", nullptr}; | 
|  | const fdio_spawn_action_t actions[] = { | 
|  | { | 
|  | .action = FDIO_SPAWN_ACTION_ADD_HANDLE, | 
|  | .h = | 
|  | { | 
|  | .id = PA_HND(PA_USER0, 0), | 
|  | .handle = client_channel.release(), | 
|  | }, | 
|  | }, | 
|  | }; | 
|  | zx::process process; | 
|  | char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; | 
|  | ASSERT_OK(fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv, nullptr, | 
|  | sizeof(actions) / sizeof(fdio_spawn_action_t), actions, | 
|  | process.reset_and_get_address(), err_msg), | 
|  | "%s", err_msg); | 
|  |  | 
|  | // Wait until the child has let us know that it is exiting. | 
|  | ASSERT_OK(zx_object_wait_one(server_handle, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr)); | 
|  | // Close the channel to unblock the child's Close call. | 
|  | ASSERT_OK(zx_handle_close(server_handle)); | 
|  |  | 
|  | // Verify that the child didn't crash. | 
|  | ASSERT_OK(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr)); | 
|  | sync_completion_signal(&server.accept_end_); | 
|  | zx_info_process_t proc_info; | 
|  | ASSERT_OK(process.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr)); | 
|  | ASSERT_EQ(proc_info.return_code, 0); | 
|  | } | 
|  |  | 
|  | }  // namespace |