#include "first.h"

#include "safe_memclear.h"

#include <string.h>

#if defined(_WIN32) && !defined(__CYGWIN__)
#include <WinBase.h>
/*(Windows XP and later provide SecureZeroMemory())*/
#define HAVE_SECUREZEROMEMORY
#endif

#if !defined(HAVE_MEMSET_S) \
 && !defined(HAVE_EXPLICIT_BZERO) \
 && !defined(HAVE_EXPLICIT_MEMSET) \
 && !defined(HAVE_SECUREZEROMEMORY)

typedef void *(*safe_memclear_func_t)(void *, int, size_t);
extern volatile safe_memclear_func_t safe_memclear_func;
volatile safe_memclear_func_t safe_memclear_func = memset;

#  if defined(HAVE_WEAK_SYMBOLS)
/* it seems weak functions are never inlined, even for static builds */
__attribute__((weak)) void __li_safe_memset_hook(void *buf, size_t len);

void __li_safe_memset_hook(void *buf, size_t len)
{
	UNUSED(buf);
	UNUSED(len);
}
#  endif /* HAVE_WEAK_SYMBOLS */

static void* safe_memset(void *s, int c, size_t n)
{
	if (n > 0) {
		volatile unsigned volatile_zero = 0;
		volatile unsigned char *vs = (volatile unsigned char*)s;

		do {
			safe_memclear_func(s, c, n);
		} while (vs[volatile_zero] != (unsigned char)c);
#  if defined(HAVE_WEAK_SYMBOLS)
		__li_safe_memset_hook(s, n);
#  endif /* HAVE_WEAK_SYMBOLS */
	}

	return s;
}

#endif


void safe_memclear(void *s, size_t n) {
#if defined(HAVE_MEMSET_S)
	memset_s(s, n, 0, n);
#elif defined(HAVE_EXPLICIT_BZERO)
	explicit_bzero(s, n);
#elif defined(HAVE_EXPLICIT_MEMSET)
	explicit_memset(s, 0, n);
#elif defined(HAVE_SECUREZEROMEMORY)
	SecureZeroMemory(s, n);
#else
	safe_memset(s, 0, n);
#endif
}
