summaryrefslogtreecommitdiff
path: root/src/libstrongswan/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/utils')
-rw-r--r--src/libstrongswan/utils/backtrace.c117
-rw-r--r--src/libstrongswan/utils/backtrace.h8
-rw-r--r--src/libstrongswan/utils/capabilities.c152
-rw-r--r--src/libstrongswan/utils/capabilities.h36
-rw-r--r--src/libstrongswan/utils/chunk.c251
-rw-r--r--src/libstrongswan/utils/chunk.h60
-rw-r--r--src/libstrongswan/utils/enum.c2
-rw-r--r--src/libstrongswan/utils/identification.c101
-rw-r--r--src/libstrongswan/utils/identification.h1
-rw-r--r--src/libstrongswan/utils/integrity_checker.c4
-rw-r--r--src/libstrongswan/utils/leak_detective.c660
-rw-r--r--src/libstrongswan/utils/leak_detective.h10
-rw-r--r--src/libstrongswan/utils/printf_hook.c1
-rw-r--r--src/libstrongswan/utils/settings.c21
-rw-r--r--src/libstrongswan/utils/settings.h10
-rw-r--r--src/libstrongswan/utils/utils.c28
-rw-r--r--src/libstrongswan/utils/utils.h71
17 files changed, 1160 insertions, 373 deletions
diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c
index 77137f9f1..93031908a 100644
--- a/src/libstrongswan/utils/backtrace.c
+++ b/src/libstrongswan/utils/backtrace.c
@@ -53,6 +53,11 @@ struct private_backtrace_t {
};
/**
+ * Forward declaration of method getter
+ */
+static backtrace_t get_methods();
+
+/**
* Write a format string with arguments to a FILE line, if it is NULL to DBG
*/
static void println(FILE *file, char *format, ...)
@@ -299,7 +304,7 @@ static bfd_entry_t *get_bfd_entry(char *filename)
/**
* Print the source file with line number to file, libbfd variant
*/
-static void print_sourceline(FILE *file, char *filename, void *ptr)
+static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
{
bfd_entry_t *entry;
bfd_find_data_t data = {
@@ -334,13 +339,20 @@ void backtrace_deinit() {}
/**
* Print the source file with line number to file, slow addr2line variant
*/
-static void print_sourceline(FILE *file, char *filename, void *ptr)
+static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
{
char buf[1024];
FILE *output;
int c, i = 0;
+#ifdef __APPLE__
+ snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1",
+ filename, base, ptr);
+#else /* !__APPLE__ */
snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
+#endif /* __APPLE__ */
+
+
output = popen(buf, "r");
if (output)
{
@@ -373,11 +385,9 @@ void backtrace_deinit() {}
METHOD(backtrace_t, log_, void,
private_backtrace_t *this, FILE *file, bool detailed)
{
-#ifdef HAVE_BACKTRACE
+#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H)
size_t i;
- char **strings;
-
- strings = backtrace_symbols(this->frames, this->frame_count);
+ char **strings = NULL;
println(file, " dumping %d stack frame addresses:", this->frame_count);
for (i = 0; i < this->frame_count; i++)
@@ -410,19 +420,33 @@ METHOD(backtrace_t, log_, void,
}
if (detailed && info.dli_fname[0])
{
- print_sourceline(file, (char*)info.dli_fname, ptr);
+ print_sourceline(file, (char*)info.dli_fname,
+ ptr, info.dli_fbase);
}
}
else
#endif /* HAVE_DLADDR */
{
- println(file, " %s", strings[i]);
+#ifdef HAVE_BACKTRACE
+ if (!strings)
+ {
+ strings = backtrace_symbols(this->frames, this->frame_count);
+ }
+ if (strings)
+ {
+ println(file, " %s", strings[i]);
+ }
+ else
+#endif /* HAVE_BACKTRACE */
+ {
+ println(file, " %p", this->frames[i]);
+ }
}
}
- free (strings);
-#else /* !HAVE_BACKTRACE */
- println(file, "C library does not support backtrace().");
-#endif /* HAVE_BACKTRACE */
+ free(strings);
+#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
+ println(file, "no support for backtrace()/libunwind");
+#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
}
METHOD(backtrace_t, contains_function, bool,
@@ -512,12 +536,69 @@ METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
return &enumerator->public;
}
+METHOD(backtrace_t, clone, backtrace_t*,
+ private_backtrace_t *this)
+{
+ private_backtrace_t *clone;
+
+ clone = malloc(sizeof(private_backtrace_t) +
+ this->frame_count * sizeof(void*));
+ memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*));
+ clone->frame_count = this->frame_count;
+
+ clone->public = get_methods();
+
+ return &clone->public;
+}
+
METHOD(backtrace_t, destroy, void,
private_backtrace_t *this)
{
free(this);
}
+#ifdef HAVE_LIBUNWIND_H
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+/**
+ * libunwind variant for glibc backtrace()
+ */
+static inline int backtrace_unwind(void **frames, int count)
+{
+ unw_context_t context;
+ unw_cursor_t cursor;
+ unw_word_t ip;
+ int depth = 0;
+
+ unw_getcontext(&context);
+ unw_init_local(&cursor, &context);
+ do
+ {
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ frames[depth++] = (void*)ip;
+ }
+ while (depth < count && unw_step(&cursor) > 0);
+
+ return depth;
+}
+#endif /* HAVE_UNWIND */
+
+/**
+ * Get implementation methods of backtrace_t
+ */
+static backtrace_t get_methods()
+{
+ return (backtrace_t) {
+ .log = _log_,
+ .contains_function = _contains_function,
+ .equals = _equals,
+ .clone = _clone,
+ .create_frame_enumerator = _create_frame_enumerator,
+ .destroy = _destroy,
+ };
+}
+
/**
* See header
*/
@@ -527,7 +608,9 @@ backtrace_t *backtrace_create(int skip)
void *frames[50];
int frame_count = 0;
-#ifdef HAVE_BACKTRACE
+#ifdef HAVE_LIBUNWIND_H
+ frame_count = backtrace_unwind(frames, countof(frames));
+#elif defined(HAVE_BACKTRACE)
frame_count = backtrace(frames, countof(frames));
#endif /* HAVE_BACKTRACE */
frame_count = max(frame_count - skip, 0);
@@ -535,13 +618,7 @@ backtrace_t *backtrace_create(int skip)
memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
this->frame_count = frame_count;
- this->public = (backtrace_t) {
- .log = _log_,
- .contains_function = _contains_function,
- .equals = _equals,
- .create_frame_enumerator = _create_frame_enumerator,
- .destroy = _destroy,
- };
+ this->public = get_methods();
return &this->public;
}
diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h
index 62104238d..416f58898 100644
--- a/src/libstrongswan/utils/backtrace.h
+++ b/src/libstrongswan/utils/backtrace.h
@@ -59,6 +59,14 @@ struct backtrace_t {
* @return TRUE if backtraces are equal
*/
bool (*equals)(backtrace_t *this, backtrace_t *other);
+
+ /**
+ * Create a copy of this backtrace.
+ *
+ * @return cloned copy
+ */
+ backtrace_t* (*clone)(backtrace_t *this);
+
/**
* Create an enumerator over the stack frame addresses.
*
diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c
index 44a14496c..c5e90b6c3 100644
--- a/src/libstrongswan/utils/capabilities.c
+++ b/src/libstrongswan/utils/capabilities.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
@@ -76,8 +76,116 @@ struct private_capabilities_t {
#endif
};
-METHOD(capabilities_t, keep, void,
- private_capabilities_t *this, u_int cap)
+/**
+ * Returns TRUE if the current process/user is member of the given group
+ */
+static bool has_group(gid_t group)
+{
+ gid_t *groups;
+ long ngroups, i;
+ bool found = FALSE;
+
+ if (group == getegid())
+ { /* it's unspecified if this is part of the list below or not */
+ return TRUE;
+ }
+ ngroups = sysconf(_SC_NGROUPS_MAX);
+ if (ngroups == -1)
+ {
+ DBG1(DBG_LIB, "getting groups for current process failed: %s",
+ strerror(errno));
+ return FALSE;
+ }
+ groups = calloc(ngroups + 1, sizeof(gid_t));
+ ngroups = getgroups(ngroups, groups);
+ if (ngroups == -1)
+ {
+ DBG1(DBG_LIB, "getting groups for current process failed: %s",
+ strerror(errno));
+ free(groups);
+ return FALSE;
+ }
+ for (i = 0; i < ngroups; i++)
+ {
+ if (group == groups[i])
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ free(groups);
+ return found;
+}
+
+/**
+ * Verify that the current process has the given capability
+ */
+static bool has_capability(private_capabilities_t *this, u_int cap,
+ bool *ignore)
+{
+ if (cap == CAP_CHOWN)
+ { /* if new files/UNIX sockets are created they should be owned by the
+ * configured user and group. This requires a call to chown(2). But
+ * CAP_CHOWN is not always required. */
+ if (!this->uid || geteuid() == this->uid)
+ { /* if the owner does not change CAP_CHOWN is not needed */
+ if (!this->gid || has_group(this->gid))
+ { /* the same applies if the owner is a member of the group */
+ if (ignore)
+ { /* we don't have to keep this, if requested */
+ *ignore = TRUE;
+ }
+ return TRUE;
+ }
+ }
+ }
+#ifndef CAPABILITIES
+ /* if we can't check the actual capabilities assume only root has it */
+ return geteuid() == 0;
+#endif /* !CAPABILITIES */
+#ifdef CAPABILITIES_LIBCAP
+ cap_flag_value_t val;
+ cap_t caps;
+ bool ok;
+
+ caps = cap_get_proc();
+ if (!caps)
+ {
+ return FALSE;
+ }
+ ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET;
+ cap_free(caps);
+ return ok;
+#endif /* CAPABILITIES_LIBCAP */
+#ifdef CAPABILITIES_NATIVE
+ struct __user_cap_header_struct header = {
+#if defined(_LINUX_CAPABILITY_VERSION_3)
+ .version = _LINUX_CAPABILITY_VERSION_3,
+#elif defined(_LINUX_CAPABILITY_VERSION_2)
+ .version = _LINUX_CAPABILITY_VERSION_2,
+#elif defined(_LINUX_CAPABILITY_VERSION_1)
+ .version = _LINUX_CAPABILITY_VERSION_1,
+#else
+ .version = _LINUX_CAPABILITY_VERSION,
+#endif
+ };
+ struct __user_cap_data_struct caps[2];
+ int i = 0;
+
+ if (cap >= 32)
+ {
+ i++;
+ cap -= 32;
+ }
+ return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap);
+#endif /* CAPABILITIES_NATIVE */
+}
+
+/**
+ * Keep the given capability if it is held by the current process. Returns
+ * FALSE, if this is not the case.
+ */
+static bool keep_capability(private_capabilities_t *this, u_int cap)
{
#ifdef CAPABILITIES_LIBCAP
cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
@@ -96,18 +204,41 @@ METHOD(capabilities_t, keep, void,
this->caps[i].permitted |= 1 << cap;
this->caps[i].inheritable |= 1 << cap;
#endif /* CAPABILITIES_NATIVE */
+ return TRUE;
+}
+
+METHOD(capabilities_t, keep, bool,
+ private_capabilities_t *this, u_int cap)
+{
+ bool ignore = FALSE;
+
+ if (!has_capability(this, cap, &ignore))
+ {
+ return FALSE;
+ }
+ else if (ignore)
+ { /* don't keep capabilities that are not required */
+ return TRUE;
+ }
+ return keep_capability(this, cap);
+}
+
+METHOD(capabilities_t, check, bool,
+ private_capabilities_t *this, u_int cap)
+{
+ return has_capability(this, cap, NULL);
}
METHOD(capabilities_t, get_uid, uid_t,
private_capabilities_t *this)
{
- return this->uid;
+ return this->uid ?: geteuid();
}
METHOD(capabilities_t, get_gid, gid_t,
private_capabilities_t *this)
{
- return this->gid;
+ return this->gid ?: getegid();
}
METHOD(capabilities_t, set_uid, void,
@@ -225,7 +356,7 @@ METHOD(capabilities_t, drop, bool,
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
#endif
- if (!init_supplementary_groups(this))
+ if (this->uid && !init_supplementary_groups(this))
{
DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
this->uid);
@@ -271,7 +402,7 @@ METHOD(capabilities_t, drop, bool,
#endif /* CAPABILITIES_NATIVE */
#ifdef CAPABILITIES
DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
- this->uid, this->gid);
+ geteuid(), getegid());
#endif /* CAPABILITIES */
return TRUE;
}
@@ -298,6 +429,7 @@ capabilities_t *capabilities_create()
INIT(this,
.public = {
.keep = _keep,
+ .check = _check,
.get_uid = _get_uid,
.get_gid = _get_gid,
.set_uid = _set_uid,
@@ -309,15 +441,9 @@ capabilities_t *capabilities_create()
},
);
-#ifdef CAPABILITIES
#ifdef CAPABILITIES_LIBCAP
this->caps = cap_init();
#endif /* CAPABILITIES_LIBCAP */
- if (lib->leak_detective)
- {
- keep(this, CAP_SYS_NICE);
- }
-#endif /* CAPABILITIES */
#ifdef EMULATE_R_FUNCS
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h
index cd23cbf10..fe11a4dfc 100644
--- a/src/libstrongswan/utils/capabilities.h
+++ b/src/libstrongswan/utils/capabilities.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
*
@@ -21,6 +23,8 @@
#ifndef CAPABILITIES_H_
#define CAPABILITIES_H_
+typedef struct capabilities_t capabilities_t;
+
#include <library.h>
#ifdef HAVE_SYS_CAPABILITY_H
# include <sys/capability.h>
@@ -28,7 +32,18 @@
# include <linux/capability.h>
#endif
-typedef struct capabilities_t capabilities_t;
+#ifndef CAP_CHOWN
+# define CAP_CHOWN 0
+#endif
+#ifndef CAP_NET_BIND_SERVICE
+# define CAP_NET_BIND_SERVICE 10
+#endif
+#ifndef CAP_NET_ADMIN
+# define CAP_NET_ADMIN 12
+#endif
+#ifndef CAP_NET_RAW
+# define CAP_NET_RAW 13
+#endif
/**
* POSIX capability dropping abstraction layer.
@@ -36,11 +51,26 @@ typedef struct capabilities_t capabilities_t;
struct capabilities_t {
/**
- * Register a capability to keep while calling drop().
+ * Register a capability to keep while calling drop(). Verifies that the
+ * capability is currently held.
+ *
+ * @note CAP_CHOWN is handled specially as it might not be required.
*
* @param cap capability to keep
+ * @return FALSE if the capability is currently not held
+ */
+ bool (*keep)(capabilities_t *this,
+ u_int cap) __attribute__((warn_unused_result));
+
+ /**
+ * Check if the given capability is currently held.
+ *
+ * @note CAP_CHOWN is handled specially as it might not be required.
+ *
+ * @param cap capability to check
+ * @return TRUE if the capability is currently held
*/
- void (*keep)(capabilities_t *this, u_int cap);
+ bool (*check)(capabilities_t *this, u_int cap);
/**
* Get the user ID set through set_uid/resolve_uid.
diff --git a/src/libstrongswan/utils/chunk.c b/src/libstrongswan/utils/chunk.c
index d7f1c31d9..04f3eea7d 100644
--- a/src/libstrongswan/utils/chunk.c
+++ b/src/libstrongswan/utils/chunk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -16,24 +16,17 @@
*/
#include <stdio.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <pthread.h>
#include <ctype.h>
#include "chunk.h"
#include "debug.h"
-/* required for chunk_hash */
-#undef get16bits
-#if (defined(__GNUC__) && defined(__i386__))
-#define get16bits(d) (*((const u_int16_t*)(d)))
-#endif
-#if !defined (get16bits)
-#define get16bits(d) ((((u_int32_t)(((const u_int8_t*)(d))[1])) << 8)\
- + (u_int32_t)(((const u_int8_t*)(d))[0]) )
-#endif
-
/**
* Empty chunk.
*/
@@ -579,72 +572,193 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace)
}
/**
- * Described in header.
- *
- * The implementation is based on Paul Hsieh's SuperFastHash:
- * http://www.azillionmonkeys.com/qed/hash.html
+ * Helper functions for chunk_mac()
*/
-u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
+static inline u_int64_t sipget(u_char *in)
{
- u_char *data = chunk.ptr;
- size_t len = chunk.len;
- u_int32_t tmp;
- int rem;
+ u_int64_t v = 0;
+ int i;
- if (!len || data == NULL)
+ for (i = 0; i < 64; i += 8, ++in)
{
- return 0;
+ v |= ((u_int64_t)*in) << i;
}
+ return v;
+}
- rem = len & 3;
- len >>= 2;
+static inline u_int64_t siprotate(u_int64_t v, int shift)
+{
+ return (v << shift) | (v >> (64 - shift));
+}
- /* Main loop */
- for (; len > 0; --len)
- {
- hash += get16bits(data);
- tmp = (get16bits(data + 2) << 11) ^ hash;
- hash = (hash << 16) ^ tmp;
- data += 2 * sizeof(u_int16_t);
- hash += hash >> 11;
- }
+static inline void sipround(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2,
+ u_int64_t *v3)
+{
+ *v0 += *v1;
+ *v1 = siprotate(*v1, 13);
+ *v1 ^= *v0;
+ *v0 = siprotate(*v0, 32);
+
+ *v2 += *v3;
+ *v3 = siprotate(*v3, 16);
+ *v3 ^= *v2;
+
+ *v2 += *v1;
+ *v1 = siprotate(*v1, 17);
+ *v1 ^= *v2;
+ *v2 = siprotate(*v2, 32);
+
+ *v0 += *v3;
+ *v3 = siprotate(*v3, 21);
+ *v3 ^= *v0;
+}
+
+static inline void sipcompress(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2,
+ u_int64_t *v3, u_int64_t m)
+{
+ *v3 ^= m;
+ sipround(v0, v1, v2, v3);
+ sipround(v0, v1, v2, v3);
+ *v0 ^= m;
+}
- /* Handle end cases */
+static inline u_int64_t siplast(size_t len, u_char *pos)
+{
+ u_int64_t b;
+ int rem = len & 7;
+
+ b = ((u_int64_t)len) << 56;
switch (rem)
{
+ case 7:
+ b |= ((u_int64_t)pos[6]) << 48;
+ case 6:
+ b |= ((u_int64_t)pos[5]) << 40;
+ case 5:
+ b |= ((u_int64_t)pos[4]) << 32;
+ case 4:
+ b |= ((u_int64_t)pos[3]) << 24;
case 3:
- {
- hash += get16bits(data);
- hash ^= hash << 16;
- hash ^= data[sizeof(u_int16_t)] << 18;
- hash += hash >> 11;
- break;
- }
+ b |= ((u_int64_t)pos[2]) << 16;
case 2:
- {
- hash += get16bits(data);
- hash ^= hash << 11;
- hash += hash >> 17;
+ b |= ((u_int64_t)pos[1]) << 8;
+ case 1:
+ b |= ((u_int64_t)pos[0]);
+ break;
+ case 0:
break;
+ }
+ return b;
+}
+
+/**
+ * Caculate SipHash-2-4 with an optional first block given as argument.
+ */
+static u_int64_t chunk_mac_inc(chunk_t chunk, u_char *key, u_int64_t m)
+{
+ u_int64_t v0, v1, v2, v3, k0, k1;
+ size_t len = chunk.len;
+ u_char *pos = chunk.ptr, *end;
+
+ end = chunk.ptr + len - (len % 8);
+
+ k0 = sipget(key);
+ k1 = sipget(key + 8);
+
+ v0 = k0 ^ 0x736f6d6570736575ULL;
+ v1 = k1 ^ 0x646f72616e646f6dULL;
+ v2 = k0 ^ 0x6c7967656e657261ULL;
+ v3 = k1 ^ 0x7465646279746573ULL;
+
+ if (m)
+ {
+ sipcompress(&v0, &v1, &v2, &v3, m);
+ }
+
+ /* compression with c = 2 */
+ for (; pos != end; pos += 8)
+ {
+ m = sipget(pos);
+ sipcompress(&v0, &v1, &v2, &v3, m);
+ }
+ sipcompress(&v0, &v1, &v2, &v3, siplast(len, pos));
+
+ /* finalization with d = 4 */
+ v2 ^= 0xff;
+ sipround(&v0, &v1, &v2, &v3);
+ sipround(&v0, &v1, &v2, &v3);
+ sipround(&v0, &v1, &v2, &v3);
+ sipround(&v0, &v1, &v2, &v3);
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+/**
+ * Described in header.
+ */
+u_int64_t chunk_mac(chunk_t chunk, u_char *key)
+{
+ return chunk_mac_inc(chunk, key, 0);
+}
+
+/**
+ * Secret key allocated randomly during first use.
+ */
+static u_char key[16];
+
+/**
+ * Static key used in case predictable hash values are required.
+ */
+static u_char static_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+
+/**
+ * Only allocate the key once
+ */
+static pthread_once_t key_allocated = PTHREAD_ONCE_INIT;
+
+/**
+ * Allocate a key on first use, we do this manually to avoid dependencies on
+ * plugins.
+ */
+static void allocate_key()
+{
+ ssize_t len;
+ size_t done = 0;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0)
+ {
+ while (done < sizeof(key))
+ {
+ len = read(fd, key + done, sizeof(key) - done);
+ if (len < 0)
+ {
+ break;
+ }
+ done += len;
}
- case 1:
+ close(fd);
+ }
+ /* on error we use random() to generate the key (better than nothing) */
+ if (done < sizeof(key))
+ {
+ srandom(time(NULL) + getpid());
+ for (; done < sizeof(key); done++)
{
- hash += *data;
- hash ^= hash << 10;
- hash += hash >> 1;
- break;
+ key[done] = (u_char)random();
}
}
+}
- /* Force "avalanching" of final 127 bits */
- hash ^= hash << 3;
- hash += hash >> 5;
- hash ^= hash << 4;
- hash += hash >> 17;
- hash ^= hash << 25;
- hash += hash >> 6;
-
- return hash;
+/**
+ * Described in header.
+ */
+u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
+{
+ pthread_once(&key_allocated, allocate_key);
+ /* we could use a mac of the previous hash, but this is faster */
+ return chunk_mac_inc(chunk, key, ((u_int64_t)hash) << 32 | hash);
}
/**
@@ -652,7 +766,24 @@ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
*/
u_int32_t chunk_hash(chunk_t chunk)
{
- return chunk_hash_inc(chunk, chunk.len);
+ pthread_once(&key_allocated, allocate_key);
+ return chunk_mac(chunk, key);
+}
+
+/**
+ * Described in header.
+ */
+u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash)
+{ /* we could use a mac of the previous hash, but this is faster */
+ return chunk_mac_inc(chunk, static_key, ((u_int64_t)hash) << 32 | hash);
+}
+
+/**
+ * Described in header.
+ */
+u_int32_t chunk_hash_static(chunk_t chunk)
+{
+ return chunk_mac(chunk, static_key);
}
/**
diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h
index bc14b7394..34ba77357 100644
--- a/src/libstrongswan/utils/chunk.h
+++ b/src/libstrongswan/utils/chunk.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -191,9 +191,9 @@ static inline void chunk_clear(chunk_t *chunk)
#define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing))
/**
- * Initialize a chunk from a static string, not containing 0-terminator
+ * Initialize a chunk from a string, not containing 0-terminator
*/
-#define chunk_from_str(str) chunk_create(str, strlen(str))
+#define chunk_from_str(str) ({char *x = (str); chunk_create(x, strlen(x));})
/**
* Allocate a chunk on the heap
@@ -301,16 +301,68 @@ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace);
/**
* Computes a 32 bit hash of the given chunk.
- * Note: This hash is only intended for hash tables not for cryptographic purposes.
+ *
+ * @note The output of this function is randomized, that is, it will only
+ * produce the same output for the same input when calling it from the same
+ * process. For a more predictable hash function use chunk_hash_static()
+ * instead.
+ *
+ * @note This hash is only intended for hash tables not for cryptographic
+ * purposes.
+ *
+ * @param chunk data to hash
+ * @return hash value
*/
u_int32_t chunk_hash(chunk_t chunk);
/**
* Incremental version of chunk_hash. Use this to hash two or more chunks.
+ *
+ * @param chunk data to hash
+ * @param hash previous hash value
+ * @return hash value
*/
u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash);
/**
+ * Computes a 32 bit hash of the given chunk.
+ *
+ * Compared to chunk_hash() this will always calculate the same output for the
+ * same input. Therefore, it should not be used for hash tables (to prevent
+ * hash flooding).
+ *
+ * @note This hash is not intended for cryptographic purposes.
+ *
+ * @param chunk data to hash
+ * @return hash value
+ */
+u_int32_t chunk_hash_static(chunk_t chunk);
+
+/**
+ * Incremental version of chunk_hash_static(). Use this to hash two or more
+ * chunks in a predictable way.
+ *
+ * @param chunk data to hash
+ * @param hash previous hash value
+ * @return hash value
+ */
+u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash);
+
+/**
+ * Computes a quick MAC from the given chunk and key using SipHash.
+ *
+ * The key must have a length of 128-bit (16 bytes).
+ *
+ * @note While SipHash has strong features using it for cryptographic purposes
+ * is not recommended (in particular because of the rather short output size).
+ *
+ * @param chunk data to process
+ * @param key key to use
+ * @return MAC for given input and key
+ */
+u_int64_t chunk_mac(chunk_t chunk, u_char *key);
+
+/**
* printf hook function for chunk_t.
*
* Arguments are:
diff --git a/src/libstrongswan/utils/enum.c b/src/libstrongswan/utils/enum.c
index 9b3c4d566..3db9a34e0 100644
--- a/src/libstrongswan/utils/enum.c
+++ b/src/libstrongswan/utils/enum.c
@@ -47,7 +47,7 @@ int enum_from_name(enum_name_t *e, char *name)
for (i = 0; i < count; i++)
{
- if (strcaseeq(name, e->names[i]))
+ if (name && strcaseeq(name, e->names[i]))
{
return e->first + i;
}
diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c
index 4176320dc..5df3e5fe2 100644
--- a/src/libstrongswan/utils/identification.c
+++ b/src/libstrongswan/utils/identification.c
@@ -277,6 +277,23 @@ METHOD(identification_t, create_part_enumerator, enumerator_t*,
}
/**
+ * Print a separator between two RDNs
+ */
+static inline bool print_separator(char **buf, size_t *len)
+{
+ int written;
+
+ written = snprintf(*buf, *len, ", ");
+ if (written < 0 || written >= *len)
+ {
+ return FALSE;
+ }
+ *buf += written;
+ *len -= written;
+ return TRUE;
+}
+
+/**
* Print a DN with all its RDN in a buffer to present it to the user
*/
static void dntoa(chunk_t dn, char *buf, size_t len)
@@ -292,8 +309,14 @@ static void dntoa(chunk_t dn, char *buf, size_t len)
{
empty = FALSE;
- oid = asn1_known_oid(oid_data);
+ /* previous RDN was empty but it wasn't the last one */
+ if (finished && !print_separator(&buf, &len))
+ {
+ break;
+ }
+ finished = FALSE;
+ oid = asn1_known_oid(oid_data);
if (oid == OID_UNKNOWN)
{
written = snprintf(buf, len, "%#B=", &oid_data);
@@ -319,21 +342,19 @@ static void dntoa(chunk_t dn, char *buf, size_t len)
buf += written;
len -= written;
- if (data.ptr + data.len != dn.ptr + dn.len)
- {
- written = snprintf(buf, len, ", ");
- if (written < 0 || written >= len)
- {
- break;
- }
- buf += written;
- len -= written;
+ if (!data.ptr)
+ { /* we can't calculate if we're finished, assume we are */
+ finished = TRUE;
}
- else
+ else if (data.ptr + data.len == dn.ptr + dn.len)
{
finished = TRUE;
break;
}
+ else if (!print_separator(&buf, &len))
+ {
+ break;
+ }
}
if (empty)
{
@@ -377,7 +398,7 @@ static status_t atodn(char *src, chunk_t *dn)
switch (state)
{
case SEARCH_OID:
- if (*src != ' ' && *src != '/' && *src != ',')
+ if (*src != ' ' && *src != '/' && *src != ',' && *src != '\0')
{
oid.ptr = src;
oid.len = 1;
@@ -418,7 +439,7 @@ static status_t atodn(char *src, chunk_t *dn)
{
break;
}
- else if (*src != ',' && *src != '/')
+ else if (*src != ',' && *src != '/' && *src != '\0')
{
name.ptr = src;
name.len = 1;
@@ -481,6 +502,11 @@ static status_t atodn(char *src, chunk_t *dn)
}
} while (*src++ != '\0');
+ if (state == READ_OID)
+ { /* unterminated OID */
+ status = INVALID_ARG;
+ }
+
/* build the distinguished name sequence */
{
int i;
@@ -493,7 +519,6 @@ static status_t atodn(char *src, chunk_t *dn)
free(rdns[i].ptr);
}
}
-
if (status != SUCCESS)
{
free(dn->ptr);
@@ -799,7 +824,7 @@ int identification_printf_hook(printf_hook_data_t *data,
dntoa(this->encoded, buf, sizeof(buf));
break;
case ID_DER_ASN1_GN:
- snprintf(buf, sizeof(buf), "(ASN.1 general Name");
+ snprintf(buf, sizeof(buf), "(ASN.1 general name)");
break;
case ID_KEY_ID:
if (chunk_printable(this->encoded, NULL, '?') &&
@@ -915,14 +940,15 @@ identification_t *identification_create_from_string(char *string)
else
{
this = identification_create(ID_KEY_ID);
- this->encoded = chunk_clone(chunk_create(string, strlen(string)));
+ this->encoded = chunk_from_str(strdup(string));
}
return &this->public;
}
else if (strchr(string, '@') == NULL)
{
- if (streq(string, "%any")
- || streq(string, "%any6")
+ if (streq(string, "")
+ || streq(string, "%any")
+ || streq(string, "%any6")
|| streq(string, "0.0.0.0")
|| streq(string, "*")
|| streq(string, "::")
@@ -947,11 +973,7 @@ identification_t *identification_create_from_string(char *string)
else
{ /* not IPv4, mostly FQDN */
this = identification_create(ID_FQDN);
- this->encoded.len = strlen(string);
- if (this->encoded.len)
- {
- this->encoded.ptr = strdup(string);
- }
+ this->encoded = chunk_from_str(strdup(string));
}
return &this->public;
}
@@ -968,11 +990,7 @@ identification_t *identification_create_from_string(char *string)
else
{ /* not IPv4/6 fallback to KEY_ID */
this = identification_create(ID_KEY_ID);
- this->encoded.len = strlen(string);
- if (this->encoded.len)
- {
- this->encoded.ptr = strdup(string);
- }
+ this->encoded = chunk_from_str(strdup(string));
}
return &this->public;
}
@@ -982,34 +1000,30 @@ identification_t *identification_create_from_string(char *string)
{
if (*string == '@')
{
- if (*(string + 1) == '#')
+ string++;
+ if (*string == '#')
{
this = identification_create(ID_KEY_ID);
- string += 2;
- this->encoded = chunk_from_hex(
- chunk_create(string, strlen(string)), NULL);
+ this->encoded = chunk_from_hex(chunk_from_str(string + 1), NULL);
+ return &this->public;
+ }
+ else if (*string == '@')
+ {
+ this = identification_create(ID_USER_FQDN);
+ this->encoded = chunk_clone(chunk_from_str(string + 1));
return &this->public;
}
else
{
this = identification_create(ID_FQDN);
- string += 1;
- this->encoded.len = strlen(string);
- if (this->encoded.len)
- {
- this->encoded.ptr = strdup(string);
- }
+ this->encoded = chunk_clone(chunk_from_str(string));
return &this->public;
}
}
else
{
this = identification_create(ID_RFC822_ADDR);
- this->encoded.len = strlen(string);
- if (this->encoded.len)
- {
- this->encoded.ptr = strdup(string);
- }
+ this->encoded = chunk_from_str(strdup(string));
return &this->public;
}
}
@@ -1079,4 +1093,3 @@ identification_t *identification_create_from_sockaddr(sockaddr_t *sockaddr)
}
}
}
-
diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h
index 00d740765..e62446879 100644
--- a/src/libstrongswan/utils/identification.h
+++ b/src/libstrongswan/utils/identification.h
@@ -241,7 +241,6 @@ struct identification_t {
* no match at all, 1 means a bad match, and 2 a slightly better match.
*
* @param other the ID containing one or more wildcards
- * @param wildcards returns the number of wildcards, may be NULL
* @return match value as described above
*/
id_match_t (*matches) (identification_t *this, identification_t *other);
diff --git a/src/libstrongswan/utils/integrity_checker.c b/src/libstrongswan/utils/integrity_checker.c
index e962aba70..d59a76232 100644
--- a/src/libstrongswan/utils/integrity_checker.c
+++ b/src/libstrongswan/utils/integrity_checker.c
@@ -91,7 +91,7 @@ METHOD(integrity_checker_t, build_file, u_int32_t,
*len = sb.st_size;
contents = chunk_create(addr, sb.st_size);
- checksum = chunk_hash(contents);
+ checksum = chunk_hash_static(contents);
munmap(addr, sb.st_size);
close(fd);
@@ -153,7 +153,7 @@ METHOD(integrity_checker_t, build_segment, u_int32_t,
segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase);
*len = segment.len;
- return chunk_hash(segment);
+ return chunk_hash_static(segment);
}
/**
diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c
index 6bf4d63cd..ffbc62085 100644
--- a/src/libstrongswan/utils/leak_detective.c
+++ b/src/libstrongswan/utils/leak_detective.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2006-2008 Martin Willi
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2006-2013 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -14,20 +15,29 @@
*/
#define _GNU_SOURCE
-#include <sched.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
-#include <malloc.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <syslog.h>
-#include <pthread.h>
#include <netdb.h>
#include <locale.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef __APPLE__
+#include <sys/mman.h>
+#include <malloc/malloc.h>
+/* overload some of our types clashing with mach */
+#define host_t strongswan_host_t
+#define processor_t strongswan_processor_t
+#define thread_t strongswan_thread_t
+#endif /* __APPLE__ */
#include "leak_detective.h"
@@ -35,6 +45,8 @@
#include <utils/debug.h>
#include <utils/backtrace.h>
#include <collections/hashtable.h>
+#include <threading/thread_value.h>
+#include <threading/spinlock.h>
typedef struct private_leak_detective_t private_leak_detective_t;
@@ -69,21 +81,6 @@ struct private_leak_detective_t {
*/
#define MEMORY_ALLOC_PATTERN 0xEE
-
-static void install_hooks(void);
-static void uninstall_hooks(void);
-static void *malloc_hook(size_t, const void *);
-static void *realloc_hook(void *, size_t, const void *);
-static void free_hook(void*, const void *);
-
-void *(*old_malloc_hook)(size_t, const void *);
-void *(*old_realloc_hook)(void *, size_t, const void *);
-void (*old_free_hook)(void*, const void *);
-
-static u_int count_malloc = 0;
-static u_int count_free = 0;
-static u_int count_realloc = 0;
-
typedef struct memory_header_t memory_header_t;
typedef struct memory_tail_t memory_tail_t;
@@ -108,6 +105,11 @@ struct memory_header_t {
backtrace_t *backtrace;
/**
+ * Padding to make sizeof(memory_header_t) == 32
+ */
+ u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0];
+
+ /**
* Number of bytes following after the header
*/
u_int32_t bytes;
@@ -136,50 +138,337 @@ struct memory_tail_t {
* the others on it...
*/
static memory_header_t first_header = {
- magic: MEMORY_HEADER_MAGIC,
- bytes: 0,
- backtrace: NULL,
- previous: NULL,
- next: NULL
+ .magic = MEMORY_HEADER_MAGIC,
};
/**
- * are the hooks currently installed?
+ * Spinlock to access header linked list
+ */
+static spinlock_t *lock;
+
+/**
+ * Is leak detection currently enabled?
+ */
+static bool enabled = FALSE;
+
+/**
+ * Is leak detection disabled for the current thread?
*/
-static bool installed = FALSE;
+static thread_value_t *thread_disabled;
/**
* Installs the malloc hooks, enables leak detection
*/
-static void install_hooks()
+static void enable_leak_detective()
+{
+ enabled = TRUE;
+}
+
+/**
+ * Uninstalls the malloc hooks, disables leak detection
+ */
+static void disable_leak_detective()
+{
+ enabled = FALSE;
+}
+
+/**
+ * Enable/Disable leak detective for the current thread
+ *
+ * @return Previous value
+ */
+static bool enable_thread(bool enable)
+{
+ bool before;
+
+ before = thread_disabled->get(thread_disabled) == NULL;
+ thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
+ return before;
+}
+
+/**
+ * Add a header to the beginning of the list
+ */
+static void add_hdr(memory_header_t *hdr)
{
- if (!installed)
+ lock->lock(lock);
+ hdr->next = first_header.next;
+ if (hdr->next)
{
- old_malloc_hook = __malloc_hook;
- old_realloc_hook = __realloc_hook;
- old_free_hook = __free_hook;
- __malloc_hook = malloc_hook;
- __realloc_hook = realloc_hook;
- __free_hook = free_hook;
- installed = TRUE;
+ hdr->next->previous = hdr;
}
+ hdr->previous = &first_header;
+ first_header.next = hdr;
+ lock->unlock(lock);
}
/**
- * Uninstalls the malloc hooks, disables leak detection
+ * Remove a header from the list
+ */
+static void remove_hdr(memory_header_t *hdr)
+{
+ lock->lock(lock);
+ if (hdr->next)
+ {
+ hdr->next->previous = hdr->previous;
+ }
+ hdr->previous->next = hdr->next;
+ lock->unlock(lock);
+}
+
+/**
+ * Check if a header is in the list
+ */
+static bool has_hdr(memory_header_t *hdr)
+{
+ memory_header_t *current;
+ bool found = FALSE;
+
+ lock->lock(lock);
+ for (current = &first_header; current != NULL; current = current->next)
+ {
+ if (current == hdr)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ lock->unlock(lock);
+
+ return found;
+}
+
+#ifdef __APPLE__
+
+/**
+ * Copy of original default zone, with functions we call in hooks
+ */
+static malloc_zone_t original;
+
+/**
+ * Call original malloc()
+ */
+static void* real_malloc(size_t size)
+{
+ return original.malloc(malloc_default_zone(), size);
+}
+
+/**
+ * Call original free()
+ */
+static void real_free(void *ptr)
+{
+ original.free(malloc_default_zone(), ptr);
+}
+
+/**
+ * Call original realloc()
+ */
+static void* real_realloc(void *ptr, size_t size)
+{
+ return original.realloc(malloc_default_zone(), ptr, size);
+}
+
+/**
+ * Hook definition: static function with _hook suffix, takes additional zone
+ */
+#define HOOK(ret, name, ...) \
+ static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
+
+/**
+ * forward declaration of hooks
+ */
+HOOK(void*, malloc, size_t bytes);
+HOOK(void*, calloc, size_t nmemb, size_t size);
+HOOK(void*, valloc, size_t size);
+HOOK(void, free, void *ptr);
+HOOK(void*, realloc, void *old, size_t bytes);
+
+/**
+ * malloc zone size(), must consider the memory header prepended
+ */
+HOOK(size_t, size, const void *ptr)
+{
+ bool before;
+ size_t size;
+
+ if (enabled)
+ {
+ before = enable_thread(FALSE);
+ if (before)
+ {
+ ptr -= sizeof(memory_header_t);
+ }
+ }
+ size = original.size(malloc_default_zone(), ptr);
+ if (enabled)
+ {
+ enable_thread(before);
+ }
+ return size;
+}
+
+/**
+ * Version of malloc zones we currently support
+ */
+#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
+
+/**
+ * Hook-in our malloc functions into the default zone
+ */
+static bool register_hooks()
+{
+ malloc_zone_t *zone;
+ void *page;
+
+ zone = malloc_default_zone();
+ if (zone->version != MALLOC_ZONE_VERSION)
+ {
+ DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
+ zone->version, MALLOC_ZONE_VERSION);
+ return FALSE;
+ }
+
+ original = *zone;
+
+ page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
+ if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
+ {
+ DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
+ return FALSE;
+ }
+
+ zone->size = size_hook;
+ zone->malloc = malloc_hook;
+ zone->calloc = calloc_hook;
+ zone->valloc = valloc_hook;
+ zone->free = free_hook;
+ zone->realloc = realloc_hook;
+
+ /* those other functions can be NULLed out to not use them */
+ zone->batch_malloc = NULL;
+ zone->batch_free = NULL;
+ zone->memalign = NULL;
+ zone->free_definite_size = NULL;
+
+ return TRUE;
+}
+
+#else /* !__APPLE__ */
+
+/**
+ * dlsym() might do a malloc(), but we can't do one before we get the malloc()
+ * function pointer. Use this minimalistic malloc implementation instead.
+ */
+static void* malloc_for_dlsym(size_t size)
+{
+ static char buf[1024] = {};
+ static size_t used = 0;
+ char *ptr;
+
+ /* roundup to a multiple of 32 */
+ size = (size - 1) / 32 * 32 + 32;
+
+ if (used + size > sizeof(buf))
+ {
+ return NULL;
+ }
+ ptr = buf + used;
+ used += size;
+ return ptr;
+}
+
+/**
+ * Lookup a malloc function, while disabling wrappers
*/
-static void uninstall_hooks()
+static void* get_malloc_fn(char *name)
{
- if (installed)
+ bool before = FALSE;
+ void *fn;
+
+ if (enabled)
+ {
+ before = enable_thread(FALSE);
+ }
+ fn = dlsym(RTLD_NEXT, name);
+ if (enabled)
+ {
+ enable_thread(before);
+ }
+ return fn;
+}
+
+/**
+ * Call original malloc()
+ */
+static void* real_malloc(size_t size)
+{
+ static void* (*fn)(size_t size);
+ static int recursive = 0;
+
+ if (!fn)
{
- __malloc_hook = old_malloc_hook;
- __free_hook = old_free_hook;
- __realloc_hook = old_realloc_hook;
- installed = FALSE;
+ /* checking recursiveness should actually be thread-specific. But as
+ * it is very likely that the first allocation is done before we go
+ * multi-threaded, we keep it simple. */
+ if (recursive)
+ {
+ return malloc_for_dlsym(size);
+ }
+ recursive++;
+ fn = get_malloc_fn("malloc");
+ recursive--;
}
+ return fn(size);
}
/**
+ * Call original free()
+ */
+static void real_free(void *ptr)
+{
+ static void (*fn)(void *ptr);
+
+ if (!fn)
+ {
+ fn = get_malloc_fn("free");
+ }
+ return fn(ptr);
+}
+
+/**
+ * Call original realloc()
+ */
+static void* real_realloc(void *ptr, size_t size)
+{
+ static void* (*fn)(void *ptr, size_t size);
+
+ if (!fn)
+ {
+ fn = get_malloc_fn("realloc");
+ }
+ return fn(ptr, size);
+}
+
+/**
+ * Hook definition: plain function overloading existing malloc calls
+ */
+#define HOOK(ret, name, ...) ret name(__VA_ARGS__)
+
+/**
+ * Hook initialization when not using hooks, resolve functions.
+ */
+static bool register_hooks()
+{
+ void *buf = real_malloc(8);
+ real_realloc(buf, 16);
+ real_free(buf);
+ return TRUE;
+}
+
+#endif /* !__APPLE__ */
+
+/**
* Leak report white list
*
* List of functions using static allocation buffers or should be suppressed
@@ -194,12 +483,6 @@ char *whitelist[] = {
"pthread_setspecific",
"__pthread_setspecific",
/* glibc functions */
- "mktime",
- "ctime",
- "__gmtime_r",
- "localtime_r",
- "tzset",
- "time_printf_hook",
"inet_ntoa",
"strerror",
"getprotobyname",
@@ -247,18 +530,16 @@ char *whitelist[] = {
"Curl_client_write",
/* ClearSilver */
"nerr_init",
- /* OpenSSL */
- "RSA_new_method",
- "DH_new_method",
- "ENGINE_load_builtin_engines",
- "OPENSSL_config",
- "ecdsa_check",
- "ERR_put_error",
/* libgcrypt */
"gcry_control",
"gcry_check_version",
"gcry_randomize",
"gcry_create_nonce",
+ /* OpenSSL: These are needed for unit-tests only, the openssl plugin
+ * does properly clean up any memory during destroy(). */
+ "ECDSA_do_sign_ex",
+ "ECDSA_verify",
+ "RSA_new_method",
/* NSPR */
"PR_CallOnce",
/* libapr */
@@ -277,6 +558,14 @@ char *whitelist[] = {
"gnutls_global_init",
};
+/**
+ * Some functions are hard to whitelist, as they don't use a symbol directly.
+ * Use some static initialization to suppress them on leak reports
+ */
+static void init_static_allocations()
+{
+ tzset();
+}
/**
* Hashtable hash function
@@ -309,7 +598,8 @@ static bool equals(backtrace_t *a, backtrace_t *b)
* Summarize and print backtraces
*/
static int print_traces(private_leak_detective_t *this,
- FILE *out, int thresh, bool detailed, int *whitelisted)
+ FILE *out, int thresh, int thresh_count,
+ bool detailed, int *whitelisted, size_t *sum)
{
int leaks = 0;
memory_header_t *hdr;
@@ -323,11 +613,13 @@ static int print_traces(private_leak_detective_t *this,
/** number of allocations */
u_int count;
} *entry;
+ bool before;
- uninstall_hooks();
+ before = enable_thread(FALSE);
entries = hashtable_create((hashtable_hash_t)hash,
(hashtable_equals_t)equals, 1024);
+ lock->lock(lock);
for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
{
if (whitelisted &&
@@ -346,29 +638,37 @@ static int print_traces(private_leak_detective_t *this,
else
{
INIT(entry,
- .backtrace = hdr->backtrace,
+ .backtrace = hdr->backtrace->clone(hdr->backtrace),
.bytes = hdr->bytes,
.count = 1,
);
- entries->put(entries, hdr->backtrace, entry);
+ entries->put(entries, entry->backtrace, entry);
+ }
+ if (sum)
+ {
+ *sum += hdr->bytes;
}
leaks++;
}
+ lock->unlock(lock);
enumerator = entries->create_enumerator(entries);
while (enumerator->enumerate(enumerator, NULL, &entry))
{
- if (!thresh || entry->bytes >= thresh)
+ if (out &&
+ (!thresh || entry->bytes >= thresh) &&
+ (!thresh_count || entry->count >= thresh_count))
{
fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
entry->bytes, entry->count, entry->bytes / entry->count);
entry->backtrace->log(entry->backtrace, out, detailed);
}
+ entry->backtrace->destroy(entry->backtrace);
free(entry);
}
enumerator->destroy(enumerator);
entries->destroy(entries);
- install_hooks();
+ enable_thread(before);
return leaks;
}
@@ -377,9 +677,10 @@ METHOD(leak_detective_t, report, void,
{
if (lib->leak_detective)
{
- int leaks = 0, whitelisted = 0;
+ int leaks, whitelisted = 0;
+ size_t sum = 0;
- leaks = print_traces(this, stderr, 0, detailed, &whitelisted);
+ leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum);
switch (leaks)
{
case 0:
@@ -389,7 +690,7 @@ METHOD(leak_detective_t, report, void,
fprintf(stderr, "One leak detected");
break;
default:
- fprintf(stderr, "%d leaks detected", leaks);
+ fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum);
break;
}
fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
@@ -400,114 +701,115 @@ METHOD(leak_detective_t, report, void,
}
}
-METHOD(leak_detective_t, set_state, bool,
- private_leak_detective_t *this, bool enable)
+METHOD(leak_detective_t, leaks, int,
+ private_leak_detective_t *this)
{
- static struct sched_param oldparams;
- static int oldpolicy;
- struct sched_param params;
- pthread_t thread_id;
-
- if (enable == installed)
- {
- return installed;
- }
- thread_id = pthread_self();
- if (enable)
- {
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
- }
- else
+ if (lib->leak_detective)
{
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
- uninstall_hooks();
+ int leaks, whitelisted = 0;
+
+ leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL);
+ return leaks;
}
- installed = enable;
- return !installed;
+ return 0;
+}
+
+METHOD(leak_detective_t, set_state, bool,
+ private_leak_detective_t *this, bool enable)
+{
+ return enable_thread(enable);
}
METHOD(leak_detective_t, usage, void,
private_leak_detective_t *this, FILE *out)
{
- int oldpolicy, thresh;
bool detailed;
- pthread_t thread_id = pthread_self();
- struct sched_param oldparams, params;
+ int thresh, thresh_count;
+ size_t sum = 0;
thresh = lib->settings->get_int(lib->settings,
"libstrongswan.leak_detective.usage_threshold", 10240);
+ thresh_count = lib->settings->get_int(lib->settings,
+ "libstrongswan.leak_detective.usage_threshold_count", 0);
detailed = lib->settings->get_bool(lib->settings,
"libstrongswan.leak_detective.detailed", TRUE);
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
- print_traces(this, out, thresh, detailed, NULL);
+ print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum);
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ fprintf(out, "Total memory usage: %zu\n", sum);
}
/**
- * Hook function for malloc()
+ * Wrapped malloc() function
*/
-void *malloc_hook(size_t bytes, const void *caller)
+HOOK(void*, malloc, size_t bytes)
{
memory_header_t *hdr;
memory_tail_t *tail;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
+ bool before;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
-
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ return real_malloc(bytes);
+ }
- count_malloc++;
- uninstall_hooks();
- hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
/* set to something which causes crashes */
memset(hdr, MEMORY_ALLOC_PATTERN,
sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ before = enable_thread(FALSE);
+ hdr->backtrace = backtrace_create(2);
+ enable_thread(before);
+
hdr->magic = MEMORY_HEADER_MAGIC;
hdr->bytes = bytes;
- hdr->backtrace = backtrace_create(2);
tail->magic = MEMORY_TAIL_MAGIC;
- install_hooks();
-
- /* insert at the beginning of the list */
- hdr->next = first_header.next;
- if (hdr->next)
- {
- hdr->next->previous = hdr;
- }
- hdr->previous = &first_header;
- first_header.next = hdr;
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ add_hdr(hdr);
return hdr + 1;
}
/**
- * Hook function for free()
+ * Wrapped calloc() function
*/
-void free_hook(void *ptr, const void *caller)
+HOOK(void*, calloc, size_t nmemb, size_t size)
{
- memory_header_t *hdr, *current;
+ void *ptr;
+
+ size *= nmemb;
+ ptr = malloc(size);
+ memset(ptr, 0, size);
+
+ return ptr;
+}
+
+/**
+ * Wrapped valloc(), TODO: currently not supported
+ */
+HOOK(void*, valloc, size_t size)
+{
+ DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
+ return NULL;
+}
+
+/**
+ * Wrapped free() function
+ */
+HOOK(void, free, void *ptr)
+{
+ memory_header_t *hdr;
memory_tail_t *tail;
backtrace_t *backtrace;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
- bool found = FALSE;
+ bool before;
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ real_free(ptr);
+ return;
+ }
/* allow freeing of NULL */
if (ptr == NULL)
{
@@ -516,25 +818,11 @@ void free_hook(void *ptr, const void *caller)
hdr = ptr - sizeof(memory_header_t);
tail = ptr + hdr->bytes;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
-
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
- count_free++;
- uninstall_hooks();
+ before = enable_thread(FALSE);
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
- for (current = &first_header; current != NULL; current = current->next)
- {
- if (current == hdr)
- {
- found = TRUE;
- break;
- }
- }
- if (found)
+ if (has_hdr(hdr))
{
/* memory was allocated by our hooks but is corrupted */
fprintf(stderr, "freeing corrupted memory (%p): "
@@ -544,7 +832,7 @@ void free_hook(void *ptr, const void *caller)
else
{
/* memory was not allocated by our hooks */
- fprintf(stderr, "freeing invalid memory (%p)", ptr);
+ fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
}
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
@@ -552,53 +840,50 @@ void free_hook(void *ptr, const void *caller)
}
else
{
- /* remove item from list */
- if (hdr->next)
- {
- hdr->next->previous = hdr->previous;
- }
- hdr->previous->next = hdr->next;
+ remove_hdr(hdr);
+
hdr->backtrace->destroy(hdr->backtrace);
/* clear MAGIC, set mem to something remarkable */
memset(hdr, MEMORY_FREE_PATTERN,
sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
- free(hdr);
+ real_free(hdr);
}
-
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ enable_thread(before);
}
/**
- * Hook function for realloc()
+ * Wrapped realloc() function
*/
-void *realloc_hook(void *old, size_t bytes, const void *caller)
+HOOK(void*, realloc, void *old, size_t bytes)
{
memory_header_t *hdr;
memory_tail_t *tail;
backtrace_t *backtrace;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
+ bool before;
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ return real_realloc(old, bytes);
+ }
/* allow reallocation of NULL */
if (old == NULL)
{
- return malloc_hook(bytes, caller);
+ return malloc(bytes);
+ }
+ /* handle zero size as a free() */
+ if (bytes == 0)
+ {
+ free(old);
+ return NULL;
}
hdr = old - sizeof(memory_header_t);
tail = old + hdr->bytes;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
+ remove_hdr(hdr);
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
- count_realloc++;
- uninstall_hooks();
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
@@ -613,33 +898,30 @@ void *realloc_hook(void *old, size_t bytes, const void *caller)
/* clear tail magic, allocate, set tail magic */
memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
}
- hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ hdr = real_realloc(hdr,
+ sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
tail->magic = MEMORY_TAIL_MAGIC;
/* update statistics */
hdr->bytes = bytes;
+
+ before = enable_thread(FALSE);
hdr->backtrace->destroy(hdr->backtrace);
hdr->backtrace = backtrace_create(2);
+ enable_thread(before);
+
+ add_hdr(hdr);
- /* update header of linked list neighbours */
- if (hdr->next)
- {
- hdr->next->previous = hdr;
- }
- hdr->previous->next = hdr;
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
return hdr + 1;
}
METHOD(leak_detective_t, destroy, void,
private_leak_detective_t *this)
{
- if (installed)
- {
- uninstall_hooks();
- }
+ disable_leak_detective();
+ lock->destroy(lock);
+ thread_disabled->destroy(thread_disabled);
free(this);
}
@@ -653,26 +935,24 @@ leak_detective_t *leak_detective_create()
INIT(this,
.public = {
.report = _report,
+ .leaks = _leaks,
.usage = _usage,
.set_state = _set_state,
.destroy = _destroy,
},
);
- if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
- {
- cpu_set_t mask;
+ lock = spinlock_create();
+ thread_disabled = thread_value_create(NULL);
- CPU_ZERO(&mask);
- CPU_SET(0, &mask);
+ init_static_allocations();
- if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
+ if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
+ {
+ if (register_hooks())
{
- fprintf(stderr, "setting CPU affinity failed: %m");
+ enable_leak_detective();
}
-
- install_hooks();
}
return &this->public;
}
-
diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h
index 55d7e44d9..7a29e81d7 100644
--- a/src/libstrongswan/utils/leak_detective.h
+++ b/src/libstrongswan/utils/leak_detective.h
@@ -43,6 +43,13 @@ struct leak_detective_t {
void (*report)(leak_detective_t *this, bool detailed);
/**
+ * Number of detected leaks.
+ *
+ * @return number of leaks
+ */
+ int (*leaks)(leak_detective_t *this);
+
+ /**
* Report current memory usage to out.
*
* @param out target to write usage report to
@@ -50,7 +57,7 @@ struct leak_detective_t {
void (*usage)(leak_detective_t *this, FILE *out);
/**
- * Enable/disable leak detective hooks.
+ * Enable/disable leak detective hooks for the current thread.
*
* @param TRUE to enable, FALSE to disable
* @return state active before calling set_state
@@ -69,4 +76,3 @@ struct leak_detective_t {
leak_detective_t *leak_detective_create();
#endif /** LEAK_DETECTIVE_H_ @}*/
-
diff --git a/src/libstrongswan/utils/printf_hook.c b/src/libstrongswan/utils/printf_hook.c
index 6e51aa4c3..f030f45c8 100644
--- a/src/libstrongswan/utils/printf_hook.c
+++ b/src/libstrongswan/utils/printf_hook.c
@@ -474,7 +474,6 @@ METHOD(printf_hook_t, destroy, void,
/* freeing the Vstr_conf of the main thread */
vstr_conf->destroy(vstr_conf);
vstr_conf = NULL;
- vstr_free_conf(conf);
vstr_exit();
#endif
free(this);
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
index 712ea6ee2..809ca10ab 100644
--- a/src/libstrongswan/utils/settings.c
+++ b/src/libstrongswan/utils/settings.c
@@ -644,6 +644,26 @@ METHOD(settings_t, set_time, void,
va_end(args);
}
+METHOD(settings_t, set_default_str, bool,
+ private_settings_t *this, char *key, char *value, ...)
+{
+ char *old;
+ va_list args;
+
+ va_start(args, value);
+ old = find_value(this, this->top, key, args);
+ va_end(args);
+
+ if (!old)
+ {
+ va_start(args, value);
+ set_value(this, this->top, key, args, value);
+ va_end(args);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
* Enumerate section names, not sections
*/
@@ -1209,6 +1229,7 @@ settings_t *settings_create(char *file)
.set_double = _set_double,
.set_time = _set_time,
.set_bool = _set_bool,
+ .set_default_str = _set_default_str,
.create_section_enumerator = _create_section_enumerator,
.create_key_value_enumerator = _create_key_value_enumerator,
.load_files = _load_files,
diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h
index a861325f5..df0c534e9 100644
--- a/src/libstrongswan/utils/settings.h
+++ b/src/libstrongswan/utils/settings.h
@@ -239,6 +239,16 @@ struct settings_t {
void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
/**
+ * Set a default for string value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value to set if unconfigured
+ * @param ... argument list for key
+ * @return TRUE if a new default value for key has been set
+ */
+ bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
+
+ /**
* Create an enumerator over subsection names of a section.
*
* @param section section including parents, printf style format
diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c
index 2f38d8a93..30084cd81 100644
--- a/src/libstrongswan/utils/utils.c
+++ b/src/libstrongswan/utils/utils.c
@@ -48,19 +48,6 @@ ENUM(status_names, SUCCESS, NEED_MORE,
/**
* Described in header.
*/
-void *clalloc(void * pointer, size_t size)
-{
- void *data;
- data = malloc(size);
-
- memcpy(data, pointer, size);
-
- return (data);
-}
-
-/**
- * Described in header.
- */
void memxor(u_int8_t dst[], u_int8_t src[], size_t n)
{
int m, i;
@@ -115,7 +102,12 @@ void memwipe_noinline(void *ptr, size_t n)
void *memstr(const void *haystack, const char *needle, size_t n)
{
unsigned const char *pos = haystack;
- size_t l = strlen(needle);
+ size_t l;
+
+ if (!haystack || !needle || (l = strlen(needle)) == 0)
+ {
+ return NULL;
+ }
for (; n >= l; ++pos, --n)
{
if (memeq(pos, needle, l))
@@ -474,11 +466,15 @@ static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* Increase refcount
*/
-void ref_get(refcount_t *ref)
+refcount_t ref_get(refcount_t *ref)
{
+ refcount_t current;
+
pthread_mutex_lock(&ref_mutex);
- (*ref)++;
+ current = ++(*ref);
pthread_mutex_unlock(&ref_mutex);
+
+ return current;
}
/**
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index c66c665e0..d055f712d 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -81,9 +81,20 @@ static inline bool streq(const char *x, const char *y)
}
/**
- * Macro compares two strings for equality, length limited
+ * Helper function that compares two strings for equality, length limited
*/
-#define strneq(x,y,len) (strncmp(x, y, len) == 0)
+static inline bool strneq(const char *x, const char *y, size_t len)
+{
+ return strncmp(x, y, len) == 0;
+}
+
+/**
+ * Helper function that checks if a string starts with a given prefix
+ */
+static inline bool strpfx(const char *x, const char *prefix)
+{
+ return strneq(x, prefix, strlen(prefix));
+}
/**
* Helper function that compares two strings for equality ignoring case
@@ -94,9 +105,12 @@ static inline bool strcaseeq(const char *x, const char *y)
}
/**
- * Macro compares two strings for equality ignoring case, length limited
+ * Helper function that compares two strings for equality ignoring case, length limited
*/
-#define strncaseeq(x,y,len) (strncasecmp(x, y, len) == 0)
+static inline bool strncaseeq(const char *x, const char *y, size_t len)
+{
+ return strncasecmp(x, y, len) == 0;
+}
/**
* NULL-safe strdup variant
@@ -107,9 +121,12 @@ static inline char *strdupnull(const char *s)
}
/**
- * Macro compares two binary blobs for equality
+ * Helper function that compares two binary blobs for equality
*/
-#define memeq(x,y,len) (memcmp(x, y, len) == 0)
+static inline bool memeq(const void *x, const void *y, size_t len)
+{
+ return memcmp(x, y, len) == 0;
+}
/**
* Macro gives back larger of two values.
@@ -376,11 +393,6 @@ typedef struct timespec timespec_t;
typedef struct sockaddr sockaddr_t;
/**
- * Clone a data to a newly allocated buffer
- */
-void *clalloc(void *pointer, size_t size);
-
-/**
* Same as memcpy, but XORs src into dst instead of copy
*/
void memxor(u_int8_t dest[], u_int8_t src[], size_t n);
@@ -423,6 +435,10 @@ static inline void memwipe_inline(void *ptr, size_t n)
*/
static inline void memwipe(void *ptr, size_t n)
{
+ if (!ptr)
+ {
+ return;
+ }
if (__builtin_constant_p(n))
{
memwipe_inline(ptr, n);
@@ -503,7 +519,7 @@ time_t time_monotonic(timeval_t *tv);
static inline void timeval_add_ms(timeval_t *tv, u_int ms)
{
tv->tv_usec += ms * 1000;
- while (tv->tv_usec > 1000000 /* 1s */)
+ while (tv->tv_usec >= 1000000 /* 1s */)
{
tv->tv_usec -= 1000000;
tv->tv_sec++;
@@ -655,14 +671,36 @@ static inline u_int64_t untoh64(void *network)
}
/**
- * Special type to count references
+ * Round up size to be multiple of alignement
*/
-typedef volatile u_int refcount_t;
+static inline size_t round_up(size_t size, int alignement)
+{
+ int remainder;
+
+ remainder = size % alignement;
+ if (remainder)
+ {
+ size += alignement - remainder;
+ }
+ return size;
+}
+/**
+ * Round down size to be a multiple of alignement
+ */
+static inline size_t round_down(size_t size, int alignement)
+{
+ return size - (size % alignement);
+}
+
+/**
+ * Special type to count references
+ */
+typedef u_int refcount_t;
#ifdef HAVE_GCC_ATOMIC_OPERATIONS
-#define ref_get(ref) {__sync_fetch_and_add(ref, 1); }
+#define ref_get(ref) __sync_add_and_fetch(ref, 1)
#define ref_put(ref) (!__sync_sub_and_fetch(ref, 1))
#define cas_bool(ptr, oldval, newval) \
@@ -678,8 +716,9 @@ typedef volatile u_int refcount_t;
* Increments the reference counter atomic.
*
* @param ref pointer to ref counter
+ * @return new value of ref
*/
-void ref_get(refcount_t *ref);
+refcount_t ref_get(refcount_t *ref);
/**
* Put back a unused reference.