Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions src/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "hs_db_hmac_key.h"
#include "nfa/nfa_internal.h"
#include "nfa/limex_internal.h"
#include "nfa/tamarama_internal.h"
#include "rose/rose_internal.h"
#include "util/compile_error.h"
#include "util/unaligned.h"
Expand Down Expand Up @@ -270,6 +271,107 @@ hs_error_t db_check_integrity(const hs_database_t *db) {
return HS_SUCCESS;
}

/**
* \brief Validate Tamarama subengine offsets to prevent out-of-bounds access
* (CWE-125). A forged child-offset can cause getSubEngine() to return a wild
* pointer that is then dereferenced by nfaQueueInitState().
*/
static
hs_error_t db_validate_tamarama_offsets(const struct RoseEngine *rose,
u32 rose_size) {
if (!rose->nfaInfoOffset || rose->nfaInfoOffset >= rose_size) {
return HS_SUCCESS; /* no NFA engines - nothing to check */
}
Comment on lines +282 to +284

const char *rose_base = (const char *)rose;
const struct NfaInfo *infos =
(const struct NfaInfo *)(rose_base + rose->nfaInfoOffset);

for (u32 qi = 0; qi < rose->queueCount; qi++) {
if (unlikely((const char *)(&infos[qi + 1]) > rose_base + rose_size)) {
return HS_INVALID;
}
Comment on lines +290 to +293

const struct NfaInfo *ni = &infos[qi];
if (!ni->nfaOffset || ni->nfaOffset >= rose_size) {
continue;
}

if (unlikely(ni->nfaOffset + sizeof(struct NFA) > rose_size)) {
return HS_INVALID;
}
Comment on lines +300 to +302

const struct NFA *nfa =
(const struct NFA *)(rose_base + ni->nfaOffset);

if (nfa->type != TAMARAMA_NFA) {
continue;
}

if (unlikely(ni->nfaOffset + nfa->length > rose_size)) {
return HS_INVALID;
}
Comment on lines +311 to +313

if (unlikely(nfa->length <= sizeof(struct NFA))) {
return HS_INVALID;
}

const char *tama_base = (const char *)getImplNfa(nfa);
u32 tama_len = nfa->length - (u32)sizeof(struct NFA);

if (unlikely(tama_len < sizeof(struct Tamarama))) {
DEBUG_PRINTF("Tamarama[%u] too small for header\n", qi);
return HS_INVALID;
}

const struct Tamarama *t = (const struct Tamarama *)tama_base;
u32 numSub = t->numSubEngines;

if (unlikely(numSub == 0)) {
DEBUG_PRINTF("Tamarama[%u] has zero subengines\n", qi);
return HS_INVALID;
}

/* The layout after the Tamarama header is:
* u32 baseTops[numSub] — top values
* u32 subOffsets[numSub] — offsets to child NFAs
* Both arrays must fit within tama_len. */
u64a table_end = (u64a)sizeof(struct Tamarama) +
(u64a)numSub * 2 * sizeof(u32);
if (unlikely(table_end > tama_len)) {
DEBUG_PRINTF("Tamarama[%u] offset table overflows\n", qi);
return HS_INVALID;
}

/* subOffsets array starts after baseTops */
const u32 *subOffsets =
(const u32 *)(tama_base + sizeof(struct Tamarama) +
numSub * sizeof(u32));
Comment on lines +346 to +349

for (u32 i = 0; i < numSub; i++) {
u32 child_off = subOffsets[i];

if (unlikely(child_off >= tama_len ||
child_off + sizeof(struct NFA) > tama_len)) {
DEBUG_PRINTF("Tamarama[%u] sub[%u] offset %u out of bounds "
"(tama_len=%u)\n", qi, i, child_off, tama_len);
return HS_INVALID;
}
Comment on lines +354 to +359

/* Verify child NFA body fits within the Tamarama image */
const struct NFA *child =
(const struct NFA *)(tama_base + child_off);
if (unlikely(child_off + child->length > tama_len)) {
DEBUG_PRINTF("Tamarama[%u] sub[%u] child body overflows\n",
qi, i);
return HS_INVALID;
}
Comment on lines +364 to +368
}
}

return HS_SUCCESS;
}

/**
* \brief Validate critical RoseEngine offsets to prevent out-of-bounds
* access (CWE-125) when scanning a deserialized database.
Expand Down Expand Up @@ -497,6 +599,12 @@ hs_error_t db_validate_rose_offsets(const hs_database_t *db) {
return HS_INVALID;
}

/* Validate Tamarama subengine offsets (CWE-125). */
if (unlikely(db_validate_tamarama_offsets(rose, rose_size) != HS_SUCCESS)) {
DEBUG_PRINTF("Tamarama subengine offset validation failed\n");
return HS_INVALID;
}

DEBUG_PRINTF("rose offset validation passed\n");
return HS_SUCCESS;
}
Expand Down