Add an internal-representation no-op function.
[u/mdw/catacomb] / lmem.c
CommitLineData
359df778 1/* -*-c-*-
2 *
37038e63 3 * $Id: lmem.c,v 1.3 2000/07/29 21:58:15 mdw Exp $
359df778 4 *
5 * Locked memory allocation (Unix-specific)
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: lmem.c,v $
37038e63 33 * Revision 1.3 2000/07/29 21:58:15 mdw
34 * (l_destroy): New function for destroying locked memory blocks.
35 *
47d5f7c2 36 * Revision 1.2 2000/06/17 11:29:20 mdw
37 * Add arena support.
38 *
359df778 39 * Revision 1.1 1999/12/22 16:02:52 mdw
40 * Interface to allocating `locked' memory (which isn't paged out).
41 *
42 */
43
44/*----- Header files ------------------------------------------------------*/
45
46#include "config.h"
47
48#include <assert.h>
49#include <errno.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include <sys/types.h>
55#include <unistd.h>
56
57#ifdef HAVE_MLOCK
58# include <sys/mman.h>
59#endif
60
47d5f7c2 61#include <mLib/arena.h>
359df778 62#include <mLib/dstr.h>
63#include <mLib/sub.h>
64
65#include "lmem.h"
66
47d5f7c2 67/*----- Arena operations --------------------------------------------------*/
68
69static void *aalloc(arena *a, size_t sz) { return l_alloc((lmem *)a, sz); }
70static void afree(arena *a, void *p) { l_free((lmem *)a, p); }
71static void apurge(arena *a) { l_purge((lmem *)a); }
72
73static arena_ops l_ops = { aalloc, arena_fakerealloc, afree, apurge };
74
359df778 75/*----- Main code ---------------------------------------------------------*/
76
77/* --- @l_init@ --- *
78 *
79 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
80 * @size_t sz@ = size of locked memory area requested
81 *
82 * Returns: Zero if everything is fine, @+1@ if some insecure memory was
83 * allocated, and @-1@ if everything went horribly wrong.
84 *
85 * Use: Initializes the locked memory manager. This function is safe
86 * to call in a privileged program; privileges should usually be
87 * dropped after allocating the locked memory block.
88 *
89 * You must call @sub_init@ before allocating locked memory
90 * buffers.
91 */
92
93int l_init(lmem *lm, size_t sz)
94{
95 char *p;
96 int rc = 0;
97 l_node *l;
98
99 /* --- Preliminaries --- */
100
47d5f7c2 101 lm->a.ops = &l_ops;
359df778 102 lm->err = 0;
37038e63 103 lm->f = 0;
359df778 104
105 /* --- Try making a secure locked passphrase buffer --- *
106 *
107 * Drop privileges before emitting diagnostic messages.
108 */
109
110#ifdef HAVE_MLOCK
111
112 /* --- Memory-map a page from somewhere --- */
113
114# ifdef MAP_ANON
115 p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
116# else
117 {
118 int fd;
119 if ((fd = open("/dev/zero", O_RDWR)) >= 0) {
120 p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
121 close(fd);
122 }
123 }
124# endif
125
126 /* --- Lock the page in memory --- *
127 *
128 * Why does @mmap@ return such a stupid result if it fails?
129 */
130
131 if (p == 0 || p == MAP_FAILED) {
132 lm->emsg = "couldn't map locked memory area: %s";
133 lm->err = errno;
134 p = 0;
135 } else if (mlock(p, sz)) {
136 lm->emsg = "error locking memory area: %s";
137 lm->err = errno;
138 munmap(p, sz);
139 p = 0;
37038e63 140 } else
141 lm->f |= LF_LOCKED;
359df778 142
143#endif
144
145 /* --- Make a standard passphrase buffer --- */
146
147#ifdef HAVE_MLOCK
148 if (!p)
149#else
150 ll->err = 0;
151 ll->emsg = "locked memory not available on this system";
152#endif
153 {
154 if ((p = malloc(sz)) == 0) {
155 lm->emsg = "not enough standard memory!";
156 lm->err = ENOMEM;
157 return (-1);
158 }
159 rc = +1;
160 }
161
162 /* --- Initialize the buffer --- */
163
164 lm->sz = lm->free = sz;
165 lm->p = p;
166
167 /* --- Initialize the free list --- */
168
169 l = CREATE(l_node);
170 l->next = 0;
171 l->p = p;
172 l->sz = sz;
173 l->f = 0;
174 lm->l = l;
175
176 /* --- Done --- */
177
178 return (rc);
179}
180
181/* --- @l_alloc@ --- *
182 *
183 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
184 * @size_t sz@ = size requested
185 *
186 * Returns: Pointer to allocated memory.
187 *
188 * Use: Allocates @sz@ bytes of locked memory.
189 */
190
191void *l_alloc(lmem *lm, size_t sz)
192{
193 l_node *l;
194
195 sz = (sz + 3u) & ~3u;
196 for (l = lm->l; l; l = l->next) {
197 if (l->f & LF_ALLOC)
198 continue;
199 if (l->sz < sz)
200 continue;
201 l->f |= LF_ALLOC;
202 if (l->sz > sz) {
203 l_node *n = CREATE(l_node);
204 n->next = l->next;
205 n->p = l->p + sz;
206 n->sz = l->sz - sz;
207 l->sz = sz;
208 n->f = 0;
209 l->next = n;
210 }
211 assert(((void)"Locked buffer space has vanished", lm->free >= sz));
212 lm->free -= sz;
213 return (l->p);
214 }
215 return (0);
216}
217
218/* --- @l_free@ --- *
219 *
220 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
221 * @void *p@ = pointer to block
222 *
223 * Returns: ---
224 *
225 * Use: Releases a block of locked memory.
226 */
227
228void l_free(lmem *lm, void *p)
229{
230 l_node *l;
231 l_node *ll = 0;
232
233 for (l = lm->l; l; l = l->next) {
234 size_t sz;
235
236 /* --- If this isn't the block, skip it --- */
237
238 if (l->p != p) {
239 ll = l;
240 continue;
241 }
242 assert(((void)"Block is already free", l->f & LF_ALLOC));
243
244 /* --- Coalesce with adjacent free blocks --- */
245
246 l->f &= ~LF_ALLOC;
247 sz = l->sz;
248 memset(p, 0, sz);
249
250 if (ll && !(ll->f & LF_ALLOC)) {
251 assert(((void)"Previous block doesn't fit", ll->p + ll->sz == p));
252 ll->sz += sz;
253 ll->next = l->next;
254 DESTROY(l);
255 l = ll;
256 }
257
258 ll = l->next;
259 if (ll && !(ll->f & LF_ALLOC)) {
260 assert(((void)"Next block doesn't fit", ll->p == l->p + l->sz));
261 l->sz += ll->sz;
262 l->next = ll->next;
263 DESTROY(ll);
264 }
265
266 lm->free += sz;
267 assert(((void)"Free lunch", lm->free <= lm->sz));
268 return;
269 }
270 assert(((void)"Not a locked block", 0));
271}
272
273/* --- @l_purge@ --- *
274 *
275 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
276 *
277 * Returns: ---
278 *
279 * Use: Purges all the free blocks in the buffer, and clears all of
280 * the locked memory. Memory is not freed back to the system.
281 */
282
283void l_purge(lmem *lm)
284{
285 l_node *l;
286
287 l = lm->l;
288 while (l) {
289 l_node *ll = l->next;
290 DESTROY(l);
291 l = ll;
292 }
293 memset(lm->p, 0, lm->sz);
294 l = CREATE(l_node);
295 l->next = 0;
296 l->p = lm->p;
297 l->sz = lm->sz;
298 l->f = 0;
299 lm->l = l;
300 lm->free = l->sz;
301}
302
37038e63 303/* --- @l_destroy@ --- *
304 *
305 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
306 *
307 * Returns: ---
308 *
309 * Use: Disposes of a locked memory arena permanently.
310 */
311
312void l_destroy(lmem *lm)
313{
314 l_node *l;
315
316 l = lm->l;
317 while (l) {
318 l_node *ll = l->next;
319 DESTROY(l);
320 l = ll;
321 }
322 memset(lm->p, 0, lm->sz);
323
324 if (lm->f & LF_LOCKED)
325 munmap(lm->p, lm->sz);
326 else
327 free(lm->p);
328}
329
359df778 330/* --- @l_report@ --- *
331 *
332 * Arguments: @lmem *lm@ = pointer to locked memory descriptor
333 * @dstr *d@ = string to write the error message on
334 *
335 * Returns: Zero if the buffer is fine, @+1@ if there was a problem
336 * getting locked memory but insecure stuff could be allocated,
337 * and @-1@ if not even insecure memory could be found.
338 *
339 * Use: Returns a user-digestable explanation for the state of a
340 * locked memory buffer. If the return code is zero, no message
341 * is emitted to the string @d@.
342 */
343
344int l_report(lmem *lm, dstr *d)
345{
346 int rc;
347 if (lm->err)
348 dstr_putf(d, lm->emsg, strerror(lm->err));
349 if (!lm->p)
350 rc = -1;
351 else if (lm->err)
352 rc = +1;
353 else
354 rc = 0;
355 return (rc);
356}
357
358/*----- That's all, folks -------------------------------------------------*/