xref: /aosp_15_r20/external/cronet/base/sync_socket_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/sync_socket.h"
6 
7 #include <limits.h>
8 #include <stddef.h>
9 
10 #include <utility>
11 
12 #include "base/containers/span.h"
13 #include "base/logging.h"
14 #include "base/notimplemented.h"
15 #include "base/notreached.h"
16 #include "base/rand_util.h"
17 #include "base/threading/scoped_blocking_call.h"
18 #include "base/win/scoped_handle.h"
19 
20 namespace base {
21 
22 using win::ScopedHandle;
23 
24 namespace {
25 // IMPORTANT: do not change how this name is generated because it will break
26 // in sandboxed scenarios as we might have by-name policies that allow pipe
27 // creation. Also keep the secure random number generation.
28 const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
29 const size_t kPipePathMax = std::size(kPipeNameFormat) + (3 * 10) + 1;
30 
31 // To avoid users sending negative message lengths to Send/Receive
32 // we clamp message lengths, which are size_t, to no more than INT_MAX.
33 const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
34 
35 const int kOutBufferSize = 4096;
36 const int kInBufferSize = 4096;
37 const int kDefaultTimeoutMilliSeconds = 1000;
38 
CreatePairImpl(ScopedHandle * socket_a,ScopedHandle * socket_b,bool overlapped)39 bool CreatePairImpl(ScopedHandle* socket_a,
40                     ScopedHandle* socket_b,
41                     bool overlapped) {
42   DCHECK_NE(socket_a, socket_b);
43   DCHECK(!socket_a->is_valid());
44   DCHECK(!socket_b->is_valid());
45 
46   wchar_t name[kPipePathMax];
47   ScopedHandle handle_a;
48   DWORD flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE;
49   if (overlapped)
50     flags |= FILE_FLAG_OVERLAPPED;
51 
52   do {
53     unsigned long rnd_name;
54     RandBytes(&rnd_name, sizeof(rnd_name));
55 
56     swprintf(name, kPipePathMax,
57              kPipeNameFormat,
58              GetCurrentProcessId(),
59              GetCurrentThreadId(),
60              rnd_name);
61 
62     handle_a.Set(CreateNamedPipeW(
63         name,
64         flags,
65         PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
66         1,
67         kOutBufferSize,
68         kInBufferSize,
69         kDefaultTimeoutMilliSeconds,
70         NULL));
71   } while (!handle_a.is_valid() && (GetLastError() == ERROR_PIPE_BUSY));
72 
73   if (!handle_a.is_valid()) {
74     NOTREACHED();
75     return false;
76   }
77 
78   // The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot
79   // impersonate the client (handle_b). This allows us not to care which side
80   // ends up in which side of a privilege boundary.
81   flags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
82   if (overlapped)
83     flags |= FILE_FLAG_OVERLAPPED;
84 
85   ScopedHandle handle_b(CreateFileW(name,
86                                     GENERIC_READ | GENERIC_WRITE,
87                                     0,          // no sharing.
88                                     NULL,       // default security attributes.
89                                     OPEN_EXISTING,  // opens existing pipe.
90                                     flags,
91                                     NULL));     // no template file.
92   if (!handle_b.is_valid()) {
93     DPLOG(ERROR) << "CreateFileW failed";
94     return false;
95   }
96 
97   if (!ConnectNamedPipe(handle_a.get(), NULL)) {
98     DWORD error = GetLastError();
99     if (error != ERROR_PIPE_CONNECTED) {
100       DPLOG(ERROR) << "ConnectNamedPipe failed";
101       return false;
102     }
103   }
104 
105   *socket_a = std::move(handle_a);
106   *socket_b = std::move(handle_b);
107 
108   return true;
109 }
110 
111 // Inline helper to avoid having the cast everywhere.
GetNextChunkSize(size_t current_pos,size_t max_size)112 DWORD GetNextChunkSize(size_t current_pos, size_t max_size) {
113   // The following statement is for 64 bit portability.
114   return static_cast<DWORD>(((max_size - current_pos) <= UINT_MAX) ?
115       (max_size - current_pos) : UINT_MAX);
116 }
117 
118 // Template function that supports calling ReadFile or WriteFile in an
119 // overlapped fashion and waits for IO completion.  The function also waits
120 // on an event that can be used to cancel the operation.  If the operation
121 // is cancelled, the function returns and closes the relevant socket object.
122 template <typename DataType, typename Function>
CancelableFileOperation(Function operation,HANDLE file,span<DataType> buffer,WaitableEvent * io_event,WaitableEvent * cancel_event,CancelableSyncSocket * socket,DWORD timeout_in_ms)123 size_t CancelableFileOperation(Function operation,
124                                HANDLE file,
125                                span<DataType> buffer,
126                                WaitableEvent* io_event,
127                                WaitableEvent* cancel_event,
128                                CancelableSyncSocket* socket,
129                                DWORD timeout_in_ms) {
130   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
131   // The buffer must be byte size or the length check won't make much sense.
132   static_assert(sizeof(DataType) == 1u, "incorrect buffer type");
133   CHECK(!buffer.empty());
134   CHECK_LE(buffer.size(), kMaxMessageLength);
135   CHECK_NE(file, SyncSocket::kInvalidHandle);
136 
137   // Track the finish time so we can calculate the timeout as data is read.
138   TimeTicks current_time, finish_time;
139   if (timeout_in_ms != INFINITE) {
140     current_time = TimeTicks::Now();
141     finish_time = current_time + base::Milliseconds(timeout_in_ms);
142   }
143 
144   size_t count = 0;
145   do {
146     // The OVERLAPPED structure will be modified by ReadFile or WriteFile.
147     OVERLAPPED ol = { 0 };
148     ol.hEvent = io_event->handle();
149 
150     const DWORD chunk_size = GetNextChunkSize(count, buffer.size());
151     // This is either the ReadFile or WriteFile call depending on whether
152     // we're receiving or sending data.
153     DWORD len = 0;
154     auto operation_buffer = buffer.subspan(count, chunk_size);
155     // SAFETY: The below static_cast is in range for DWORD because
156     // `operation_buffer` is constructed with a DWORD length above from
157     // `chunk_size`.
158     const BOOL operation_ok =
159         operation(file, operation_buffer.data(),
160                   static_cast<DWORD>(operation_buffer.size()), &len, &ol);
161     if (!operation_ok) {
162       if (::GetLastError() == ERROR_IO_PENDING) {
163         HANDLE events[] = { io_event->handle(), cancel_event->handle() };
164         const DWORD wait_result = WaitForMultipleObjects(
165             std::size(events), events, FALSE,
166             timeout_in_ms == INFINITE
167                 ? timeout_in_ms
168                 : static_cast<DWORD>(
169                       (finish_time - current_time).InMilliseconds()));
170         if (wait_result != WAIT_OBJECT_0 + 0) {
171           // CancelIo() doesn't synchronously cancel outstanding IO, only marks
172           // outstanding IO for cancellation. We must call GetOverlappedResult()
173           // below to ensure in flight writes complete before returning.
174           CancelIo(file);
175         }
176 
177         // We set the |bWait| parameter to TRUE for GetOverlappedResult() to
178         // ensure writes are complete before returning.
179         if (!GetOverlappedResult(file, &ol, &len, TRUE))
180           len = 0;
181 
182         if (wait_result == WAIT_OBJECT_0 + 1) {
183           DVLOG(1) << "Shutdown was signaled. Closing socket.";
184           socket->Close();
185           return count;
186         }
187 
188         // Timeouts will be handled by the while() condition below since
189         // GetOverlappedResult() may complete successfully after CancelIo().
190         DCHECK(wait_result == WAIT_OBJECT_0 + 0 || wait_result == WAIT_TIMEOUT);
191       } else {
192         break;
193       }
194     }
195 
196     count += len;
197 
198     // Quit the operation if we can't write/read anymore.
199     if (len != chunk_size) {
200       break;
201     }
202 
203     // Since TimeTicks::Now() is expensive, only bother updating the time if we
204     // have more work to do.
205     if (timeout_in_ms != INFINITE && count < buffer.size()) {
206       current_time = base::TimeTicks::Now();
207     }
208   } while (count < buffer.size() &&
209            (timeout_in_ms == INFINITE || current_time < finish_time));
210 
211   return count;
212 }
213 
214 }  // namespace
215 
216 // static
CreatePair(SyncSocket * socket_a,SyncSocket * socket_b)217 bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
218   return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, false);
219 }
220 
Close()221 void SyncSocket::Close() {
222   handle_.Close();
223 }
224 
Send(span<const uint8_t> data)225 size_t SyncSocket::Send(span<const uint8_t> data) {
226   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
227   CHECK_LE(data.size(), kMaxMessageLength);
228   DCHECK(IsValid());
229   size_t count = 0;
230   while (count < data.size()) {
231     DWORD len;
232     const DWORD chunk_size = GetNextChunkSize(count, data.size());
233     auto data_chunk = data.subspan(count, chunk_size);
234     // SAFETY: The below static_cast is in range for DWORD because `data_chunk`
235     // is constructed with a DWORD length above from `chunk_size`.
236     if (::WriteFile(handle(), data_chunk.data(),
237                     static_cast<DWORD>(data_chunk.size()), &len,
238                     NULL) == FALSE) {
239       return count;
240     }
241     count += len;
242   }
243   return count;
244 }
245 
Send(const void * buffer,size_t length)246 size_t SyncSocket::Send(const void* buffer, size_t length) {
247   return Send(make_span(static_cast<const uint8_t*>(buffer), length));
248 }
249 
ReceiveWithTimeout(span<uint8_t> buffer,TimeDelta timeout)250 size_t SyncSocket::ReceiveWithTimeout(span<uint8_t> buffer, TimeDelta timeout) {
251   NOTIMPLEMENTED();
252   return 0;
253 }
254 
ReceiveWithTimeout(void * buffer,size_t length,TimeDelta timeout)255 size_t SyncSocket::ReceiveWithTimeout(void* buffer,
256                                       size_t length,
257                                       TimeDelta timeout) {
258   NOTIMPLEMENTED();
259   return 0;
260 }
261 
Receive(span<uint8_t> buffer)262 size_t SyncSocket::Receive(span<uint8_t> buffer) {
263   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
264   CHECK_LE(buffer.size(), kMaxMessageLength);
265   DCHECK(IsValid());
266   size_t count = 0;
267   while (count < buffer.size()) {
268     DWORD len;
269     const DWORD chunk_size = GetNextChunkSize(count, buffer.size());
270     auto data_chunk = buffer.subspan(count, chunk_size);
271     // SAFETY: The below static_cast is in range for DWORD because `data_chunk`
272     // is constructed with a DWORD length above from `chunk_size`.
273     if (::ReadFile(handle(), data_chunk.data(),
274                    static_cast<DWORD>(data_chunk.size()), &len,
275                    NULL) == FALSE) {
276       return count;
277     }
278     count += len;
279   }
280   return count;
281 }
282 
Receive(void * buffer,size_t length)283 size_t SyncSocket::Receive(void* buffer, size_t length) {
284   return Receive(make_span(static_cast<uint8_t*>(buffer), length));
285 }
286 
Peek()287 size_t SyncSocket::Peek() {
288   DWORD available = 0;
289   PeekNamedPipe(handle(), NULL, 0, NULL, &available, NULL);
290   return available;
291 }
292 
IsValid() const293 bool SyncSocket::IsValid() const {
294   return handle_.is_valid();
295 }
296 
handle() const297 SyncSocket::Handle SyncSocket::handle() const {
298   return handle_.get();
299 }
300 
Release()301 SyncSocket::Handle SyncSocket::Release() {
302   return handle_.release();
303 }
304 
Shutdown()305 bool CancelableSyncSocket::Shutdown() {
306   // This doesn't shut down the pipe immediately, but subsequent Receive or Send
307   // methods will fail straight away.
308   shutdown_event_.Signal();
309   return true;
310 }
311 
Close()312 void CancelableSyncSocket::Close() {
313   SyncSocket::Close();
314   shutdown_event_.Reset();
315 }
316 
Send(span<const uint8_t> data)317 size_t CancelableSyncSocket::Send(span<const uint8_t> data) {
318   static const DWORD kWaitTimeOutInMs = 500;
319   return CancelableFileOperation(&::WriteFile, handle(), data, &file_operation_,
320                                  &shutdown_event_, this, kWaitTimeOutInMs);
321 }
322 
Send(const void * buffer,size_t length)323 size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
324   return Send(make_span(static_cast<const uint8_t*>(buffer), length));
325 }
326 
Receive(span<uint8_t> buffer)327 size_t CancelableSyncSocket::Receive(span<uint8_t> buffer) {
328   return CancelableFileOperation(&::ReadFile, handle(), buffer,
329                                  &file_operation_, &shutdown_event_, this,
330                                  INFINITE);
331 }
332 
Receive(void * buffer,size_t length)333 size_t CancelableSyncSocket::Receive(void* buffer, size_t length) {
334   return Receive(make_span(static_cast<uint8_t*>(buffer), length));
335 }
336 
ReceiveWithTimeout(span<uint8_t> buffer,TimeDelta timeout)337 size_t CancelableSyncSocket::ReceiveWithTimeout(span<uint8_t> buffer,
338                                                 TimeDelta timeout) {
339   return CancelableFileOperation(&::ReadFile, handle(), buffer,
340                                  &file_operation_, &shutdown_event_, this,
341                                  static_cast<DWORD>(timeout.InMilliseconds()));
342 }
343 
ReceiveWithTimeout(void * buffer,size_t length,TimeDelta timeout)344 size_t CancelableSyncSocket::ReceiveWithTimeout(void* buffer,
345                                                 size_t length,
346                                                 TimeDelta timeout) {
347   return ReceiveWithTimeout(make_span(static_cast<uint8_t*>(buffer), length),
348                             std::move(timeout));
349 }
350 
351 // static
CreatePair(CancelableSyncSocket * socket_a,CancelableSyncSocket * socket_b)352 bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
353                                       CancelableSyncSocket* socket_b) {
354   return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true);
355 }
356 
357 }  // namespace base
358