Skip to content

Commit 81bf40f

Browse files
committed
Fix: stack buffer overflow in Lizard Huffman weight decoder
GHSL-2026-136 Credit: discovered and reported by GHSL team member @JarLob Remediation: add output bounds checking to FSE_decompress_usingDTable_generic()
1 parent d573cd2 commit 81bf40f

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

C/lizard/error_private.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,39 @@ ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) retu
8484
#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e
8585
#define CHECK_F(f) { CHECK_V_F(_var_err__, f); }
8686

87+
/**
88+
* Ignore: this is an internal helper.
89+
*
90+
* We want to force this function invocation to be syntactically correct, but
91+
* we don't want to force runtime evaluation of its arguments.
92+
*/
93+
#define _FORCE_HAS_FORMAT_STRING(...) \
94+
do { \
95+
if (0) { \
96+
_force_has_format_string(__VA_ARGS__); \
97+
} \
98+
} while (0)
99+
100+
#define ERR_QUOTE(str) #str
101+
102+
/**
103+
* Return the specified error if the condition evaluates to true.
104+
*
105+
* In debug modes, prints additional information.
106+
* In order to do that (particularly, printing the conditional that failed),
107+
* this can't just wrap RETURN_ERROR().
108+
*/
109+
#define RETURN_ERROR_IF(cond, err, ...) \
110+
do { \
111+
if (cond) { \
112+
RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \
113+
__FILE__, __LINE__, ERR_QUOTE(cond), ERR_QUOTE(ERROR(err))); \
114+
_FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
115+
RAWLOG(3, ": " __VA_ARGS__); \
116+
RAWLOG(3, "\n"); \
117+
return ERROR(err); \
118+
} \
119+
} while (0)
87120

88121
/*-****************************************
89122
* Error Strings

C/lizard/liz_fse_decompress.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits)
174174
return 0;
175175
}
176176

177+
/* update to zstd version /tr 2026-06-07 */
177178
FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
178179
void* dst, size_t maxDstSize,
179180
const void* cSrc, size_t cSrcSize,
@@ -194,6 +195,8 @@ FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
194195
FSE_initDState(&state1, &bitD, dt);
195196
FSE_initDState(&state2, &bitD, dt);
196197

198+
RETURN_ERROR_IF(BIT_reloadDStream(&bitD)==BIT_DStream_overflow, corruption_detected, "");
199+
197200
#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD)
198201

199202
/* 4 symbols per loop */
@@ -233,10 +236,10 @@ FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
233236
break;
234237
} }
235238

236-
return op-ostart;
239+
assert(op >= ostart);
240+
return (size_t)(op-ostart);
237241
}
238242

239-
240243
size_t FSE_decompress_usingDTable(void* dst, size_t originalSize,
241244
const void* cSrc, size_t cSrcSize,
242245
const FSE_DTable* dt)

0 commit comments

Comments
 (0)