From ad5c707f64cc8a7ca475686859e3d57e32807d2a Mon Sep 17 00:00:00 2001 From: Sang Woo Kim Date: Fri, 17 Jul 2026 23:43:51 +0900 Subject: [PATCH] sCalc: fix out-of-bounds read in lrc() on an empty operand lrc() looped with `i < strlen(rawInput)-1`. strlen() returns size_t, so for an empty operand strlen(rawInput)-1 wraps to SIZE_MAX and the loop condition is always true, reading rawInput[] far out of bounds. The LRC and AMODBUS operators reach lrc() with any string on the stack (sCalcPerform.c case LRC/AMODBUS), so an scalcout expression such as LRC('') or LRC applied to an empty field crashes the IOC with SIGSEGV. Compute the length once into a signed int and loop while `i+1 < len`, so an empty operand yields an empty loop (LRC "00") and non-empty operands are unchanged. The sibling xor8() already guards len==0. Add scalcTest cases: LRC('') -> "00" and LRC('F7031389000A') -> "60" (the LRC 0x60 from the AMODBUS example in the source comment). Without the fix scalcTest terminates with SIGSEGV on the empty-operand case. --- calcApp/src/sCalcPerform.c | 8 ++++++-- tests/scalcTest.cpp | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/calcApp/src/sCalcPerform.c b/calcApp/src/sCalcPerform.c index 31816ac..54795d2 100644 --- a/calcApp/src/sCalcPerform.c +++ b/calcApp/src/sCalcPerform.c @@ -241,10 +241,14 @@ int hex(char c) { int lrc(char *output, char *rawInput) { - int i; + int i, len; unsigned int lrc; - for (i=0, lrc=0; i=20) printf("lrc: adding %d\n", rawInput[i]*0x10 + rawInput[i+1]); } diff --git a/tests/scalcTest.cpp b/tests/scalcTest.cpp index 3efd27e..47dcc1f 100644 --- a/tests/scalcTest.cpp +++ b/tests/scalcTest.cpp @@ -106,7 +106,7 @@ MAIN(scalcTest) double args[12] = {A, B, C, D, E, F, G, H, I, J, K, L}; const char* sargs[12] = {AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL}; - testPlan(143); + testPlan(145); testValExpr("finite(1)", args, sargs, 1); testValExpr("isnan(1)", args, sargs, 0); @@ -288,6 +288,12 @@ MAIN(scalcTest) /* String subtract-first and subtract-last */ testSValExpr("'abca'-|'a'", args, sargs, "bca"); testSValExpr("'abca'|-'a'", args, sargs, "abc"); + + /* LRC on an empty operand: strlen(rawInput)-1 must not wrap size_t and + * read out of bounds. Empty -> empty loop -> lrc 0 -> "00". + * The documented AMODBUS example "F7031389000A" has LRC 0x60. */ + testSValExpr("LRC('')", args, sargs, "00"); + testSValExpr("LRC('F7031389000A')", args, sargs, "60"); /* UNTIL loops */ testValExpr("UNTIL(1)", args, sargs, 1.0);