Skip to content

Commit

Permalink
fix: only handle signals in memory block data
Browse files Browse the repository at this point in the history
Some callbacks from YARA code are done from YR_TRYCATCH. Currently,
YARA assumes that SIGBUS / SIGSEGV from these callbacks are due to
the scanned memory range, but this is not necessarily true.
Add logic to identify the currently scanned memory block and only
catch the signal if the SIGBUS / SIGSEGV originated from it.
  • Loading branch information
secDre4mer committed Dec 4, 2023
1 parent d1ce12f commit a05151b
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 112 deletions.
162 changes: 93 additions & 69 deletions libyara/exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <yara/globals.h>

typedef struct {
void* memfault_from;
void* memfault_to;
void* jump_back;
} jumpinfo;


#if _WIN32 || __CYGWIN__

#include <windows.h>
Expand Down Expand Up @@ -83,46 +90,56 @@ static LONG CALLBACK exception_handler(PEXCEPTION_POINTERS ExceptionInfo)

static LONG CALLBACK exception_handler(PEXCEPTION_POINTERS ExceptionInfo)
{
jmp_buf* jb_ptr;
jumpinfo* jump_info;

switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
{
case EXCEPTION_IN_PAGE_ERROR:
case EXCEPTION_ACCESS_VIOLATION:
jb_ptr =
(jmp_buf*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls);
jump_info =
(jumpinfo*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls);

if (jb_ptr != NULL)
longjmp(*jb_ptr, 1);
if (jump_info != NULL)
{
void* fault_address = (void*) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
if (jump_info->memfault_from <= fault_address && jump_info->memfault_to > fault_address)
{
longjmp(*(jmp_buf*)jump_info->jump_back, 1);
}
}
}

return EXCEPTION_CONTINUE_SEARCH;
}

#define YR_TRYCATCH(_do_, _try_clause_, _catch_clause_) \
do \
{ \
if (_do_) \
{ \
jmp_buf jb; \
/* Store pointer to sigjmp_buf in TLS */ \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &jb); \
HANDLE exh = AddVectoredExceptionHandler(1, exception_handler); \
if (setjmp(jb) == 0) \
{ \
_try_clause_ \
} \
else \
{ \
_catch_clause_ \
} \
RemoveVectoredExceptionHandler(exh); \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, NULL); \
} \
else \
{ \
_try_clause_ \
} \
#define YR_TRYCATCH(_do_, _try_clause_, _catch_clause_) \
do \
{ \
if (_do_) \
{ \
jumpinfo jump_info; \
jump_info.memfault_from = 0; \
jump_info.memfault_to = 0; \
jmp_buf jb; \
jump_info.jump_back = (void*) &jb; \
/* Store pointer to sigjmp_buf in TLS */ \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &jump_info); \
HANDLE exh = AddVectoredExceptionHandler(1, exception_handler); \
if (setjmp(jb) == 0) \
{ \
_try_clause_ \
} \
else \
{ \
_catch_clause_ \
} \
RemoveVectoredExceptionHandler(exh); \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, NULL); \
} \
else \
{ \
_try_clause_ \
} \
} while (0)

#endif
Expand All @@ -136,55 +153,58 @@ static LONG CALLBACK exception_handler(PEXCEPTION_POINTERS ExceptionInfo)

