Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F26446638
memory.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
12 KB
Subscribers
None
memory.c
View Options
/* memory.c - memory allocation
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* We use our own memory allocation functions instead of plain malloc(),
* so that we can provide some special enhancements:
* a) functions to provide memory from a secure memory.
* b) by looking at the requested allocation size we
* can reuse memory very quickly (e.g. MPI storage)
* (really needed?)
* c) memory usage reporting if compiled with M_DEBUG
* d) memory checking if compiled with M_GUARD
*
* This file is part of GNUPG.
*
* GNUPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GNUPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<stdarg.h>
#include
"types.h"
#include
"memory.h"
#include
"util.h"
#define MAGIC_NOR_BYTE 0x55
#define MAGIC_SEC_BYTE 0xcc
#define MAGIC_END_BYTE 0xaa
#if SIZEOF_UNSIGNED_LONG == 8
#define EXTRA_ALIGN 4
#else
#define EXTRA_ALIGN 0
#endif
const
void
membug
(
const
char
*
fmt
,
...
);
#ifdef M_DEBUG
#ifndef M_GUARD
#define M_GUARD 1
#endif
#undef m_alloc
#undef m_alloc_clear
#undef m_alloc_secure
#undef m_alloc_secure_clear
#undef m_realloc
#undef m_free
#undef m_check
#undef m_strdup
#define FNAME(a) m_debug_ ##a
#define FNAMEPRT , const char *info
#define FNAMEARG , info
#define store_len(p,n,m) do { add_entry(p,n,m, \
info, __FUNCTION__); } while(0)
#else
#define FNAME(a) m_ ##a
#define FNAMEPRT
#define FNAMEARG
#define store_len(p,n,m) do { ((byte*)p)[EXTRA_ALIGN+0] = n; \
((byte*)p)[EXTRA_ALIGN+1] = n >> 8 ; \
((byte*)p)[EXTRA_ALIGN+2] = n >> 16 ; \
((byte*)p)[EXTRA_ALIGN+3] = m? MAGIC_SEC_BYTE \
: MAGIC_NOR_BYTE; \
} while(0)
#endif
#ifdef M_DEBUG
/* stuff used for memory debuging */
struct
info_entry
{
struct
info_entry
*
next
;
unsigned
count
;
/* call count */
const
char
*
info
;
/* the reference to the info string */
};
struct
memtbl_entry
{
const
void
*
user_p
;
/* for reference: the pointer given to the user */
size_t
user_n
;
/* length requested by the user */
struct
memtbl_entry
*
next
;
/* to build a list of unused entries */
const
struct
info_entry
*
info
;
/* points into the table with */
/* the info strings */
unsigned
inuse
:
1
;
/* this entry is in use */
unsigned
count
:
31
;
};
#define INFO_BUCKETS 53
#define info_hash(p) ( *(u32*)((p)) % INFO_BUCKETS )
static
struct
info_entry
*
info_strings
[
INFO_BUCKETS
];
/* hash table */
static
struct
memtbl_entry
*
memtbl
;
/* the table with the memory info */
static
unsigned
memtbl_size
;
/* number of allocated entries */
static
unsigned
memtbl_len
;
/* number of used entries */
static
struct
memtbl_entry
*
memtbl_unused
;
/* to keep track of unused entries */
static
void
dump_table_at_exit
(
void
);
static
void
dump_table
(
void
);
static
void
check_allmem
(
const
char
*
info
);
/****************
* Put the new P into the debug table and return a pointer to the table entry.
* mode is true for security. BY is the name of the function which called us.
*/
static
void
add_entry
(
byte
*
p
,
unsigned
n
,
int
mode
,
const
char
*
info
,
const
char
*
by
)
{
unsigned
index
;
struct
memtbl_entry
*
e
;
struct
info_entry
*
ie
;
if
(
memtbl_len
<
memtbl_size
)
index
=
memtbl_len
++
;
else
{
struct
memtbl_entry
*
e
;
/* look for a used entry in the table. We take the first one,
* so that freed entries remain as long as possible in the table
* (free appends a new one)
*/
if
(
(
e
=
memtbl_unused
)
)
{
index
=
e
-
memtbl
;
memtbl_unused
=
e
->
next
;
e
->
next
=
NULL
;
}
else
{
/* no free entries in the table: extend the table */
if
(
!
memtbl_size
)
{
/* first time */
memtbl_size
=
100
;
if
(
!
(
memtbl
=
calloc
(
memtbl_size
,
sizeof
*
memtbl
))
)
membug
(
"memory debug table malloc failed
\n
"
);
index
=
0
;
memtbl_len
=
1
;
atexit
(
dump_table_at_exit
);
}
else
{
/* realloc */
unsigned
n
=
memtbl_size
/
4
;
/* enlarge by 25% */
if
(
!
(
memtbl
=
realloc
(
memtbl
,
(
memtbl_size
+
n
)
*
sizeof
*
memtbl
)))
membug
(
"memory debug table realloc failed
\n
"
);
memset
(
memtbl
+
memtbl_size
,
0
,
n
*
sizeof
*
memtbl
);
memtbl_size
+=
n
;
index
=
memtbl_len
++
;
}
}
}
e
=
memtbl
+
index
;
if
(
e
->
inuse
)
membug
(
"Ooops: entry %u is flagged as in use
\n
"
,
index
);
e
->
user_p
=
p
+
4
;
e
->
user_n
=
n
;
e
->
count
++
;
if
(
e
->
next
)
membug
(
"Ooops: entry is in free entry list
\n
"
);
/* do we already have this info string */
for
(
ie
=
info_strings
[
info_hash
(
info
)];
ie
;
ie
=
ie
->
next
)
if
(
ie
->
info
==
info
)
break
;
if
(
!
ie
)
{
/* no: make a new entry */
if
(
!
(
ie
=
malloc
(
sizeof
*
ie
))
)
membug
(
"can't allocate info entry
\n
"
);
ie
->
next
=
info_strings
[
info_hash
(
info
)];
info_strings
[
info_hash
(
info
)]
=
ie
;
ie
->
info
=
info
;
ie
->
count
=
0
;
}
ie
->
count
++
;
e
->
info
=
ie
;
e
->
inuse
=
1
;
/* put the index at the start of the memory */
p
[
0
]
=
index
;
p
[
1
]
=
index
>>
8
;
p
[
2
]
=
index
>>
16
;
p
[
3
]
=
mode
?
MAGIC_SEC_BYTE
:
MAGIC_NOR_BYTE
;
if
(
DBG_MEMORY
)
log_debug
(
"%s allocates %u bytes using %s
\n
"
,
info
,
e
->
user_n
,
by
);
}
/****************
* Check that the memory block is correct. The magic byte has already been
* checked. Checks which are done here:
* - see whether the index points into our memory table
* - see whether P is the same as the one stored in the table
* - see whether we have already freed this block.
*/
struct
memtbl_entry
*
check_mem
(
const
byte
*
p
,
const
char
*
info
)
{
unsigned
n
;
struct
memtbl_entry
*
e
;
n
=
p
[
EXTRA_ALIGN
+
0
];
n
|=
p
[
EXTRA_ALIGN
+
1
]
<<
8
;
n
|=
p
[
EXTRA_ALIGN
+
2
]
<<
16
;
if
(
n
>=
memtbl_len
)
membug
(
"memory at %p corrupted: index=%u table_len=%u (%s)
\n
"
,
p
+
EXTRA_ALIGN
+
4
,
n
,
memtbl_len
,
info
);
e
=
memtbl
+
n
;
if
(
e
->
user_p
!=
p
+
EXTRA_ALIGN
+
4
)
membug
(
"memory at %p corrupted: reference mismatch (%s)
\n
"
,
p
+
EXTRA_ALIGN
+
4
,
info
);
if
(
!
e
->
inuse
)
membug
(
"memory at %p corrupted: marked as free (%s)
\n
"
,
p
+
EXTRA_ALIGN
+
4
,
info
);
if
(
!
(
p
[
EXTRA_ALIGN
+
3
]
==
MAGIC_NOR_BYTE
||
p
[
EXTRA_ALIGN
+
3
]
==
MAGIC_SEC_BYTE
)
)
membug
(
"memory at %p corrupted: underflow=%02x (%s)
\n
"
,
p
+
EXTRA_ALIGN
+
4
,
p
[
EXTRA_ALIGN
+
3
],
info
);
if
(
p
[
EXTRA_ALIGN
+
4
+
e
->
user_n
]
!=
MAGIC_END_BYTE
)
membug
(
"memory at %p corrupted: overflow=%02x (%s)
\n
"
,
p
+
EXTRA_ALIGN
+
4
,
p
[
EXTRA_ALIGN
+
4
+
e
->
user_n
],
info
);
return
e
;
}
/****************
* free the entry and the memory (replaces free)
*/
static
void
free_entry
(
byte
*
p
,
const
char
*
info
)
{
struct
memtbl_entry
*
e
,
*
e2
;
check_allmem
(
"add_entry"
);
e
=
check_mem
(
p
,
info
);
if
(
DBG_MEMORY
)
log_debug
(
"%s frees %u bytes alloced by %s
\n
"
,
info
,
e
->
user_n
,
e
->
info
->
info
);
if
(
!
e
->
inuse
)
{
if
(
e
->
user_p
==
p
+
EXTRA_ALIGN
+
4
)
membug
(
"freeing an already freed pointer at %p
\n
"
,
p
+
EXTRA_ALIGN
+
4
);
else
membug
(
"freeing pointer %p which is flagged as freed
\n
"
,
p
+
EXTRA_ALIGN
+
4
);
}
e
->
inuse
=
0
;
e
->
next
=
NULL
;
if
(
!
memtbl_unused
)
memtbl_unused
=
e
;
else
{
for
(
e2
=
memtbl_unused
;
e2
->
next
;
e2
=
e2
->
next
)
;
e2
->
next
=
e
;
}
memset
(
p
,
'f'
,
e
->
user_n
+
5
);
free
(
p
);
}
static
void
dump_entry
(
struct
memtbl_entry
*
e
)
{
unsigned
n
=
e
-
memtbl
;
fprintf
(
stderr
,
"mem %4u%c %5u %p %5u %s (%u)
\n
"
,
n
,
e
->
inuse
?
'a'
:
'u'
,
e
->
count
,
e
->
user_p
,
e
->
user_n
,
e
->
info
->
info
,
e
->
info
->
count
);
}
static
void
dump_table_at_exit
(
void
)
{
if
(
DBG_MEMSTAT
)
dump_table
();
}
static
void
dump_table
(
void
)
{
unsigned
n
;
struct
memtbl_entry
*
e
;
ulong
sum
=
0
,
chunks
=
0
;
for
(
e
=
memtbl
,
n
=
0
;
n
<
memtbl_len
;
n
++
,
e
++
)
{
if
(
e
->
inuse
)
{
dump_entry
(
e
);
sum
+=
e
->
user_n
;
chunks
++
;
}
}
fprintf
(
stderr
,
" memory used: %8lu bytes in %ld chunks
\n
"
,
sum
,
chunks
);
}
static
void
check_allmem
(
const
char
*
info
)
{
unsigned
n
;
struct
memtbl_entry
*
e
;
for
(
e
=
memtbl
,
n
=
0
;
n
<
memtbl_len
;
n
++
,
e
++
)
if
(
e
->
inuse
)
check_mem
(
e
->
user_p
-4
-
EXTRA_ALIGN
,
info
);
}
#endif
/* M_DEBUG */
const
void
membug
(
const
char
*
fmt
,
...
)
{
va_list
arg_ptr
;
fprintf
(
stderr
,
"
\n
Memory Error: "
)
;
va_start
(
arg_ptr
,
fmt
)
;
vfprintf
(
stderr
,
fmt
,
arg_ptr
)
;
va_end
(
arg_ptr
);
fflush
(
stderr
);
#ifdef M_DEBUG
if
(
DBG_MEMSTAT
)
dump_table
();
#endif
abort
();
}
static
void
out_of_core
(
size_t
n
,
int
secure
)
{
log_fatal
(
"out of %s memory while allocating %u bytes
\n
"
,
secure
?
"secure"
:
""
,(
unsigned
)
n
);
}
/****************
* Allocate memory of size n.
* This function gives up if we do not have enough memory
*/
void
*
FNAME
(
alloc
)(
size_t
n
FNAMEPRT
)
{
char
*
p
;
#ifdef M_GUARD
if
(
!
(
p
=
malloc
(
n
+
EXTRA_ALIGN
+
5
))
)
out_of_core
(
n
,
0
);
store_len
(
p
,
n
,
0
);
p
[
4
+
EXTRA_ALIGN
+
n
]
=
MAGIC_END_BYTE
;
return
p
+
EXTRA_ALIGN
+
4
;
#else
if
(
!
(
p
=
malloc
(
n
))
)
out_of_core
(
n
,
0
);
return
p
;
#endif
}
/****************
* Allocate memory of size n from the secure memory pool.
* This function gives up if we do not have enough memory
*/
void
*
FNAME
(
alloc_secure
)(
size_t
n
FNAMEPRT
)
{
char
*
p
;
#ifdef M_GUARD
if
(
!
(
p
=
secmem_malloc
(
n
+
EXTRA_ALIGN
+
5
))
)
out_of_core
(
n
,
1
);
store_len
(
p
,
n
,
1
);
p
[
4
+
EXTRA_ALIGN
+
n
]
=
MAGIC_END_BYTE
;
return
p
+
EXTRA_ALIGN
+
4
;
#else
if
(
!
(
p
=
secmem_malloc
(
n
))
)
out_of_core
(
n
,
1
);
return
p
;
#endif
}
void
*
FNAME
(
alloc_clear
)(
size_t
n
FNAMEPRT
)
{
void
*
p
;
p
=
FNAME
(
alloc
)(
n
FNAMEARG
);
memset
(
p
,
0
,
n
);
return
p
;
}
void
*
FNAME
(
alloc_secure_clear
)(
size_t
n
FNAMEPRT
)
{
void
*
p
;
p
=
FNAME
(
alloc_secure
)(
n
FNAMEARG
);
memset
(
p
,
0
,
n
);
return
p
;
}
/****************
* realloc and clear the old space
*/
void
*
FNAME
(
realloc
)(
void
*
a
,
size_t
n
FNAMEPRT
)
{
#ifdef M_GUARD
unsigned
char
*
p
=
a
;
void
*
b
;
size_t
len
=
m_size
(
a
);
if
(
len
>=
n
)
/* we don't shrink for now */
return
a
;
if
(
p
[
-1
]
==
MAGIC_SEC_BYTE
)
b
=
FNAME
(
alloc_secure_clear
)(
n
FNAMEARG
);
else
b
=
FNAME
(
alloc_clear
)(
n
FNAMEARG
);
FNAME
(
check
)(
NULL
FNAMEARG
);
memcpy
(
b
,
a
,
len
);
FNAME
(
free
)(
p
FNAMEARG
);
#else
void
*
b
;
if
(
m_is_secure
(
a
)
)
{
if
(
!
(
b
=
secmem_realloc
(
a
,
n
))
)
out_of_core
(
n
,
1
);
}
else
{
if
(
!
(
b
=
realloc
(
a
,
n
))
)
out_of_core
(
n
,
0
);
}
#endif
return
b
;
}
/****************
* Free a pointer
*/
void
FNAME
(
free
)(
void
*
a
FNAMEPRT
)
{
byte
*
p
=
a
;
if
(
!
p
)
return
;
#ifdef M_DEBUG
free_entry
(
p
-
EXTRA_ALIGN
-4
,
info
);
#elif M_GUARD
m_check
(
p
);
if
(
m_is_secure
(
a
)
)
secmem_free
(
p
-
EXTRA_ALIGN
-4
);
else
free
(
p
-
EXTRA_ALIGN
-4
);
#else
if
(
m_is_secure
(
a
)
)
secmem_free
(
p
);
else
free
(
p
);
#endif
}
void
FNAME
(
check
)(
const
void
*
a
FNAMEPRT
)
{
#ifdef M_GUARD
const
byte
*
p
=
a
;
#ifdef M_DEBUG
if
(
p
)
check_mem
(
p
-
EXTRA_ALIGN
-4
,
info
);
else
check_allmem
(
info
);
#else
if
(
!
p
)
return
;
if
(
!
(
p
[
-1
]
==
MAGIC_NOR_BYTE
||
p
[
-1
]
==
MAGIC_SEC_BYTE
)
)
membug
(
"memory at %p corrupted (underflow=%02x)
\n
"
,
p
,
p
[
-1
]
);
else
if
(
p
[
m_size
(
p
)]
!=
MAGIC_END_BYTE
)
membug
(
"memory at %p corrupted (overflow=%02x)
\n
"
,
p
,
p
[
-1
]
);
#endif
#endif
}
size_t
m_size
(
const
void
*
a
)
{
#ifndef M_GUARD
log_debug
(
"Ooops, m_size called
\n
"
);
return
0
;
#else
const
byte
*
p
=
a
;
size_t
n
;
#ifdef M_DEBUG
n
=
check_mem
(
p
-
EXTRA_ALIGN
-4
,
"m_size"
)
->
user_n
;
#else
n
=
((
byte
*
)
p
)[
-4
];
n
|=
((
byte
*
)
p
)[
-3
]
<<
8
;
n
|=
((
byte
*
)
p
)[
-2
]
<<
16
;
#endif
return
n
;
#endif
}
#if 0
/* not used */
/****************
* Make a copy of the memory block at a
*/
void *
FNAME(copy)( const void *a FNAMEPRT )
{
void *b;
size_t n;
if( !a )
return NULL;
n = m_size(a); Aiiiih woher nehmen
if( m_is_secure(a) )
b = FNAME(alloc_secure)(n FNAMEARG);
else
b = FNAME(alloc)(n FNAMEARG);
memcpy(b, a, n );
return b;
}
#endif
char
*
FNAME
(
strdup
)(
const
char
*
a
FNAMEPRT
)
{
size_t
n
=
strlen
(
a
);
char
*
p
=
FNAME
(
alloc
)(
n
+
1
FNAMEARG
);
strcpy
(
p
,
a
);
return
p
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Jul 17, 1:32 AM (8 h, 9 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
23/44/b7cceea62414b924a7a4f7362f9b
Attached To
rG GnuPG
Event Timeline
Log In to Comment