Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/mmgr/alignedalloc.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * alignedalloc.c
4
 *    Allocator functions to implement palloc_aligned
5
 *
6
 * This is not a fully-fledged MemoryContext type as there is no means to
7
 * create a MemoryContext of this type.  The code here only serves to allow
8
 * operations such as pfree() and repalloc() to work correctly on a memory
9
 * chunk that was allocated by palloc_aligned().
10
 *
11
 * Portions Copyright (c) 2022-2026, PostgreSQL Global Development Group
12
 *
13
 * IDENTIFICATION
14
 *    src/backend/utils/mmgr/alignedalloc.c
15
 *
16
 *-------------------------------------------------------------------------
17
 */
18
19
#include "postgres.h"
20
21
#include "utils/memdebug.h"
22
#include "utils/memutils_memorychunk.h"
23
24
/*
25
 * AlignedAllocFree
26
 *    Frees allocated memory; memory is removed from its owning context.
27
 */
28
void
29
AlignedAllocFree(void *pointer)
30
0
{
31
0
  MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
32
0
  void     *unaligned;
33
34
0
  VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk));
35
36
0
  Assert(!MemoryChunkIsExternal(chunk));
37
38
  /* obtain the original (unaligned) allocated pointer */
39
0
  unaligned = MemoryChunkGetBlock(chunk);
40
41
#ifdef MEMORY_CONTEXT_CHECKING
42
  /* Test for someone scribbling on unused space in chunk */
43
  if (!sentinel_ok(pointer, chunk->requested_size))
44
    elog(WARNING, "detected write past chunk end in %s %p",
45
       GetMemoryChunkContext(unaligned)->name, chunk);
46
#endif
47
48
  /*
49
   * Create a dummy vchunk covering the start of the unaligned chunk, but
50
   * not overlapping the aligned chunk.  This will be freed while pfree'ing
51
   * the unaligned chunk, keeping Valgrind happy.  Then when we return to
52
   * the outer pfree, that will clean up the vchunk for the aligned chunk.
53
   */
54
0
  VALGRIND_MEMPOOL_ALLOC(GetMemoryChunkContext(unaligned), unaligned,
55
0
               (char *) pointer - (char *) unaligned);
56
57
  /* Recursively pfree the unaligned chunk */
58
0
  pfree(unaligned);
59
0
}
60
61
/*
62
 * AlignedAllocRealloc
63
 *    Change the allocated size of a chunk and return possibly a different
64
 *    pointer to a memory address aligned to the same boundary as the
65
 *    originally requested alignment.  The contents of 'pointer' will be
66
 *    copied into the returned pointer up until 'size'.  Any additional
67
 *    memory will be uninitialized.
68
 */
69
void *
70
AlignedAllocRealloc(void *pointer, Size size, int flags)
71
0
{
72
0
  MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
73
0
  Size    alignto;
74
0
  void     *unaligned;
75
0
  MemoryContext ctx;
76
0
  Size    old_size;
77
0
  void     *newptr;
78
79
0
  VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
80
81
0
  alignto = MemoryChunkGetValue(redirchunk);
82
0
  unaligned = MemoryChunkGetBlock(redirchunk);
83
84
  /* sanity check this is a power of 2 value */
85
0
  Assert((alignto & (alignto - 1)) == 0);
86
87
  /*
88
   * Determine the size of the original allocation.  We can't determine this
89
   * exactly as GetMemoryChunkSpace() returns the total space used for the
90
   * allocation, which for contexts like aset includes rounding up to the
91
   * next power of 2.  However, this value is just used to memcpy() the old
92
   * data into the new allocation, so we only need to concern ourselves with
93
   * not reading beyond the end of the original allocation's memory.  The
94
   * drawback here is that we may copy more bytes than we need to, which
95
   * only amounts to wasted effort.  We can safely subtract the extra bytes
96
   * that we requested to allow us to align the pointer.  We must also
97
   * subtract the space for the unaligned pointer's MemoryChunk since
98
   * GetMemoryChunkSpace should have included that.  This does assume that
99
   * all context types use MemoryChunk as a chunk header.
100
   */
101
0
  old_size = GetMemoryChunkSpace(unaligned) -
102
0
    PallocAlignedExtraBytes(alignto) - sizeof(MemoryChunk);
103
104
#ifdef MEMORY_CONTEXT_CHECKING
105
  /* check that GetMemoryChunkSpace returned something realistic */
106
  Assert(old_size >= redirchunk->requested_size);
107
#endif
108
109
  /*
110
   * To keep things simple, we always allocate a new aligned chunk and copy
111
   * data into it.  Because of the above inaccuracy, this may end in copying
112
   * more data than was in the original allocation request size, but that
113
   * should be OK.
114
   */
115
0
  ctx = GetMemoryChunkContext(unaligned);
116
0
  newptr = MemoryContextAllocAligned(ctx, size, alignto, flags);
117
118
  /* Cope cleanly with OOM */
119
0
  if (unlikely(newptr == NULL))
120
0
  {
121
0
    VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
122
0
    return MemoryContextAllocationFailure(ctx, size, flags);
123
0
  }
124
125
  /*
126
   * We may memcpy more than the original allocation request size, which
127
   * would result in trying to copy trailing bytes that the original
128
   * MemoryContextAllocAligned call marked NOACCESS.  So we must mark the
129
   * entire old_size as defined.  That's slightly annoying, but probably not
130
   * worth improving.
131
   */
132
0
  VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
133
0
  memcpy(newptr, pointer, Min(size, old_size));
134
135
  /*
136
   * Create a dummy vchunk covering the start of the old unaligned chunk,
137
   * but not overlapping the aligned chunk.  This will be freed while
138
   * pfree'ing the old unaligned chunk, keeping Valgrind happy.  Then when
139
   * we return to repalloc, it will move the vchunk for the aligned chunk.
140
   */
141
0
  VALGRIND_MEMPOOL_ALLOC(ctx, unaligned,
142
0
               (char *) pointer - (char *) unaligned);
143
144
0
  pfree(unaligned);
145
146
0
  return newptr;
147
0
}
148
149
/*
150
 * AlignedAllocGetChunkContext
151
 *    Return the MemoryContext that 'pointer' belongs to.
152
 */
153
MemoryContext
154
AlignedAllocGetChunkContext(void *pointer)
155
0
{
156
0
  MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
157
0
  MemoryContext cxt;
158
159
0
  VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
160
161
0
  Assert(!MemoryChunkIsExternal(redirchunk));
162
163
0
  cxt = GetMemoryChunkContext(MemoryChunkGetBlock(redirchunk));
164
165
0
  VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
166
167
0
  return cxt;
168
0
}
169
170
/*
171
 * AlignedAllocGetChunkSpace
172
 *    Given a currently-allocated chunk, determine the total space
173
 *    it occupies (including all memory-allocation overhead).
174
 */
175
Size
176
AlignedAllocGetChunkSpace(void *pointer)
177
0
{
178
0
  MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
179
0
  void     *unaligned;
180
0
  Size    space;
181
182
0
  VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
183
184
0
  unaligned = MemoryChunkGetBlock(redirchunk);
185
0
  space = GetMemoryChunkSpace(unaligned);
186
187
0
  VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
188
189
0
  return space;
190
0
}