Coverage Report

Created: 2026-08-31 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/cache/pointer.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Glyph Cache
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <stdio.h>
23
24
#include <winpr/crt.h>
25
#include <winpr/assert.h>
26
#include <winpr/stream.h>
27
28
#include <freerdp/log.h>
29
30
#include "pointer.h"
31
#include "cache.h"
32
33
#define TAG FREERDP_TAG("cache.pointer")
34
35
WINPR_ATTR_NODISCARD
36
static const char* pointerType(bool colorCache)
37
0
{
38
39
0
  return colorCache ? "ColorPointerCache" : "PointerCache";
40
0
}
41
42
WINPR_ATTR_NODISCARD
43
static BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
44
                              BOOL colorCache);
45
46
WINPR_ATTR_NODISCARD
47
static rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index);
48
49
static void pointer_clear(rdpPointer* pointer)
50
0
{
51
0
  if (pointer)
52
0
  {
53
0
    pointer->lengthAndMask = 0;
54
0
    free(pointer->andMaskData);
55
0
    pointer->andMaskData = nullptr;
56
57
0
    pointer->lengthXorMask = 0;
58
0
    free(pointer->xorMaskData);
59
0
    pointer->xorMaskData = nullptr;
60
0
  }
61
0
}
62
63
static void pointer_free(rdpContext* context, rdpPointer* pointer)
64
0
{
65
0
  if (pointer)
66
0
  {
67
0
    IFCALL(pointer->Free, context, pointer);
68
0
    pointer_clear(pointer);
69
0
  }
70
0
  free(pointer);
71
0
}
72
73
WINPR_ATTR_NODISCARD
74
static BOOL update_pointer_position(rdpContext* context,
75
                                    const POINTER_POSITION_UPDATE* pointer_position)
76
0
{
77
0
  if (!context || !context->graphics || !context->graphics->Pointer_Prototype ||
78
0
      !pointer_position)
79
0
    return FALSE;
80
81
0
  const BOOL GrabMouse = freerdp_settings_get_bool(context->settings, FreeRDP_GrabMouse);
82
0
  if (!GrabMouse)
83
0
    return TRUE;
84
85
0
  const rdpPointer* pointer = context->graphics->Pointer_Prototype;
86
0
  WINPR_ASSERT(pointer);
87
88
0
  return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos,
89
0
                      pointer_position->yPos);
90
0
}
91
92
WINPR_ATTR_NODISCARD
93
static BOOL update_pointer_system(rdpContext* context, const POINTER_SYSTEM_UPDATE* pointer_system)
94
0
{
95
0
  if (!context || !context->graphics || !context->graphics->Pointer_Prototype || !pointer_system)
96
0
    return FALSE;
97
98
0
  rdpPointer* pointer = context->graphics->Pointer_Prototype;
99
100
0
  switch (pointer_system->type)
101
0
  {
102
0
    case SYSPTR_NULL:
103
0
      return IFCALLRESULT(TRUE, pointer->SetNull, context);
104
105
0
    case SYSPTR_DEFAULT:
106
0
      return IFCALLRESULT(TRUE, pointer->SetDefault, context);
107
108
0
    default:
109
0
      WLog_ERR(TAG, "Unknown system pointer type (0x%08" PRIX32 ")", pointer_system->type);
110
0
  }
111
0
  return TRUE;
112
0
}
113
114
WINPR_ATTR_NODISCARD
115
static BOOL update_pointer_copy_andxor(rdpPointer* pointer, const BYTE* andMaskData,
116
                                       size_t lengthAndMask, const BYTE* xorMaskData,
117
                                       size_t lengthXorMask)
