Coverage Report

Created: 2026-09-14 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/handle/handle.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Handle Management
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/config.h>
22
23
#include <winpr/handle.h>
24
#include <winpr/assert.h>
25
26
#include "../log.h"
27
#define TAG WINPR_TAG("handle")
28
29
#ifndef _WIN32
30
31
#include <errno.h>
32
#include <fcntl.h>
33
#include <pthread.h>
34
#include <string.h>
35
36
#include "../synch/synch.h"
37
#include "../thread/thread.h"
38
#include "../pipe/pipe.h"
39
#include "../comm/comm.h"
40
#include "../security/security.h"
41
42
#ifdef WINPR_HAVE_UNISTD_H
43
#include <unistd.h>
44
#endif
45
46
#include "../handle/handle.h"
47
48
#endif
49
50
/* finds the "{}" placeholder in `format`, splitting it into the literal text before and after it.
51
 * Returns FALSE (and leaves *prefixLen and *suffix untouched) if `format` has none. */
52
WINPR_ATTR_NODISCARD
53
static BOOL winpr_handle_format_split(const char* format, size_t* prefixLen, const char** suffix)
54
0
{
55
0
  const char* placeholder = strstr(format, "{}");
56
0
  if (!placeholder)
57
0
  {
58
0
    WLog_ERR(TAG, "format string '%s' has no {} placeholder for the handle value", format);
59
0
    return FALSE;
60
0
  }
61
62
0
  *prefixLen = (size_t)(placeholder - format);
63
0
  *suffix = placeholder + 2;
64
0
  return TRUE;
65
0
}
66
67
#ifndef _WIN32
68
/* single-character tag identifying a handle's type in the exported value (see
69
 * winpr_exportHandleToString()/winpr_importHandleFromString()), so a POSIX-side import knows how
70
 * to rebuild the right kind of HANDLE around the fd. Only pipes (as returned by CreatePipe()) are
71
 * supported for now; add a case here (and a matching one in winpr_importHandleFromString()) when
72
 * another handle type needs to cross a fork()+exec(). */
