1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-12-19 16:41:39 +03:00

Heap area protection of memory has been enhanced.

When memory is released and reallocated, a random security value called a canary is written to the before/after area of memory, and if the value has been modified, the process is terminated (restarted) for safety, assuming it is a buffer overflow of the memory area. This feature may effectively prevent confidentiality or integrity violations in the event that some heap area overflow vulnerability is discovered in this system in the future.
This commit is contained in:
Daiyuu Nobori
2023-10-07 04:42:00 +02:00
committed by Davide Beatrici
parent c49e462ed1
commit 2dec52b875
9 changed files with 347 additions and 52 deletions

View File

@ -16,10 +16,16 @@
#include "Object.h"
#include "OS.h"
#include "Str.h"
#include "Tick64.h"
#include "Tracking.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef OS_UNIX
#include <sys/time.h>
#endif
#include <zlib.h>
@ -34,6 +40,105 @@
static UINT fifo_current_realloc_mem_size = FIFO_REALLOC_MEM_SIZE;
static bool canary_inited = false;
typedef struct CANARY_RAND_DATA
{
UCHAR Data[CANARY_RAND_SIZE + 4];
} CANARY_RAND_DATA;
static CANARY_RAND_DATA canary_rand_data[NUM_CANARY_RAND] = { 0 };
static UINT64 canary_memtag_magic1 = 0;
static UINT64 canary_memtag_magic2 = 0;
UCHAR *GetCanaryRand(UINT id)
{
if (id >= NUM_CANARY_RAND)
{
id = NUM_CANARY_RAND - 1;
}
return &((canary_rand_data[id].Data)[0]);
}
void InitCanaryRand()
{
SYSTEMTIME st = { 0 };
char random_seed[1024] = { 0 };
UINT64 t1 = 0, t2 = 0;
if (canary_inited)
{
return;
}
#ifdef OS_WIN32
Win32GetSystemTime(&st);
memcpy(&t1, ((UCHAR *)&st) + 0, 8);
memcpy(&t2, ((UCHAR *)&st) + 8, 8);
#else // OS_WIN32
struct timeval tv = { 0 };
struct timezone tz = { 0 };
gettimeofday(&tv, &tz);
t1 = (UINT64)tv.tv_sec;
t2 = (UINT64)tv.tv_usec;
#endif // OS_WIN32
{
UINT64 dos_rand = (UINT64)rand();
UINT64 tick1 = TickHighresNano64(true);
UINT64 tick2 = TickHighresNano64(true);
UINT i;
void *p1 = malloc(1);
void *p2 = malloc(1);
for (i = 0;i < NUM_CANARY_RAND;i++)
{
// using sprintf() here is safe.
sprintf(random_seed,
"%u "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%llu "
"%u "
,
i,
(UINT64)InitCanaryRand,
(UINT64)&canary_inited,
(UINT64) & ((canary_rand_data[0].Data)[0]),
(UINT64)&random_seed[0],
tick1,
tick2,
dos_rand,
(UINT64)p1,
(UINT64)p2,
t1,
t2,
~i
);
Sha0(canary_rand_data[i].Data, random_seed, (UINT)strlen(random_seed));
}
free(p1);
free(p2);
canary_memtag_magic1 = *((UINT64 *)(GetCanaryRand(CANARY_RAND_ID_MEMTAG_MAGIC) + 0));
canary_memtag_magic2 = *((UINT64 *)(GetCanaryRand(CANARY_RAND_ID_MEMTAG_MAGIC) + 8));
canary_inited = true;
}
}
// New PRand
PRAND *NewPRand(void *key, UINT key_size)
{
@ -3519,33 +3624,52 @@ void *Malloc(UINT size)
}
void *MallocEx(UINT size, bool zero_clear_when_free)
{
MEMTAG *tag;
MEMTAG1 *tag1;
MEMTAG2 *tag2;
UINT real_size;
if (canary_inited == false)
{
InitCanaryRand();
}
if (size > MAX_MALLOC_MEM_SIZE)
{
AbortExitEx("MallocEx() error: too large size");
}
real_size = CALC_MALLOCSIZE(size);
tag = InternalMalloc(real_size);
tag1 = InternalMalloc(real_size);
Zero(tag, sizeof(MEMTAG));
tag->Magic = MEMTAG_MAGIC;
tag->Size = size;
tag->ZeroFree = zero_clear_when_free;
tag1->Magic = canary_memtag_magic1 ^ ((UINT64)tag1 * GOLDEN_RATION_PRIME_U64);
tag1->Size = size;
tag1->ZeroFree = zero_clear_when_free;
return MEMTAG_TO_POINTER(tag);
tag2 = (MEMTAG2 *)(((UCHAR *)tag1) + CALC_MALLOCSIZE(tag1->Size) - sizeof(MEMTAG2));
tag2->Magic = canary_memtag_magic2 ^ ((UINT64)tag2 * GOLDEN_RATION_PRIME_U64);
return MEMTAG1_TO_POINTER(tag1);
}
// Get memory size
UINT GetMemSize(void *addr)
{
MEMTAG *tag;
MEMTAG1 *tag;
if (canary_inited == false)
{
InitCanaryRand();
}
// Validate arguments
if (IS_NULL_POINTER(addr))
{
return 0;
}
tag = POINTER_TO_MEMTAG(addr);
CheckMemTag(tag);
tag = POINTER_TO_MEMTAG1(addr);
CheckMemTag1(tag);
return tag->Size;
}
@ -3553,20 +3677,35 @@ UINT GetMemSize(void *addr)
// ReAlloc
void *ReAlloc(void *addr, UINT size)
{
MEMTAG *tag;
MEMTAG1 *tag1;
MEMTAG2 *tag2;
bool zerofree;
if (canary_inited == false)
{
InitCanaryRand();
}
if (size > MAX_MALLOC_MEM_SIZE)
{
AbortExitEx("ReAlloc() error: too large size");
}
// Validate arguments
if (IS_NULL_POINTER(addr))
{
return NULL;
}
tag = POINTER_TO_MEMTAG(addr);
CheckMemTag(tag);
tag1 = POINTER_TO_MEMTAG1(addr);
CheckMemTag1(tag1);
zerofree = tag->ZeroFree;
tag2 = (MEMTAG2 *)(((UCHAR *)tag1) + CALC_MALLOCSIZE(tag1->Size) - sizeof(MEMTAG2));
CheckMemTag2(tag2);
if (tag->Size == size)
zerofree = tag1->ZeroFree;
if (tag1->Size == size)
{
// No size change
return addr;
@ -3578,10 +3717,10 @@ void *ReAlloc(void *addr, UINT size)
// Size changed (zero clearing required)
void *new_p = MallocEx(size, true);
if (tag->Size <= size)
if (tag1->Size <= size)
{
// Size expansion
Copy(new_p, addr, tag->Size);
Copy(new_p, addr, tag1->Size);
}
else
{
@ -3597,13 +3736,22 @@ void *ReAlloc(void *addr, UINT size)
else
{
// Size changed
MEMTAG *tag2 = InternalReAlloc(tag, CALC_MALLOCSIZE(size));
MEMTAG1 *tag1_new;
MEMTAG2 *tag2_new;
Zero(tag2, sizeof(MEMTAG));
tag2->Magic = MEMTAG_MAGIC;
tag2->Size = size;
tag1->Magic = 0;
tag2->Magic = 0;
return MEMTAG_TO_POINTER(tag2);
tag1_new = InternalReAlloc(tag1, CALC_MALLOCSIZE(size));
tag1_new->Magic = canary_memtag_magic1 ^ ((UINT64)tag1_new * GOLDEN_RATION_PRIME_U64);
tag1_new->Size = size;
tag1_new->ZeroFree = 0;
tag2_new = (MEMTAG2 *)(((UCHAR *)tag1_new) + CALC_MALLOCSIZE(size) - sizeof(MEMTAG2));
tag2_new->Magic = canary_memtag_magic2 ^ ((UINT64)tag2_new * GOLDEN_RATION_PRIME_U64);
return MEMTAG1_TO_POINTER(tag1_new);
}
}
}
@ -3611,25 +3759,35 @@ void *ReAlloc(void *addr, UINT size)
// Free
void Free(void *addr)
{
MEMTAG *tag;
MEMTAG1 *tag1;
MEMTAG2 *tag2;
// Validate arguments
if (IS_NULL_POINTER(addr))
{
return;
}
tag = POINTER_TO_MEMTAG(addr);
CheckMemTag(tag);
if (canary_inited == false)
{
InitCanaryRand();
}
if (tag->ZeroFree)
tag1 = POINTER_TO_MEMTAG1(addr);
CheckMemTag1(tag1);
tag2 = (MEMTAG2 *)(((UCHAR *)tag1) + CALC_MALLOCSIZE(tag1->Size) - sizeof(MEMTAG2));
CheckMemTag2(tag2);
if (tag1->ZeroFree)
{
// Zero clear
Zero(addr, tag->Size);
Zero(addr, tag1->Size);
}
// Memory release
tag->Magic = 0;
InternalFree(tag);
tag1->Magic = 0;
tag2->Magic = 0;
InternalFree(tag1);
}
// Free and set pointer's value to NULL
@ -3639,24 +3797,36 @@ void FreeSafe(void **addr)
*addr = NULL;
}
// Check the memtag
void CheckMemTag(MEMTAG *tag)
// Check the memtag1
void CheckMemTag1(MEMTAG1 *tag)
{
if (IsTrackingEnabled() == false)
{
return;
}
// Validate arguments
if (tag == NULL)
{
AbortExitEx("CheckMemTag: tag == NULL");
AbortExitEx("CheckMemTag1: tag1 == NULL");
return;
}
if (tag->Magic != MEMTAG_MAGIC)
if (tag->Magic != (canary_memtag_magic1 ^ ((UINT64)tag * GOLDEN_RATION_PRIME_U64)))
{
AbortExitEx("CheckMemTag: tag->Magic != MEMTAG_MAGIC");
AbortExitEx("CheckMemTag1: tag1->Magic != canary_memtag_magic1");
return;
}
}
// Check the memtag2
void CheckMemTag2(MEMTAG2 *tag)
{
// Validate arguments
if (tag == NULL)
{
AbortExitEx("CheckMemTag2: tag2 == NULL");
return;
}
if (tag->Magic != (canary_memtag_magic2 ^ ((UINT64)tag * GOLDEN_RATION_PRIME_U64)))
{
AbortExitEx("CheckMemTag2: tag2->Magic != canary_memtag_magic2");
return;
}
}