118
0
{
119
0
  WINPR_ASSERT(pointer);
120
121
0
  pointer_clear(pointer);
122
0
  if (lengthAndMask && andMaskData)
123
0
  {
124
0
    if (lengthAndMask > UINT32_MAX)
125
0
      return FALSE;
126
0
    pointer->lengthAndMask = (UINT32)lengthAndMask;
127
0
    pointer->andMaskData = (BYTE*)malloc(lengthAndMask);
128
0
    if (!pointer->andMaskData)
129
0
      return FALSE;
130
131
0
    CopyMemory(pointer->andMaskData, andMaskData, lengthAndMask);
132
0
  }
133
134
0
  if (lengthXorMask && xorMaskData)
135
0
  {
136
0
    if (lengthXorMask > UINT32_MAX)
137
0
      return FALSE;
138
0
    pointer->lengthXorMask = (UINT32)lengthXorMask;
139
0
    pointer->xorMaskData = (BYTE*)malloc(lengthXorMask);
140
0
    if (!pointer->xorMaskData)
141
0
      return FALSE;
142
143
0
    CopyMemory(pointer->xorMaskData, xorMaskData, lengthXorMask);
144
0
  }
145
146
0
  return TRUE;
147
0
}
148
149
WINPR_ATTR_NODISCARD
150
static BOOL update_pointer_color(rdpContext* context, const POINTER_COLOR_UPDATE* pointer_color)
151
0
{
152
0
  WINPR_ASSERT(context);
153
0
  WINPR_ASSERT(pointer_color);
154
155
0
  rdpCache* cache = context->cache;
156
0
  WINPR_ASSERT(cache);
157
158
0
  rdpPointer* pointer = Pointer_Alloc(context);
159
160
0
  if (pointer == nullptr)
161
0
    return FALSE;
162
0
  pointer->xorBpp = 24;
163
0
  pointer->xPos = pointer_color->hotSpotX;
164
0
  pointer->yPos = pointer_color->hotSpotY;
165
0
  pointer->width = pointer_color->width;
166
0
  pointer->height = pointer_color->height;
167
168
0
  if (!update_pointer_copy_andxor(pointer, pointer_color->andMaskData,
169
0
                                  pointer_color->lengthAndMask, pointer_color->xorMaskData,
170
0
                                  pointer_color->lengthXorMask))
171
0
    goto out_fail;
172
173
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
174
0
    goto out_fail;
175
176
0
  if (!pointer_cache_put(cache->pointer, pointer_color->cacheIndex, pointer, TRUE))
177
0
    goto out_fail;
178
179
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
180
181
0
out_fail:
182
0
  pointer_free(context, pointer);
183
0
  return FALSE;
184
0
}
185
186
WINPR_ATTR_NODISCARD
187
static BOOL update_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer_large)
188
0
{
189
0
  WINPR_ASSERT(context);
190
0
  WINPR_ASSERT(pointer_large);
191
192
0
  rdpCache* cache = context->cache;
193
0
  WINPR_ASSERT(cache);
194
195
0
  rdpPointer* pointer = Pointer_Alloc(context);
196
0
  if (pointer == nullptr)
197
0
    return FALSE;
198
0
  pointer->xorBpp = pointer_large->xorBpp;
199
0
  pointer->xPos = pointer_large->hotSpotX;
200
0
  pointer->yPos = pointer_large->hotSpotY;
201
0
  pointer->width = pointer_large->width;
202
0
  pointer->height = pointer_large->height;
203
204
0
  if (!update_pointer_copy_andxor(pointer, pointer_large->andMaskData,
205
0
                                  pointer_large->lengthAndMask, pointer_large->xorMaskData,
206
0
                                  pointer_large->lengthXorMask))
207
0
    goto out_fail;
208
209
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
210
0
    goto out_fail;
211
212
0
  if (!pointer_cache_put(cache->pointer, pointer_large->cacheIndex, pointer, FALSE))
213
0
    goto out_fail;
214
215
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
216
217
0
out_fail:
218
0
  pointer_free(context, pointer);
219
0
  return FALSE;
220
0
}
221
222
WINPR_ATTR_NODISCARD
223
static BOOL update_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
224
0
{
225
0
  if (!context || !pointer_new)
226
0
    return FALSE;
227
228
0
  rdpCache* cache = context->cache;
229
0
  rdpPointer* pointer = Pointer_Alloc(context);
230
231
0
  if (!pointer)
232
0
    return FALSE;
233
234
0
  pointer->xorBpp = pointer_new->xorBpp;
235
0
  pointer->xPos = pointer_new->colorPtrAttr.hotSpotX;
236
0
  pointer->yPos = pointer_new->colorPtrAttr.hotSpotY;
237
0
  pointer->width = pointer_new->colorPtrAttr.width;
238
0
  pointer->height = pointer_new->colorPtrAttr.height;
239
0
  if (!update_pointer_copy_andxor(
240
0
          pointer, pointer_new->colorPtrAttr.andMaskData, pointer_new->colorPtrAttr.lengthAndMask,
241
0
          pointer_new->colorPtrAttr.xorMaskData, pointer_new->colorPtrAttr.lengthXorMask))
242
0
    goto out_fail;
243
244
0
  if (!IFCALLRESULT(TRUE, pointer->New, context, pointer))
245
0
    goto out_fail;
246
247
0
  if (!pointer_cache_put(cache->pointer, pointer_new->colorPtrAttr.cacheIndex, pointer, FALSE))
248
0
    goto out_fail;
249
250
0
  return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
251
252
0
out_fail:
253
0
  pointer_free(context, pointer);
254
0
  return FALSE;
255
0
}
256
257
WINPR_ATTR_NODISCARD
258
static BOOL update_pointer_cached(rdpContext* context, const POINTER_CACHED_UPDATE* pointer_cached)
259
0
{
260
0
  WINPR_ASSERT(context);
261
0
  WINPR_ASSERT(pointer_cached);
262
263
0
  rdpCache* cache = context->cache;
264
0
  WINPR_ASSERT(cache);
265
266
0
  rdpPointer* pointer = pointer_cache_get(cache->pointer, pointer_cached->cacheIndex);
267
268
0
  if (pointer != nullptr)
269
0
    return IFCALLRESULT(TRUE, pointer->Set, context, pointer);
270
271
0
  return FALSE;
272
0
}
273
274
rdpPointer* pointer_cache_get(rdpPointerCache* pointer_cache, UINT32 index)
275
0
{
276
0
  WINPR_ASSERT(pointer_cache);
277
278
0
  if (index >= pointer_cache->cacheSize)
279
0
  {
280
0
    WLog_ERR(TAG, "invalid pointer index:%" PRIu32 " [%" PRIu32 "]", index,
281
0
             pointer_cache->cacheSize);
282
0
    return nullptr;
283
0
  }
284
285
0
  WINPR_ASSERT(pointer_cache->entries);
286
0
  return pointer_cache->entries[index];
287
0
}
288
289
BOOL pointer_cache_put(rdpPointerCache* pointer_cache, UINT32 index, rdpPointer* pointer,
290
                       BOOL colorCache)
