Skip to content
Open
Show file tree
Hide file tree
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
55 changes: 55 additions & 0 deletions src/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,61 @@ hs_error_t dbIsValid(const hs_database_t *db) {
}
}

// Validate NfaInfo entries: ensure every fullStateOffset and stateOffset
// is within its respective buffer. This prevents out-of-bounds writes
// via forged NfaInfo fields (CWE-787).
if (rose->nfaInfoOffset) {
if (unlikely(rose->nfaInfoOffset >= rose->size)) {
DEBUG_PRINTF("nfaInfoOffset %u out of range (size=%u)\n",
rose->nfaInfoOffset, rose->size);
return HS_INVALID;
}

if (unlikely(rose->size < sizeof(struct NFA))) {
DEBUG_PRINTF("rose->size %u too small for NFA header\n",
rose->size);
return HS_INVALID;
}

u64a nfa_info_table_end = (u64a)rose->nfaInfoOffset +
(u64a)rose->queueCount * sizeof(struct NfaInfo);
if (unlikely(nfa_info_table_end > rose->size)) {
DEBUG_PRINTF("NfaInfo table overflows blob\n");
return HS_INVALID;
}

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

for (u32 qi = 0; qi < rose->queueCount; qi++) {
const struct NfaInfo *info = &infos[qi];

if (unlikely(info->nfaOffset == 0 ||
info->nfaOffset > rose->size - sizeof(struct NFA))) {
DEBUG_PRINTF("qi=%u: nfaOffset %u out of range (size=%u)\n",
qi, info->nfaOffset, rose->size);
return HS_INVALID;
}

const struct NFA *nfa =
(const struct NFA *)((const char *)rose + info->nfaOffset);

if (unlikely(info->fullStateOffset > rose->scratchStateSize ||
nfa->scratchStateSize >
rose->scratchStateSize - info->fullStateOffset)) {
DEBUG_PRINTF("qi=%u: fullStateOffset OOB\n", qi);
return HS_INVALID;
}

if (unlikely(info->stateOffset > rose->stateOffsets.end ||
nfa->streamStateSize >
rose->stateOffsets.end - info->stateOffset)) {
DEBUG_PRINTF("qi=%u: stateOffset OOB\n", qi);
return HS_INVALID;
}
}
}

return HS_SUCCESS;
}

Expand Down
47 changes: 40 additions & 7 deletions src/nfa/gough.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,18 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m,
DEBUG_PRINTF("reporting state = %hu, loc=%llu, eod %hhu\n",
(u16)(s & STATE_MASK), loc, eod);

const struct gough_info *gi = get_gough(m);
u32 num_som_slots = gi->stream_som_loc_count;

Comment thread
shubhangi-shrivastava marked this conversation as resolved.
if (!eod && s == *cached_accept_state) {
u64a from = *cached_accept_som == INVALID_SLOT ? loc
: som->slots[*cached_accept_som];
u64a from;
if (*cached_accept_som == INVALID_SLOT) {
from = loc;
} else if (unlikely(*cached_accept_som >= num_som_slots)) {
return MO_CONTINUE_MATCHING;
} else {
from = som->slots[*cached_accept_som];
}
if (cb(from, loc, *cached_accept_id, ctxt) == MO_HALT_MATCHING) {
return MO_HALT_MATCHING; /* termination requested */
}
Expand All @@ -143,8 +152,14 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m,
*cached_accept_id = rl->report[0].r;
*cached_accept_som = rl->report[0].som;

u64a from = *cached_accept_som == INVALID_SLOT ? loc
: som->slots[*cached_accept_som];
u64a from;
if (*cached_accept_som == INVALID_SLOT) {
from = loc;
} else if (unlikely(*cached_accept_som >= num_som_slots)) {
return MO_CONTINUE_MATCHING;
} else {
from = som->slots[*cached_accept_som];
}
DEBUG_PRINTF("reporting %u, using som[%u]=%llu\n", rl->report[0].r,
*cached_accept_som, from);
if (cb(from, loc, *cached_accept_id, ctxt) == MO_HALT_MATCHING) {
Expand All @@ -156,7 +171,14 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m,

for (u32 i = 0; i < count; i++) {
u32 slot = rl->report[i].som;
u64a from = slot == INVALID_SLOT ? loc : som->slots[slot];
u64a from;
if (slot == INVALID_SLOT) {
from = loc;
} else if (unlikely(slot >= num_som_slots)) {
continue;
} else {
from = som->slots[slot];
}
DEBUG_PRINTF("reporting %u, using som[%u] = %llu\n",
rl->report[i].r, slot, from);
if (cb(from, loc, rl->report[i].r, ctxt) == MO_HALT_MATCHING) {
Expand Down Expand Up @@ -190,30 +212,41 @@ void run_prog_i(UNUSED const struct NFA *nfa,
const struct gough_ins *pc, u64a som_offset,
struct gough_som_info *som) {
DEBUG_PRINTF("run prog at som_offset of %llu\n", som_offset);
u32 num_slots = nfa->scratchStateSize > 16
? (nfa->scratchStateSize - 16) / sizeof(u64a)
: 0;
while (1) {
assert((const u8 *)pc >= (const u8 *)nfa);
assert((const u8 *)pc < (const u8 *)nfa + nfa->length);
u32 dest = pc->dest;
u32 src = pc->src;
assert(pc->op == GOUGH_INS_END
|| dest < (nfa->scratchStateSize - 16) / 8);
assert(pc->op == GOUGH_INS_END || dest < num_slots);
DEBUG_PRINTF("%s %u %u\n", dump_op(pc->op), dest, src);
switch (pc->op) {
case GOUGH_INS_END:
return;
case GOUGH_INS_MOV:
if (unlikely(dest >= num_slots || src >= num_slots)) {
return;
}
som->slots[dest] = som->slots[src];
break;
case GOUGH_INS_NEW:
/* note: c has already been advanced */
DEBUG_PRINTF("current offset %llu; adjust %u\n", som_offset,
pc->src);
if (unlikely(dest >= num_slots)) {
return;
}
assert(som_offset >= pc->src);
som->slots[dest] = som_offset - pc->src;
break;
case GOUGH_INS_MIN:
/* TODO: shift all values along by one so that a normal min works
*/
if (unlikely(dest >= num_slots || src >= num_slots)) {
return;
}
if (som->slots[src] == GOUGH_SOM_EARLY) {
som->slots[dest] = som->slots[src];
} else if (som->slots[dest] != GOUGH_SOM_EARLY) {
Expand Down