static void exception_handler(int sig, siginfo_t * info, void *context)
{
if (sig == SIGBUS || sig == SIGSEGV)
if (sig != SIGBUS && sig != SIGSEGV)
{
jmp_buf* jb_ptr =
(jmp_buf*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls);
return;
}
jumpinfo* jump_info = (jumpinfo*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls);

if (jb_ptr != NULL)
if (jump_info != NULL)
{
void* fault_address = (void*) info->si_addr;
if (jump_info->memfault_from <= fault_address && jump_info->memfault_to > fault_address)
{
siglongjmp(*jb_ptr, 1);
// The long jump means the following code to invoke the old exception handler is never executed
siglongjmp(*(sigjmp_buf*)jump_info->jump_back, 1);
}
}

// If we're here, the signal we received didn't originate from YARA.
// In this case, we want to invoke the original signal handler, which may handle the signal.
// If we're here, the signal we received didn't originate from YARA.
// In this case, we want to invoke the original signal handler, which may handle the signal.

// Lock the exception handler mutex to prevent simultaneous write access while we read the old signal handler
pthread_mutex_lock(&exception_handler_mutex);
struct sigaction old_handler;
if (sig == SIGBUS)
old_handler = old_sigbus_exception_handler;
else
old_handler = old_sigsegv_exception_handler;
pthread_mutex_unlock(&exception_handler_mutex);
// Lock the exception handler mutex to prevent simultaneous write access while we read the old signal handler
pthread_mutex_lock(&exception_handler_mutex);
struct sigaction old_handler;
if (sig == SIGBUS)
old_handler = old_sigbus_exception_handler;
else
old_handler = old_sigsegv_exception_handler;
pthread_mutex_unlock(&exception_handler_mutex);

if (old_handler.sa_flags & SA_SIGINFO)
if (old_handler.sa_flags & SA_SIGINFO)
{
old_handler.sa_sigaction(sig, info, context);
}
else
{
if (old_handler.sa_handler == SIG_DFL)
{
old_handler.sa_sigaction(sig, info, context);
// Old handler is the default action. To do this, set the signal handler back to default and raise the signal.
// This is fairly volatile - since this is not an atomic operation, signals from other threads might also
// cause the default action while we're doing this. However, the default action will typically cause a
// process termination anyway.
pthread_mutex_lock(&exception_handler_mutex);
struct sigaction current_handler;
sigaction(sig, &old_handler, &current_handler);
raise(sig);
sigaction(sig, &current_handler, NULL);
pthread_mutex_unlock(&exception_handler_mutex);
}
else
else if (old_handler.sa_handler == SIG_IGN)
{
if (old_handler.sa_handler == SIG_DFL)
{
// Old handler is the default action. To do this, set the signal handler back to default and raise the signal.
// This is fairly volatile - since this is not an atomic operation, signals from other threads might also
// cause the default action while we're doing this. However, the default action will typically cause a
// process termination anyway.
pthread_mutex_lock(&exception_handler_mutex);
struct sigaction current_handler;
sigaction(sig, &old_handler, &current_handler);
raise(sig);
sigaction(sig, &current_handler, NULL);
pthread_mutex_unlock(&exception_handler_mutex);
}
else if (old_handler.sa_handler == SIG_IGN)
{
// SIG_IGN wants us to ignore the signal
return;
}
old_handler.sa_handler(sig);
// SIG_IGN wants us to ignore the signal
return;
}
old_handler.sa_handler(sig);
}
}

Expand All @@ -208,9 +228,13 @@ typedef struct sigaction sa;
} \
exception_handler_usecount++; \
pthread_mutex_unlock(&exception_handler_mutex); \
jumpinfo ji; \
ji.memfault_from = 0; \
ji.memfault_to = 0; \
sigjmp_buf jb; \
/* Store pointer to sigjmp_buf in TLS */ \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &jb); \
ji.jump_back = (void*) &jb; \
/* Store pointer to jumpinfo in TLS */ \
yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &ji); \
if (sigsetjmp(jb, 1) == 0) \
{ \
_try_clause_ \
Expand Down
2 changes: 1 addition & 1 deletion libyara/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
offset <= block->base + block->size - sizeof(type)) \
{ \
type result; \
const uint8_t* data = block->fetch_data(block); \
const uint8_t* data = yr_fetch_block_data(block); \
if (data == NULL) \
return YR_UNDEFINED; \
result = *(type*) (data + offset - block->base); \
Expand Down
2 changes: 2 additions & 0 deletions libyara/include/yara/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,8 @@ struct YR_MEMORY_BLOCK
YR_MEMORY_BLOCK_FETCH_DATA_FUNC fetch_data;
};

YR_API const uint8_t* yr_fetch_block_data(YR_MEMORY_BLOCK* self);