291
0
{
292
0
  const FreeRDP_Settings_Keys_UInt32 id =
293
0
      colorCache ? FreeRDP_ColorPointerCacheSize : FreeRDP_PointerCacheSize;
294
295
0
  WINPR_ASSERT(pointer_cache);
296
0
  WINPR_ASSERT(pointer_cache->context);
297
298
0
  const UINT32 size = freerdp_settings_get_uint32(pointer_cache->context->settings, id);
299
0
  WLog_DBG(TAG, "[%s] pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32 "]",
300
0
           pointerType(colorCache), index, pointer_cache->cacheSize, size);
301
0
  if (index >= pointer_cache->cacheSize)
302
0
  {
303
0
    WLog_ERR(TAG,
304
0
             "[%s] invalid pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32 "]",
305
0
             pointerType(colorCache), index, pointer_cache->cacheSize, size);
306
0
    return FALSE;
307
0
  }
308
0
  if (index >= size)
309
0
  {
310
0
    WLog_WARN(TAG,
311
0
              "[%s] suspicious pointer index:%" PRIu32 " [allocated %" PRIu32 ", size %" PRIu32
312
0
              "]",
313
0
              pointerType(colorCache), index, pointer_cache->cacheSize, size);
314
0
  }
315
316
0
  WINPR_ASSERT(pointer_cache->entries);
