From 0cca2bd3ceba99ae58c84c895fcd1c91decce72b Mon Sep 17 00:00:00 2001 From: Shubhangi Shrivastava Date: Wed, 15 Jul 2026 14:48:43 +0000 Subject: [PATCH 1/5] Checking som slots Signed-off-by: Shubhangi Shrivastava --- src/nfa/gough.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/nfa/gough.c b/src/nfa/gough.c index 44acd4c28..ded3ce965 100644 --- a/src/nfa/gough.c +++ b/src/nfa/gough.c @@ -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; + 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 (*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 */ } @@ -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 (*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) { @@ -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 (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) { From 2b30da1a328400679fd80fd7cfc8f769c62acada Mon Sep 17 00:00:00 2001 From: Shubhangi Shrivastava Date: Thu, 16 Jul 2026 10:22:37 +0000 Subject: [PATCH 2/5] Checking indices for instructions Signed-off-by: Shubhangi Shrivastava --- src/nfa/gough.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nfa/gough.c b/src/nfa/gough.c index ded3ce965..0574737bf 100644 --- a/src/nfa/gough.c +++ b/src/nfa/gough.c @@ -212,30 +212,39 @@ 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) / 8; 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 (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 (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 (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) { From 6a35438392b0da4536a6d051a2be8413dc117e49 Mon Sep 17 00:00:00 2001 From: Shubhangi Shrivastava Date: Fri, 17 Jul 2026 13:21:08 +0000 Subject: [PATCH 3/5] Adding optimization hint Signed-off-by: Shubhangi Shrivastava --- src/nfa/gough.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nfa/gough.c b/src/nfa/gough.c index 0574737bf..7232a3d1f 100644 --- a/src/nfa/gough.c +++ b/src/nfa/gough.c @@ -124,7 +124,7 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m, u64a from; if (*cached_accept_som == INVALID_SLOT) { from = loc; - } else if (*cached_accept_som >= num_som_slots) { + } else if (unlikely(*cached_accept_som >= num_som_slots)) { return MO_CONTINUE_MATCHING; } else { from = som->slots[*cached_accept_som]; @@ -155,7 +155,7 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m, u64a from; if (*cached_accept_som == INVALID_SLOT) { from = loc; - } else if (*cached_accept_som >= num_som_slots) { + } else if (unlikely(*cached_accept_som >= num_som_slots)) { return MO_CONTINUE_MATCHING; } else { from = som->slots[*cached_accept_som]; @@ -174,7 +174,7 @@ char doReports(NfaCallback cb, void *ctxt, const struct mcclellan *m, u64a from; if (slot == INVALID_SLOT) { from = loc; - } else if (slot >= num_som_slots) { + } else if (unlikely(slot >= num_som_slots)) { continue; } else { from = som->slots[slot]; @@ -224,7 +224,7 @@ void run_prog_i(UNUSED const struct NFA *nfa, case GOUGH_INS_END: return; case GOUGH_INS_MOV: - if (dest >= num_slots || src >= num_slots) { + if (unlikely(dest >= num_slots || src >= num_slots)) { return; } som->slots[dest] = som->slots[src]; @@ -233,7 +233,7 @@ void run_prog_i(UNUSED const struct NFA *nfa, /* note: c has already been advanced */ DEBUG_PRINTF("current offset %llu; adjust %u\n", som_offset, pc->src); - if (dest >= num_slots) { + if (unlikely(dest >= num_slots)) { return; } assert(som_offset >= pc->src); @@ -242,7 +242,7 @@ void run_prog_i(UNUSED const struct NFA *nfa, case GOUGH_INS_MIN: /* TODO: shift all values along by one so that a normal min works */ - if (dest >= num_slots || src >= num_slots) { + if (unlikely(dest >= num_slots || src >= num_slots)) { return; } if (som->slots[src] == GOUGH_SOM_EARLY) { From fd1a7c790cb3be4ab35c3dc133eddb54d97c1177 Mon Sep 17 00:00:00 2001 From: Shubhangi Shrivastava Date: Mon, 20 Jul 2026 08:37:50 +0000 Subject: [PATCH 4/5] Guarding num of slots Signed-off-by: Shubhangi Shrivastava --- src/nfa/gough.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nfa/gough.c b/src/nfa/gough.c index 7232a3d1f..68b524bc5 100644 --- a/src/nfa/gough.c +++ b/src/nfa/gough.c @@ -212,7 +212,9 @@ 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) / 8; + 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); From ea21009557b5666845b7ac9f67d7da46b7377ccc Mon Sep 17 00:00:00 2001 From: Shubhangi Shrivastava Date: Mon, 27 Jul 2026 16:05:06 +0530 Subject: [PATCH 5/5] Guarding NFA offsets Signed-off-by: Shubhangi Shrivastava --- src/database.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/database.c b/src/database.c index 6dce3908d..d636805d5 100644 --- a/src/database.c +++ b/src/database.c @@ -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; }