73
WINPR_ATTR_NODISCARD
74
static char winpr_handle_export_type_tag(ULONG type)
75
0
{
76
0
  switch (type)
77
0
  {
78
0
    case HANDLE_TYPE_ANONYMOUS_PIPE:
79
0
      return 'P';
80
0
    default:
81
0
      return '\0';
82
0
  }
83
0
}
84
#endif
85
86
BOOL winpr_exportHandleToString(HANDLE h, const char* format, char* outStr, size_t outSz)
87
0
{
88
0
  if (!format || !outStr || (outSz == 0))
89
0
    return FALSE;
90
91
0
  size_t prefixLen = 0;
92
0
  const char* suffix = nullptr;
93
0
  if (!winpr_handle_format_split(format, &prefixLen, &suffix))
94
0
    return FALSE;
95
96
0
  if (!h || (h == INVALID_HANDLE_VALUE))
97
0
  {
98
0
    WLog_ERR(TAG, "refusing to export an invalid handle");
99
0
    return FALSE;
100
0
  }
101
0
  size_t remaining = outSz - 1;
102
0
  if (remaining <= prefixLen)
103
0
    return FALSE;
104
0
  remaining -= prefixLen;
105
106
0
  size_t suffixLen = strlen(suffix);
107
0
  if (remaining <= suffixLen)
108
0
    return FALSE;
109
0
  remaining -= suffixLen;
110
111
0
  char value[32] = WINPR_C_ARRAY_INIT;
112
#ifdef _WIN32
113
  const int valueLenInt = snprintf(value, sizeof(value), "%llx", (unsigned long long)(UINT_PTR)h);
114
#else
115
0
  ULONG type = 0;
116
0
  WINPR_HANDLE* hdl = nullptr;
117
0
  if (!winpr_Handle_GetInfo(h, &type, &hdl))
118
0
  {
119
0
    WLog_ERR(TAG, "unable to resolve handle info");
120
0
    return FALSE;
121
0
  }
122
123
0
  const char typeTag = winpr_handle_export_type_tag(type);
124
0
  if (typeTag == '\0')
125
0
  {
126
0
    WLog_ERR(TAG,
127
0
             "exporting handle type %" PRIu32 " is not supported, only pipe handles can be "
128
0
             "exported for now",
129
0
             type);
130
0
    return FALSE;
131
0
  }
132
133
0
  const int fd = winpr_Handle_getFd(h);
134
0
  if (fd < 0)
135
0
  {
136
0
    WLog_ERR(TAG, "unable to resolve a file descriptor for this handle");
137
0
    return FALSE;
138
0
  }
139
0
  const int valueLenInt = snprintf(value, sizeof(value), "%c%x", typeTag, (unsigned)fd);
140
0
#endif
141
0
  if (valueLenInt <= 0)
142
0
    return FALSE;
143
0
  const size_t valueLen = (size_t)valueLenInt;
144
145
0
  if (valueLen > remaining)
146
0
    return FALSE;
147
148
0
  memcpy(outStr, format, prefixLen);
149
0
  memcpy(outStr + prefixLen, value, valueLen);
150
0
  memcpy(outStr + prefixLen + valueLen, suffix, suffixLen + 1);
151
0
  return TRUE;
152
0
}
153
154
HANDLE winpr_importHandleFromString(const char* str, const char* format)
155
0
{
156
0
  if (!str || !format)
157
0
    return INVALID_HANDLE_VALUE;
158
159
0
  size_t prefixLen = 0;
160
0
  const char* suffix = nullptr;
161
0
  if (!winpr_handle_format_split(format, &prefixLen, &suffix))
162
0
    return INVALID_HANDLE_VALUE;
163
164
0
  const size_t suffixLen = strlen(suffix);
165
0
  const size_t strLen = strlen(str);
166
0
  if ((strLen < prefixLen + suffixLen) || (strncmp(str, format, prefixLen) != 0) ||
167
0
      (strcmp(str + strLen - suffixLen, suffix) != 0))
168
0
  {
169
0
    WLog_ERR(TAG, "input string '%s' does not match format '%s'", str, format);
170
0
    return INVALID_HANDLE_VALUE;
171
0
  }
172
173
0
  char value[32] = WINPR_C_ARRAY_INIT;
174
0
  size_t valueLen = strLen - prefixLen - suffixLen;
175
0
  const char* valueStart = str + prefixLen;
176
0
  if ((valueLen == 0) || (valueLen >= sizeof(value)))
177
0
  {
178
0
    WLog_ERR(TAG, "input string '%s' has no usable handle value", str);
179
0
    return INVALID_HANDLE_VALUE;
180
0
  }
181
182
0
#ifndef _WIN32
183
  /* first character is the type tag written by winpr_exportHandleToString() (see
184
   * winpr_handle_export_type_tag()) - only pipes are supported for now. */
185
0
  const char typeTag = valueStart[0];
186
0
  if (typeTag != 'P')
187
0
  {
188
0
    WLog_ERR(TAG,
189
0
             "unsupported handle type tag '%c' in '%s', only pipe handles ('P') can be "
190
0
             "imported for now",
191
0
             typeTag, str);
192
0
    return INVALID_HANDLE_VALUE;
193
0
  }
194
0
  valueStart++;
195
0
  valueLen--;
196
0
  if (valueLen == 0)
197
0
  {
198
0
    WLog_ERR(TAG, "input string '%s' has no usable handle value after the type tag", str);
199
0
    return INVALID_HANDLE_VALUE;
200
0
  }
201
0
#endif
202
203
0
  memcpy(value, valueStart, valueLen);
204
0
  value[valueLen] = '\0';
205
206
0
  char* end = nullptr;
207
0
  const unsigned long long numeric = strtoull(value, &end, 16);
208
0
  if (!end || (*end != '\0'))
209
0
  {
210
0
    WLog_ERR(TAG, "invalid handle value '%s'", value);
211
0
    return INVALID_HANDLE_VALUE;
212
0
  }
213
214
#ifdef _WIN32
215
  return (HANDLE)(UINT_PTR)numeric;
216
#else
217
0
  if (numeric > INT32_MAX)
218
0
  {
219
0
    WLog_ERR(TAG, "handle value '%s' out of range for a file descriptor", value);
220
0
    return INVALID_HANDLE_VALUE;
221
0
  }
222
0
  return winpr_Pipe_FromFd((int)numeric);
223
0
#endif
224
0
}
225
226
#ifndef _WIN32
227
228
BOOL CloseHandle(HANDLE hObject)
229
0
{
230
0
  ULONG type = 0;
231
0
  WINPR_HANDLE* hdl = nullptr;
232
233
0
  if (!winpr_Handle_GetInfo(hObject, &type, &hdl) || !hdl || !hdl->ops)
234
0
    return FALSE;
235
236
0
  BOOL ok = TRUE;
237
0
  if (hdl->ops->CloseHandle)
238
0
    ok = hdl->ops->CloseHandle(hObject);
239
240
  /* a type's CloseHandle op can legitimately refuse (e.g. file.c won't close the pStdHandleFile
241
   * singleton unless forced) - if it did, this handle wasn't actually closed, so leave its
242
   * refcount/memory alone entirely. */
243
0
  if (!ok)
244
0
    return FALSE;
245
246
  /* WINPR_THREAD manages its own close/free lifecycle: closing the handle of a still-running
247
   * thread detaches it instead of freeing it, and the free only happens later, from the
248
   * thread's own pthread routine once it actually returns (see ThreadCloseHandle() /
249
   * cleanup_handle() in thread.c). Applying the generic release-then-maybe-free sequence below
250
   * on top of that would free the struct while the thread may still be running against it. */
251
0
  if (type == HANDLE_TYPE_THREAD)
252
0
    return TRUE;
253
254
0
  if (!winpr_Handle_Release(hObject))
255
0
    winpr_Handle_ConvertToNone(hObject);
256
257
0
  return TRUE;
258
0
}
259
260
BOOL DuplicateHandle(WINPR_ATTR_UNUSED HANDLE hSourceProcessHandle,
261
                     WINPR_ATTR_UNUSED HANDLE hSourceHandle,
262
                     WINPR_ATTR_UNUSED HANDLE hTargetProcessHandle,
263
                     WINPR_ATTR_UNUSED LPHANDLE lpTargetHandle,
264
                     WINPR_ATTR_UNUSED DWORD dwDesiredAccess, WINPR_ATTR_UNUSED BOOL bInheritHandle,
265
                     WINPR_ATTR_UNUSED DWORD dwOptions)
