Missing Negative Length Validation in _lou_backTranslate Leading to negative-size-param Crash
Summary
_lou_backTranslate does not validate that *outlen is non-negative before using it in a memset call, causing an AddressSanitizer negative-size-param abort. The symmetric forward-translation function _lou_translate contains the exact guard that is missing here.
Version
$ git describe
v3.34.0-383-g040cc859
Description
In liblouis/lou_backTranslateString.c, the function _lou_backTranslate validates only that pointers are non-NULL (line 212) and that the table loads successfully (line 217). It never checks whether *inlen or *outlen are negative. Execution then reaches line 257:
// lou_backTranslateString.c
/* line 212 – only NULL-pointer check, no value check */
if (tableList == NULL || inbuf == NULL || inlen == NULL || outbuf == NULL ||
outlen == NULL)
return 0;
if (displayTableList == NULL) displayTableList = tableList;
_lou_getTable(tableList, displayTableList, &table, &displayTable);
if (table == NULL) return 0; /* line 217 – no *inlen/*outlen guard here */
/* ... setup code ... */
typebuf = (unsigned char *)typeform;
/* line 257 – crash: *outlen is negative, but passed as size_t to memset */
if (typebuf != NULL) memset(typebuf, 0, *outlen * sizeof(formtype));
When *outlen is a large negative int (e.g. -210865563), the expression *outlen * sizeof(formtype) is evaluated as (int) × (size_t). C integer promotion converts the int to size_t, yielding a huge positive value (-210865563 * 2 = 0xFFFFFFFF974E5F0A on 64-bit), triggering ASAN's negative-size-param detector.
A secondary issue is that getStringBuffer(*outlen) on line 243 is also called with the negative value before the memset is reached, which can corrupt the internal string buffer pool.
The fix already exists in the forward-translation path (lou_translateString.c, line 1198):
/* _lou_translate – has the guard that _lou_backTranslate is missing */
if (table == NULL || *inlen < 0 || *outlen < 0) return 0; // line 1198
PoC Code
#include <cstddef>
#include <cstdint>
#include "liblouis.h"
static void avoid_log(logLevels level, const char *msg) {
(void)level;
(void)msg;
}
static void __attribute__((destructor)) free_resources(void) {
lou_free();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t * /*data*/, size_t /*size*/) {
lou_registerLogCallback(avoid_log);
lou_setDataPath("/root/FuzzAgent/output/liblouis/build/sanitizer/share/liblouis/tables");
const int BUF_SIZE = 256;
widechar inbuf[BUF_SIZE] = {};
widechar outbuf[BUF_SIZE] = {};
formtype typeform[BUF_SIZE] = {};
for (int i = 0; i < BUF_SIZE; i++)
inbuf[i] = (widechar)(i % 256);
// Negative outlen triggers: memset(typebuf, 0, *outlen * sizeof(formtype))
// with a negative size → AddressSanitizer: negative-size-param
int inlen = BUF_SIZE;
int outlen = -210865563;
lou_backTranslateString("bg.ctb", inbuf, &inlen, outbuf, &outlen,
typeform, nullptr, 0);
return 0;
}
Reproduction Steps
# You need to modify the lou data path in poc.cpp
clang++ -g -O0 -fsanitize=address,undefined,fuzzer \
-I<build>/include poc.cpp -o poc \
-Wl,--start-group <build>/lib/lib*.a -Wl,--end-group
./poc
Stack Trace
==487961==ERROR: AddressSanitizer: negative-size-param: (size=-421731126)
#0 0x5c106fba4c6c in __asan_memset (/root/FuzzAgent/output/liblouis/crash_reports/crash_004/poc+0x15ec6c) (BuildId: e5ade6ea74e866ae6f88ce10ff400dbf5ec9c40d)
#1 0x5c106fc49be7 in _lou_backTranslate /tmp/build_tmp/liblouis/lou_backTranslateString.c:257:23
#2 0x5c106fc485ce in lou_backTranslate /tmp/build_tmp/liblouis/lou_backTranslateString.c:179:9
#3 0x5c106fc48520 in lou_backTranslateString /tmp/build_tmp/liblouis/lou_backTranslateString.c:171:9
#4 0x5c106fbeca0e in LLVMFuzzerTestOneInput /root/FuzzAgent/output/liblouis/crash_reports/crash_004/poc.cpp:37:5
Address 0x7526aac004a0 is located in stack of thread T0 at offset 1184 in frame
#0 0x5c106fbec407 in LLVMFuzzerTestOneInput /root/FuzzAgent/output/liblouis/crash_reports/crash_004/poc.cpp:20
Suggested Fix
Add the same negative-value guard that already exists in _lou_translate (lou_translateString.c:1198):
--- a/liblouis/lou_backTranslateString.c
+++ b/liblouis/lou_backTranslateString.c
@@ -217,6 +217,7 @@ _lou_backTranslate(...)
_lou_getTable(tableList, displayTableList, &table, &displayTable);
if (table == NULL) return 0;
+ if (*inlen < 0 || *outlen < 0) return 0;
if (!_lou_isValidMode(mode))
Signed-off-by: FuzzAnything fuzzanything@gmail.com
Missing Negative Length Validation in
_lou_backTranslateLeading tonegative-size-paramCrashSummary
_lou_backTranslatedoes not validate that*outlenis non-negative before using it in amemsetcall, causing an AddressSanitizernegative-size-paramabort. The symmetric forward-translation function_lou_translatecontains the exact guard that is missing here.Version
Description
In
liblouis/lou_backTranslateString.c, the function_lou_backTranslatevalidates only that pointers are non-NULL (line 212) and that the table loads successfully (line 217). It never checks whether*inlenor*outlenare negative. Execution then reaches line 257:When
*outlenis a large negativeint(e.g.-210865563), the expression*outlen * sizeof(formtype)is evaluated as(int) × (size_t). C integer promotion converts theinttosize_t, yielding a huge positive value (-210865563 * 2 = 0xFFFFFFFF974E5F0Aon 64-bit), triggering ASAN'snegative-size-paramdetector.A secondary issue is that
getStringBuffer(*outlen)on line 243 is also called with the negative value before thememsetis reached, which can corrupt the internal string buffer pool.The fix already exists in the forward-translation path (
lou_translateString.c, line 1198):PoC Code
Reproduction Steps
Stack Trace
Suggested Fix
Add the same negative-value guard that already exists in
_lou_translate(lou_translateString.c:1198):