version 0.2.1
[gnupg.git] / util / secmem.c
1 /* secmem.c  -  memory allocation from a secure heap
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * G10 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
28   #include <unistd.h>
29   #include <sys/mman.h>
30   #include <sys/types.h>
31 #endif
32
33 #include "types.h"
34 #include "memory.h"
35 #include "util.h"
36
37
38 #define DEFAULT_POOLSIZE 8196
39
40 typedef struct memblock_struct MEMBLOCK;
41 struct memblock_struct {
42     unsigned size;
43     union {
44         MEMBLOCK *next;
45         long align_dummy;
46         char d[1];
47     } u;
48 };
49
50
51
52 static void  *pool;
53 static int   pool_okay;
54 static int   pool_is_mmapped;
55 static size_t poolsize; /* allocated length */
56 static size_t poollen;  /* used length */
57 static MEMBLOCK *unused_blocks;
58 static unsigned max_alloced;
59 static unsigned cur_alloced;
60 static unsigned max_blocks;
61 static unsigned cur_blocks;
62
63
64 static void
65 lock_pool( void *p, size_t n )
66 {
67   #ifdef HAVE_MLOCK
68     uid_t uid;
69     int err;
70
71     err = mlock( p, n );
72     if( err && errno )
73         err = errno;
74
75     uid = getuid();
76     if( uid && !geteuid() ) {
77         if( setuid( uid ) )
78             log_fatal("failed to reset uid: %s\n", strerror(errno));
79     }
80
81     if( err ) {
82         log_error("can´t lock memory: %s\n", strerror(err));
83         log_info("Warning: using insecure memory!\n");
84     }
85
86   #else
87     log_info("Please note that you don´t have secure memory on this system\n");
88   #endif
89 }
90
91
92 static void
93 init_pool( size_t n)
94 {
95     poolsize = n;
96
97   #if HAVE_MMAP && defined(MAP_ANONYMOUS)
98     poolsize = (poolsize + 4095) & ~4095;
99     pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
100                               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
101     if( pool == (void*)-1 )
102         log_error("can´t mmap pool of %u bytes: %s - using malloc\n",
103                             (unsigned)poolsize, strerror(errno));
104     else {
105         pool_is_mmapped = 1;
106         pool_okay = 1;
107     }
108
109   #endif
110     if( !pool_okay ) {
111         pool = malloc( poolsize );
112         if( !pool )
113             log_fatal("can´t allocate memory pool of %u bytes\n",
114                                                        (unsigned)poolsize);
115         else
116             pool_okay = 1;
117     }
118     lock_pool( pool, poolsize );
119     poollen = 0;
120 }
121
122
123 /* concatenate unused blocks */
124 static void
125 compress_pool(void)
126 {
127
128 }
129
130
131 void
132 secmem_init( size_t n )
133 {
134     if( n < DEFAULT_POOLSIZE )
135         n = DEFAULT_POOLSIZE;
136     if( !pool_okay )
137         init_pool(n);
138     else
139         log_error("Oops, secure memory pool already initialized\n");
140 }
141
142
143 void *
144 secmem_malloc( size_t size )
145 {
146     MEMBLOCK *mb, *mb2;
147     int compressed=0;
148
149     if( !pool_okay )
150         init_pool(DEFAULT_POOLSIZE);
151
152     /* blocks are always a multiple of 32 */
153     size += sizeof(MEMBLOCK);
154     size = ((size + 31) / 32) * 32;
155
156   retry:
157     /* try to get it from the used blocks */
158     for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
159         if( mb->size >= size ) {
160             if( mb2 )
161                 mb2->u.next = mb->u.next;
162             else
163                 unused_blocks = mb->u.next;
164             goto leave;
165         }
166     /* allocate a new block */
167     if( (poollen + size <= poolsize) ) {
168         mb = pool + poollen;
169         poollen += size;
170         mb->size = size;
171     }
172     else if( !compressed ) {
173         compressed=1;
174         compress_pool();
175         goto retry;
176     }
177     else
178         return NULL;
179
180   leave:
181     cur_alloced += mb->size;
182     cur_blocks++;
183     if( cur_alloced > max_alloced )
184         max_alloced = cur_alloced;
185     if( cur_blocks > max_blocks )
186         max_blocks = cur_blocks;
187     return &mb->u.d;
188 }
189
190
191 void
192 secmem_free( void *a )
193 {
194     MEMBLOCK *mb;
195     size_t size;
196
197     if( !a )
198         return;
199
200     mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.d));
201     size = mb->size;
202     memset(mb, 0xff, size );
203     memset(mb, 0xaa, size );
204     memset(mb, 0x55, size );
205     memset(mb, 0x00, size );
206     mb->size = size;
207     mb->u.next = unused_blocks;
208     unused_blocks = mb;
209     cur_blocks--;
210     cur_alloced -= size;
211 }
212
213 void
214 secmem_term()
215 {
216     if( !pool_okay )
217         return;
218
219     memset( pool, 0xff, poolsize);
220     memset( pool, 0xaa, poolsize);
221     memset( pool, 0x55, poolsize);
222     memset( pool, 0x00, poolsize);
223   #if HAVE_MMAP
224     if( pool_is_mmapped )
225         munmap( pool, poolsize );
226   #endif
227     pool = NULL;
228     pool_okay = 0;
229     poolsize=0;
230     poollen=0;
231     unused_blocks=NULL;
232 }
233
234
235 void
236 secmem_dump_stats()
237 {
238     fprintf(stderr,
239                 "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
240                 cur_alloced, max_alloced, cur_blocks, max_blocks,
241                 (ulong)poollen, (ulong)poolsize );
242 }
243