1 // Copyright (c) 2019 Klemens D. Morgenstern
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
7 #define BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
8 
9 #include <vector>
10 #include <system_error>
11 #include <boost/process/detail/windows/handle_workaround.hpp>
12 #include <boost/process/detail/windows/handler.hpp>
13 #include <boost/winapi/get_current_process_id.hpp>
14 
15 namespace boost { namespace process { namespace detail {
16 
17 
18 template<typename Executor, typename Function>
19 void foreach_used_handle(Executor &exec, Function &&func);
20 
21 
22 namespace windows {
23 
24 
25 using native_handle_type = ::boost::winapi::HANDLE_ ;
26 
get_handles(std::error_code & ec)27 inline std::vector<native_handle_type> get_handles(std::error_code & ec)
28 {
29     auto pid = ::boost::winapi::GetCurrentProcessId();
30 
31     std::vector<char> buffer(2048);
32     constexpr static auto STATUS_INFO_LENGTH_MISMATCH_ = static_cast<::boost::winapi::NTSTATUS_>(0xC0000004l);
33     auto info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
34 
35     ::boost::winapi::NTSTATUS_ nt_status = STATUS_INFO_LENGTH_MISMATCH_;
36 
37     for (int cnt = 0;
38            nt_status == STATUS_INFO_LENGTH_MISMATCH_;
39            nt_status = workaround::nt_system_query_information(
40                             workaround::SystemHandleInformation_,
41                             info_pointer, buffer.size(),
42                             NULL))
43     {
44         buffer.resize(buffer.size() * 2);
45         info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
46     }
47 
48 
49     if (nt_status < 0 || nt_status > 0x7FFFFFFF)
50     {
51         ec = ::boost::process::detail::get_last_error();
52         return {};
53     }
54     else
55         ec.clear();
56 
57     std::vector<native_handle_type> res;
58     for (auto itr = info_pointer->Handle; itr != (info_pointer->Handle + info_pointer->Count); itr++)
59     {
60         if (itr->OwnerPid == pid)
61             res.push_back(reinterpret_cast<native_handle_type>(static_cast<std::uintptr_t>(itr->HandleValue)));
62     }
63 
64     return res;
65 }
66 
get_handles()67 inline std::vector<native_handle_type> get_handles()
68 {
69     std::error_code ec;
70 
71     auto res = get_handles(ec);
72     if (ec)
73         boost::process::detail::throw_error(ec, "NtQuerySystemInformation failed");
74 
75     return res;
76 }
77 
78 
is_stream_handle(native_handle_type handle,std::error_code & ec)79 inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
80 {
81     ::boost::winapi::ULONG_ actual_size;
82     auto nt_status = workaround::nt_query_object(
83             handle,
84             workaround::ObjectTypeInformation,
85             NULL,
86             0, &actual_size);
87 
88     std::vector<char> vec;
89     vec.resize(actual_size);
90 
91     workaround::OBJECT_TYPE_INFORMATION_ * type_info_p = reinterpret_cast<workaround::OBJECT_TYPE_INFORMATION_*>(vec.data());
92     nt_status = workaround::nt_query_object(
93             handle,
94             workaround::ObjectTypeInformation,
95             type_info_p,
96             actual_size, &actual_size);
97 
98     if (nt_status < 0 || nt_status > 0x7FFFFFFF)
99     {
100         ec = ::boost::process::detail::get_last_error();
101         return false;
102     }
103     else
104         ec.clear();
105 
106     auto &nm = type_info_p->TypeName.Buffer;
107     return type_info_p->TypeName.Length >= 5 &&
108             nm[0] == L'F' &&
109             nm[1] == L'i' &&
110             nm[2] == L'l' &&
111             nm[3] == L'e' &&
112             nm[4] == L'\0';
113 }
114 
115 
is_stream_handle(native_handle_type handle)116 inline bool is_stream_handle(native_handle_type handle)
117 {
118     std::error_code ec;
119     auto res = is_stream_handle(handle, ec);
120     if (ec)
121         boost::process::detail::throw_error(ec, "NtQueryObject failed");
122 
123     return res;
124 }
125 
126 
127 struct limit_handles_ : handler_base_ext
128 {
129     mutable std::vector<::boost::winapi::HANDLE_> handles_with_inherit_flag;
130 
131     template<typename Executor>
on_setupboost::process::detail::windows::limit_handles_132     void on_setup(Executor & exec) const
133     {
134         auto all_handles = get_handles();
135         foreach_used_handle(exec,
136                 [&](::boost::winapi::HANDLE_ handle)
137                 {
138                     auto itr = std::find(all_handles.begin(), all_handles .end(), handle);
139                     DWORD flags = 0u;
140                     if (itr != all_handles.end())
141                         *itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
142                     else if ((::boost::winapi::GetHandleInformation(*itr, &flags) != 0)
143                             &&((flags & ::boost::winapi::HANDLE_FLAG_INHERIT_) == 0)) //it is NOT inherited anyhow, so ignore too
144                         *itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
145                 });
146 
147         auto part_itr = std::partition(all_handles.begin(), all_handles.end(),
148                                        [](::boost::winapi::HANDLE_ handle) {return handle != ::boost::winapi::INVALID_HANDLE_VALUE_;});
149 
150         all_handles.erase(part_itr, all_handles.end()); //remove invalid handles
151         handles_with_inherit_flag = std::move(all_handles);
152 
153         for (auto handle : handles_with_inherit_flag)
154             ::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, 0);
155     }
156 
157     template<typename Executor>
on_errorboost::process::detail::windows::limit_handles_158     void on_error(Executor & exec, const std::error_code & ec) const
159     {
160         for (auto handle : handles_with_inherit_flag)
161             ::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
162     }
163 
164     template<typename Executor>
on_sucessboost::process::detail::windows::limit_handles_165     void on_sucess(Executor & exec) const
166     {
167         for (auto handle : handles_with_inherit_flag)
168             ::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
169     }
170 
171 };
172 
173 
174 }}}}
175 
176 #endif //PROCESS_HANDLES_HPP
177