266
0
{
267
0
  WLog_ERR(TAG, "DuplicateHandle not implemented");
268
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
269
0
  return FALSE;
270
0
}
271
272
BOOL GetHandleInformation(HANDLE hObject, LPDWORD lpdwFlags)
273
0
{
274
0
  if (!lpdwFlags)
275
0
    return FALSE;
276
277
0
  const int fd = winpr_Handle_getFd(hObject);
278
0
  if (fd < 0)
279
0
  {
280
0
    WLog_ERR(TAG, "unable to resolve a file descriptor for this handle type");
281
0
    return FALSE;
282
0
  }
283
284
0
  const int flags = fcntl(fd, F_GETFD);
285
0
  if (flags < 0)
286
0
  {
287
0
    char buffer[64] = WINPR_C_ARRAY_INIT;
288
0
    WLog_ERR(TAG, "fcntl(F_GETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
289
0
    return FALSE;
290
0
  }
291
292
0
  *lpdwFlags = (flags & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT;
293
0
  return TRUE;
294
0
}
295
296
BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags)
297
0
{
298
0
  const int fd = winpr_Handle_getFd(hObject);
299
0
  if (fd < 0)
300
0
  {
301
0
    WLog_ERR(TAG, "unable to resolve a file descriptor for this handle type");
302
0
    return FALSE;
303
0
  }
304
305
  /* only HANDLE_FLAG_INHERIT is meaningful on POSIX; ignore any other bit in the mask rather
306
   * than failing, matching how Windows treats masks it doesn't recognize on other handle
307
   * types */
308
0
  if (!(dwMask & HANDLE_FLAG_INHERIT))
309
0
    return TRUE;
310
311
0
  int flags = fcntl(fd, F_GETFD);
312
0
  if (flags < 0)
313
0
  {
314
0
    char buffer[64] = WINPR_C_ARRAY_INIT;
315
0
    WLog_ERR(TAG, "fcntl(F_GETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
316
0
    return FALSE;
317
0
  }
318
319
0
  flags = (dwFlags & HANDLE_FLAG_INHERIT) ? (flags & ~FD_CLOEXEC) : (flags | FD_CLOEXEC);
320
0
  if (fcntl(fd, F_SETFD, flags) < 0)
321
0
  {
322
0
    char buffer[64] = WINPR_C_ARRAY_INIT;
323
0
    WLog_ERR(TAG, "fcntl(F_SETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
324
0
    return FALSE;
325
0
  }
326
327
0
  return TRUE;
328
0
}
329
330
BOOL winpr_set_cloexec(int fd, BOOL cloexec)
331
0
{
332
0
  const int flags = fcntl(fd, F_GETFD);
333
0
  if (flags < 0)
334
0
    return FALSE;
335
336
0
  const int newFlags = cloexec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
337
  return fcntl(fd, F_SETFD, newFlags) >= 0;
338
0
}
339
340
#endif