317
0
  rdpPointer* prevPointer = pointer_cache->entries[index];
318
0
  pointer_free(pointer_cache->context, prevPointer);
319
0
  pointer_cache->entries[index] = pointer;
320
0
  return TRUE;
321
0
}
322
323
void pointer_cache_register_callbacks(rdpUpdate* update)
324
0
{
325
0
  rdpPointerUpdate* pointer = nullptr;
326
327
0
  WINPR_ASSERT(update);
328
0
  WINPR_ASSERT(update->context);
329
330
0
  pointer = update->pointer;
331
0
  WINPR_ASSERT(pointer);
332
333
0
  if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
334
0
  {
335
0
    pointer->PointerPosition = update_pointer_position;
336
0
    pointer->PointerSystem = update_pointer_system;
337
0
    pointer->PointerColor = update_pointer_color;
338
0
    pointer->PointerLarge = update_pointer_large;
339
0
    pointer->PointerNew = update_pointer_new;
340
0
    pointer->PointerCached = update_pointer_cached;
341
0
  }
342
0
}
343
344
rdpPointerCache* pointer_cache_new(rdpContext* context)
345
0
{
346
0
  WINPR_ASSERT(context);
347
348
0
  rdpSettings* settings = context->settings;
349
0
  WINPR_ASSERT(settings);
350
351
0
  rdpPointerCache* pointer_cache = (rdpPointerCache*)calloc(1, sizeof(rdpPointerCache));
352
353
0
  if (!pointer_cache)
354
0
    return nullptr;
355
356
0
  pointer_cache->context = context;
357
358
  /* seen invalid pointer cache requests by mstsc (off by 1) so we ensure the cache entry size
359
   * matches */
360
0
  const UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
361
0
  const UINT32 colorSize = freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
362
0
  pointer_cache->cacheSize = MAX(size, colorSize) + 1;
363
364
0
  WLog_DBG(TAG,
365
0
           "setting cacheSize=%" PRIu32 "[ColorPointerCache=%" PRIu32 ", PointerCache=%" PRIu32
366
0
           "]",
367
0
           pointer_cache->cacheSize, colorSize, size);
368
0
  pointer_cache->entries = (rdpPointer**)calloc(pointer_cache->cacheSize, sizeof(rdpPointer*));
369
370
0
  if (!pointer_cache->entries)
371
0
  {
372
0
    free(pointer_cache);
373
0
    return nullptr;
374
0
  }
375
376
0
  return pointer_cache;
377
0
}
378
379
void pointer_cache_free(rdpPointerCache* pointer_cache)
380
0
{
381
0
  if (pointer_cache != nullptr)
382
0
  {
383
    /* Reset pointer to default before deleting the cache.
384
     */
385
0
    if (pointer_cache->context && pointer_cache->context->graphics)
386
0
    {
387
0
      rdpPointer* pointer = pointer_cache->context->graphics->Pointer_Prototype;
388
0
      if (pointer && pointer->SetDefault)
389
0
        (void)pointer->SetDefault(pointer_cache->context);
390
0
    }
391
392
0
    if (pointer_cache->entries)
393
0
    {
394
0
      for (UINT32 i = 0; i < pointer_cache->cacheSize; i++)
395
0
      {
396
0
        rdpPointer* pointer = pointer_cache->entries[i];
397
0
        pointer_free(pointer_cache->context, pointer);
398
0
      }
399
0
    }
400
401
0
    free((void*)pointer_cache->entries);
402
0
    free(pointer_cache);
403
0
  }
404
0
}
405
406
POINTER_COLOR_UPDATE* copy_pointer_color_update(rdpContext* context,
407
                                                const POINTER_COLOR_UPDATE* pointer)
