Coverage Report

Created: 2026-08-12 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lvm2/libdm/mm/pool.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3
 * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
4
 *
5
 * This file is part of the device-mapper userspace tools.
6
 *
7
 * This copyrighted material is made available to anyone wishing to use,
8
 * modify, copy, or redistribute it subject to the terms and conditions
9
 * of the GNU Lesser General Public License v.2.1.
10
 *
11
 * You should have received a copy of the GNU Lesser General Public License
12
 * along with this program; if not, write to the Free Software Foundation,
13
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
14
 */
15
16
#include "libdm/misc/dmlib.h"
17
#include <sys/mman.h>
18
#include <pthread.h>
19
20
static DM_LIST_INIT(_dm_pools);
21
static pthread_mutex_t _dm_pools_mutex = PTHREAD_MUTEX_INITIALIZER;
22
void dm_pools_check_leaks(void);
23
24
#ifdef DEBUG_ENFORCE_POOL_LOCKING
25
#ifdef DEBUG_POOL
26
#error Do not use DEBUG_POOL with DEBUG_ENFORCE_POOL_LOCKING
27
#endif
28
29
/*
30
 * Use mprotect system call to ensure all locked pages are not writable.
31
 * Generates segmentation fault with write access to the locked pool.
32
 *
33
 * - Implementation is using posix_memalign() to get page aligned
34
 *   memory blocks (could be implemented also through malloc).
35
 * - Only pool-fast is properly handled for now.
36
 * - Checksum is slower compared to mprotect.
37
 */
38
static size_t _pagesize = 0;
39
static size_t _pagesize_mask = 0;
40
static pthread_once_t _pagesize_once = PTHREAD_ONCE_INIT;
41
42
static void _init_pagesize(void)
43
{
44
  _pagesize = getpagesize();
45
  _pagesize_mask = _pagesize - 1;
46
}
47
48
static size_t _get_pagesize(void)
49
{
50
  pthread_once(&_pagesize_once, _init_pagesize);
51
  return _pagesize;
52
}
53
54
static size_t _get_pagesize_mask(void)
55
{
56
  pthread_once(&_pagesize_once, _init_pagesize);
57
  return _pagesize_mask;
58
}
59
60
#define ALIGN_ON_PAGE(size) (((size) + (_get_pagesize_mask())) & ~(_get_pagesize_mask()))
61
#endif
62
63
#ifdef DEBUG_POOL
64
#include "pool-debug.c"
65
#else
66
#include "pool-fast.c"
67
#endif
68
69
char *dm_pool_strdup(struct dm_pool *p, const char *str)
70
0
{
71
0
  size_t len = strlen(str) + 1;
72
0
  char *ret = dm_pool_alloc(p, len);
73
74
0
  if (ret)
75
0
    memcpy(ret, str, len);
76
77
0
  return ret;
78
0
}
79
80
char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
81
0
{
82
0
  size_t slen = strlen(str);
83
0
  size_t len = (slen < n) ? slen : n;
84
0
  char *ret = dm_pool_alloc(p, len + 1);
85
86
0
  if (ret) {
87
0
    ret[len] = '\0';
88
0
    memcpy(ret, str, len);
89
0
  }
90
91
0
  return ret;
92
0
}
93
94
void *dm_pool_zalloc(struct dm_pool *p, size_t s)
95
0
{
96
0
  void *ptr = dm_pool_alloc(p, s);
97
98
0
  if (ptr)
99
0
    memset(ptr, 0, s);
100
101
0
  return ptr;
102
0
}
103
104
void dm_pools_check_leaks(void)
105
0
{
106
0
  struct dm_pool *p;
107
108
0
  pthread_mutex_lock(&_dm_pools_mutex);
109
0
  if (dm_list_empty(&_dm_pools)) {
110
0
    pthread_mutex_unlock(&_dm_pools_mutex);
111
0
    return;
112
0
  }
113
114
0
  log_error("You have a memory leak (not released memory pool):");
115
0
  dm_list_iterate_items(p, &_dm_pools) {
116
#ifdef DEBUG_POOL
117
    log_error(" [%p] %s (%u bytes)",
118
        p->orig_pool,
119
        p->name, p->stats.bytes);
120
#else
121
0
    log_error(" [%p] %s", (void *)p, p->name);
122
0
#endif
123
0
  }
124
0
  pthread_mutex_unlock(&_dm_pools_mutex);
125
0
  log_error(INTERNAL_ERROR "Unreleased memory pool(s) found.");
126
0
}
127
128
/**
129
 * Status of locked pool.
130
 *
131
 * \param p
132
 * Pool to be tested for lock status.
133
 *
134
 * \return
135
 * 1 when the pool is locked, 0 otherwise.
136
 */
137
int dm_pool_locked(struct dm_pool *p)
138
0
{
139
0
  return p->locked;
140
0
}
141
142
/**
143
 * Lock memory pool.
144
 *
145
 * \param p
146
 * Pool to be locked.
147
 *
148
 * \param crc
149
 * Bool specifies whether to store the pool crc/hash checksum.
150
 *
151
 * \return
152
 * 1 (success) when the pool was properly locked, 0 otherwise.
153
 */
154
int dm_pool_lock(struct dm_pool *p, int crc)
155
0
{
156
0
  if (p->locked) {
157
0
    log_error(INTERNAL_ERROR "Pool %s is already locked.",
158
0
        p->name);
159
0
    return 0;
160
0
  }
161
162
0
  if (crc)
163
0
    p->crc = _pool_crc(p);  /* Get crc for pool */
164
165
0
  if (!_pool_protect(p, PROT_READ)) {
166
0
    _pool_protect(p, PROT_READ | PROT_WRITE);
167
0
    return_0;
168
0
  }
169
170
0
  p->locked = 1;
171
172
0
  log_debug_mem("Pool %s is locked.", p->name);
173
174
0
  return 1;
175
0
}
176
177
/**
178
 * Unlock memory pool.
179
 *
180
 * \param p
181
 * Pool to be unlocked.
182
 *
183
 * \param crc
184
 * Bool enables compare of the pool crc/hash with the stored value
185
 * at pool lock. The pool is not properly unlocked if there is a mismatch.
186
 *
187
 * \return
188
 * 1 (success) when the pool was properly unlocked, 0 otherwise.
189
 */
190
int dm_pool_unlock(struct dm_pool *p, int crc)
191
0
{
192
0
  if (!p->locked) {
193
0
    log_error(INTERNAL_ERROR "Pool %s is already unlocked.",
194
0
        p->name);
195
0
    return 0;
196
0
  }
197
198
0
  p->locked = 0;
199
200
0
  if (!_pool_protect(p, PROT_READ | PROT_WRITE))
201
0
    return_0;
202
203
0
  log_debug_mem("Pool %s is unlocked.", p->name);
204
205
0
  if (crc && (p->crc != _pool_crc(p))) {
206
0
    log_error(INTERNAL_ERROR "Pool %s crc mismatch.", p->name);
207
0
    return 0;
208
0
  }
209
210
0
  return 1;
211
0
}