///////////////////////////////////////////////////////////////////////////////
// YR_MEMORY_BLOCK_ITERATOR represents an iterator that returns a series of
// memory blocks to be scanned by yr_scanner_scan_mem_blocks. The iterator have
Expand Down
2 changes: 1 addition & 1 deletion libyara/modules/dex/dex.c
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ int module_load(

foreach_memory_block(iterator, block)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
continue;
Expand Down
2 changes: 1 addition & 1 deletion libyara/modules/dotnet/dotnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -3515,7 +3515,7 @@ int module_load(
{
PIMAGE_NT_HEADERS32 pe_header;

block_data = block->fetch_data(block);
block_data = yr_fetch_block_data(block);

if (block_data == NULL)
continue;
Expand Down
3 changes: 2 additions & 1 deletion libyara/modules/elf/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <yara/simple_str.h>
#include <yara/utils.h>
#include "../crypto.h"
#include "../exception.h"

#define MODULE_NAME elf

Expand Down Expand Up @@ -1113,7 +1114,7 @@ int module_load(

foreach_memory_block(iterator, block)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
continue;
Expand Down
10 changes: 5 additions & 5 deletions libyara/modules/hash/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ define_function(data_md5)

if (offset >= block->base && offset < block->base + block->size)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down Expand Up @@ -456,7 +456,7 @@ define_function(data_sha1)
// if desired block within current block
if (offset >= block->base && offset < block->base + block->size)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down Expand Up @@ -585,7 +585,7 @@ define_function(data_sha256)
// if desired block within current block
if (offset >= block->base && offset < block->base + block->size)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down Expand Up @@ -674,7 +674,7 @@ define_function(data_checksum32)
{
if (offset >= block->base && offset < block->base + block->size)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down Expand Up @@ -765,7 +765,7 @@ define_function(data_crc32)
{
if (offset >= block->base && offset < block->base + block->size)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down
2 changes: 1 addition & 1 deletion libyara/modules/macho/macho.c
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,7 @@ int module_load(

foreach_memory_block(iterator, block)
{
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL || block->size < 4)
continue;
Expand Down
4 changes: 2 additions & 2 deletions libyara/modules/magic/magic.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ define_function(magic_mime_type)
if (block == NULL)
return_string(YR_UNDEFINED);

block_data = block->fetch_data(block);
block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down Expand Up @@ -142,7 +142,7 @@ define_function(magic_type)
if (block == NULL)
return_string(YR_UNDEFINED);

block_data = block->fetch_data(block);
block_data = yr_fetch_block_data(block);

if (block_data != NULL)
{
Expand Down
8 changes: 4 additions & 4 deletions libyara/modules/math/math.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ uint32_t* get_distribution(int64_t offset, int64_t length, YR_SCAN_CONTEXT* cont
size_t data_len = (size_t) yr_min(
length, (size_t)(block->size - data_offset));

const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
{
Expand Down Expand Up @@ -145,7 +145,7 @@ uint32_t* get_distribution_global(YR_SCAN_CONTEXT* context) {
yr_free(data);
return NULL;
}
const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
{
Expand Down Expand Up @@ -344,7 +344,7 @@ define_function(data_serial_correlation)
size_t data_len = (size_t) yr_min(
length, (size_t)(block->size - data_offset));

const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
return_float(YR_UNDEFINED);
Expand Down Expand Up @@ -468,7 +468,7 @@ define_function(data_monte_carlo_pi)
size_t data_len = (size_t) yr_min(
length, (size_t)(block->size - data_offset));

const uint8_t* block_data = block->fetch_data(block);
const uint8_t* block_data = yr_fetch_block_data(block);

if (block_data == NULL)
return_float(YR_UNDEFINED);
Expand Down
2 changes: 1 addition & 1 deletion libyara/modules/pe/pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -4317,7 +4317,7 @@ int module_load(

foreach_memory_block(iterator, block)
{
block_data = block->fetch_data(block);
block_data = yr_fetch_block_data(block);

if (block_data == NULL)
continue;
Expand Down
Loading

0 comments on commit a05151b

Please sign in to comment.