408
0
{
409
0
  POINTER_COLOR_UPDATE* dst = calloc(1, sizeof(POINTER_COLOR_UPDATE));
410
411
0
  if (!dst || !pointer)
412
0
    goto fail;
413
414
0
  *dst = *pointer;
415
416
0
  if (pointer->lengthAndMask > 0)
417
0
  {
418
0
    dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
419
420
0
    if (!dst->andMaskData)
421
0
      goto fail;
422
423
0
    memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
424
0
  }
425
426
0
  if (pointer->lengthXorMask > 0)
427
0
  {
428
0
    dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
429
430
0
    if (!dst->xorMaskData)
431
0
      goto fail;
432
433
0
    memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
434
0
  }
435
436
0
  return dst;
437
0
fail:
438
0
  free_pointer_color_update(context, dst);
439
0
  return nullptr;
440
0
}
441
442
void free_pointer_color_update(rdpContext* context, POINTER_COLOR_UPDATE* pointer)
443
373
{
444
373
  WINPR_UNUSED(context);
445
446
373
  if (!pointer)
447
0
    return;
448
449
373
  free(pointer->xorMaskData);
450
373
  free(pointer->andMaskData);
451
373
  free(pointer);
452
373
}
453
454
POINTER_LARGE_UPDATE* copy_pointer_large_update(rdpContext* context,
455
                                                const POINTER_LARGE_UPDATE* pointer)
456
0
{
457
0
  POINTER_LARGE_UPDATE* dst = calloc(1, sizeof(POINTER_LARGE_UPDATE));
458
459
0
  if (!dst || !pointer)
460
0
    goto fail;
461
462
0
  *dst = *pointer;
463
464
0
  if (pointer->lengthAndMask > 0)
465
0
  {
466
0
    dst->andMaskData = calloc(pointer->lengthAndMask, sizeof(BYTE));
467
468
0
    if (!dst->andMaskData)
469
0
      goto fail;
470
471
0
    memcpy(dst->andMaskData, pointer->andMaskData, pointer->lengthAndMask);
472
0
  }
473
474
0
  if (pointer->lengthXorMask > 0)
475
0
  {
476
0
    dst->xorMaskData = calloc(pointer->lengthXorMask, sizeof(BYTE));
477
478
0
    if (!dst->xorMaskData)
479
0
      goto fail;
480
481
0
    memcpy(dst->xorMaskData, pointer->xorMaskData, pointer->lengthXorMask);
482
0
  }
483
484
0
  return dst;
485
0
fail:
486
0
  free_pointer_large_update(context, dst);
487
0
  return nullptr;
488
0
}
489
490
void free_pointer_large_update(rdpContext* context, POINTER_LARGE_UPDATE* pointer)
491
587
{
492
587
  WINPR_UNUSED(context);
493
587
  if (!pointer)
494
0
    return;
495
496
587
  free(pointer->xorMaskData);
497
587
  free(pointer->andMaskData);
498
587
  free(pointer);
499
587
}
500
501
POINTER_NEW_UPDATE* copy_pointer_new_update(rdpContext* context, const POINTER_NEW_UPDATE* pointer)
502
0
{
503
0
  POINTER_NEW_UPDATE* dst = calloc(1, sizeof(POINTER_NEW_UPDATE));
504
505
0
  if (!dst || !pointer)
506
0
    goto fail;
507
508
0
  *dst = *pointer;
509
510
0
  if (pointer->colorPtrAttr.lengthAndMask > 0)
511
0
  {
512
0
    dst->colorPtrAttr.andMaskData = calloc(pointer->colorPtrAttr.lengthAndMask, sizeof(BYTE));
513
514
0
    if (!dst->colorPtrAttr.andMaskData)
515
0
      goto fail;
516
517
0
    memcpy(dst->colorPtrAttr.andMaskData, pointer->colorPtrAttr.andMaskData,
518
0
           pointer->colorPtrAttr.lengthAndMask);
519
0
  }
520
521
0
  if (pointer->colorPtrAttr.lengthXorMask > 0)
522
0
  {
523
0
    dst->colorPtrAttr.xorMaskData = calloc(pointer->colorPtrAttr.lengthXorMask, sizeof(BYTE));
524
525
0
    if (!dst->colorPtrAttr.xorMaskData)
526
0
      goto fail;
527
528
0
    memcpy(dst->colorPtrAttr.xorMaskData, pointer->colorPtrAttr.xorMaskData,
529
0
           pointer->colorPtrAttr.lengthXorMask);
530
0
  }
531
532
0
  return dst;
533
0
fail:
534
0
  free_pointer_new_update(context, dst);
535
0
  return nullptr;
536
0
}
537
538
void free_pointer_new_update(WINPR_ATTR_UNUSED rdpContext* context, POINTER_NEW_UPDATE* pointer)
539
355
{
540
355
  if (!pointer)
541
0
    return;
542
543
355
  free(pointer->colorPtrAttr.xorMaskData);
544
355
  free(pointer->colorPtrAttr.andMaskData);
545
355
  free(pointer);
546
355
}
547
548
POINTER_CACHED_UPDATE* copy_pointer_cached_update(rdpContext* context,
549
                                                  const POINTER_CACHED_UPDATE* pointer)
550
0
{
551
0
  POINTER_CACHED_UPDATE* dst = calloc(1, sizeof(POINTER_CACHED_UPDATE));
552
553
0
  if (!dst)
554
0
    goto fail;
555
556
0
  *dst = *pointer;
557
0
  return dst;
558
0
fail:
559
0
  free_pointer_cached_update(context, dst);
560
0
  return nullptr;
561
0
}
562
563
void free_pointer_cached_update(rdpContext* context, POINTER_CACHED_UPDATE* pointer)
564
174
{
565
174
  WINPR_UNUSED(context);
566
174
  free(pointer);
567
174
}
568
569
void free_pointer_position_update(rdpContext* context, POINTER_POSITION_UPDATE* pointer)
570
219
{
571
219
  WINPR_UNUSED(context);
572
219
  free(pointer);
573
219
}
574
575
POINTER_POSITION_UPDATE* copy_pointer_position_update(rdpContext* context,
576
                                                      const POINTER_POSITION_UPDATE* pointer)
577
0
{
578
0
  POINTER_POSITION_UPDATE* dst = calloc(1, sizeof(POINTER_POSITION_UPDATE));
579
580
0
  if (!dst || !pointer)
581
0
    goto fail;
582
583
0
  *dst = *pointer;
584
0
  return dst;
585
0
fail:
586
0
  free_pointer_position_update(context, dst);
587
0
  return nullptr;
588
0
}
589
590
void free_pointer_system_update(rdpContext* context, POINTER_SYSTEM_UPDATE* pointer)
591
202
{
592
202
  WINPR_UNUSED(context);
593
202
  free(pointer);
594
202
}
595
596
POINTER_SYSTEM_UPDATE* copy_pointer_system_update(rdpContext* context,
597
                                                  const POINTER_SYSTEM_UPDATE* pointer)
598
0
{
599
0
  POINTER_SYSTEM_UPDATE* dst = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
600
601
0
  if (!dst || !pointer)
602
0
    goto fail;
603
604
0
  *dst = *pointer;
605
0
  return dst;
606
0
fail:
607
0
  free_pointer_system_update(context, dst);
608
0
  return nullptr;
609
0
}