aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-15 08:20:37 +0800
committer_ov4 <ov4@ov4.org>2026-10-15 08:20:37 +0800
commitab86ae0284469a9a941f5b196f9cc99a90305e3a (patch)
tree2e22e00e6b2c9465be964a58e10dd71f9aa457b4
parentbb64298ef26c9b42ae1bb50fb92b15e509b48c07 (diff)
fix(codegen): prevent NUM_TO_STR buffer overflow
Size REAL buffers for the full fixed-point double range and bound writes with snprintf. Register MinGW CRT aliases for embedded execution. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-rw-r--r--AGENTS/PITFALLS.md4
-rw-r--r--include/cps/StringConversionHandler.h2
-rw-r--r--lib/CodeGen/FunctionGen.cc2
-rw-r--r--lib/CodeGen/StringConversionHandler.cc11
-rw-r--r--tools/driver/EmbeddedRunner.cc13
5 files changed, 21 insertions, 11 deletions
diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md
index 47910ce..0fe61fe 100644
--- a/AGENTS/PITFALLS.md
+++ b/AGENTS/PITFALLS.md
@@ -30,8 +30,8 @@ test programs.
must be a variable"; annotate `BYVAL` explicitly.
- **`/` always yields REAL** (double FDiv), even for two INTEGERs; integer division is
DIV/MOD only. CHR returns a STRING (2-byte heap buffer), not CHAR. STR_TO_NUM always
- returns REAL. NUM_TO_STR formats INTEGER with sprintf `"%lld"` (the full signed
- i64 range) and REAL with `"%f"`.
+ returns REAL. NUM_TO_STR formats INTEGER with bounded snprintf `"%lld"` (the full signed
+ i64 range) and REAL with `"%f"`. REAL buffers cover the full fixed-point double range.
- **`getExprTypeInfo` returns nullptr** for expressions it can't classify (null-check it
at any new call site) and internally defaults to INTEGER for arithmetic binaries and
unknown-function calls; OUTPUT reports an error for a nullptr result.
diff --git a/include/cps/StringConversionHandler.h b/include/cps/StringConversionHandler.h
index 9637620..c4879aa 100644
--- a/include/cps/StringConversionHandler.h
+++ b/include/cps/StringConversionHandler.h
@@ -10,7 +10,7 @@ class StringConversionHandler {
llvm::IRBuilder<> &Builder;
llvm::Module &Module;
- llvm::FunctionCallee SprintfFunc;
+ llvm::FunctionCallee SnprintfFunc;
llvm::FunctionCallee StrtollFunc;
llvm::FunctionCallee StrtodFunc;
llvm::FunctionCallee MallocFunc;
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index 978970a..ca8fa45 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -11,7 +11,7 @@ using namespace cps;
static bool isReservedRuntimeName(const std::string &Name) {
static const char *Reserved[] = {
"main", "printf", "scanf", "malloc", "free", "strlen", "memcpy",
- "strcpy", "strcat", "toupper", "tolower", "sprintf", "strtol", "strtoll",
+ "strcpy", "strcat", "toupper", "tolower", "sprintf", "snprintf", "strtol", "strtoll",
"strtod", "memset", "exit",
"fopen", "fclose", "fgetc", "ungetc", "fputs", "fputc",
"realloc", "strcmp", "__cxa_allocate_exception", "__cxa_throw",
diff --git a/lib/CodeGen/StringConversionHandler.cc b/lib/CodeGen/StringConversionHandler.cc
index 6f7461c..2263ef1 100644
--- a/lib/CodeGen/StringConversionHandler.cc
+++ b/lib/CodeGen/StringConversionHandler.cc
@@ -1,4 +1,5 @@
#include "cps/StringConversionHandler.h"
+#include <limits>
using namespace llvm;
using namespace cps;
@@ -9,8 +10,8 @@ StringConversionHandler::StringConversionHandler(LLVMContext &Ctx, IRBuilder<> &
}
void StringConversionHandler::setupExternalFunctions() {
- FunctionType *SprintfType = FunctionType::get(Type::getInt32Ty(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, true);
- SprintfFunc = Module.getOrInsertFunction("sprintf", SprintfType);
+ FunctionType *SnprintfType = FunctionType::get(Type::getInt32Ty(Context), {PointerType::getUnqual(Context), Type::getInt64Ty(Context), PointerType::getUnqual(Context)}, true);
+ SnprintfFunc = Module.getOrInsertFunction("snprintf", SnprintfType);
FunctionType *StrtollType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context), Type::getInt32Ty(Context)}, false);
StrtollFunc = Module.getOrInsertFunction("strtoll", StrtollType);
@@ -23,7 +24,9 @@ void StringConversionHandler::setupExternalFunctions() {
}
Value *StringConversionHandler::emitNumToStr(Value *Num, bool IsReal) {
- Value *AllocSize = ConstantInt::get(Type::getInt64Ty(Context), 64);
+ // Allow an extra integer digit, sign, decimal point, six fractional digits and NUL.
+ Value *AllocSize = ConstantInt::get(Type::getInt64Ty(Context),
+ IsReal ? std::numeric_limits<double>::max_exponent10 + 10 : 64);
Value *Buffer = Builder.CreateCall(MallocFunc, AllocSize, "num_str_buf");
Value *FormatStr;
@@ -33,7 +36,7 @@ Value *StringConversionHandler::emitNumToStr(Value *Num, bool IsReal) {
FormatStr = Builder.CreateGlobalString("%lld");
}
- Builder.CreateCall(SprintfFunc, {Buffer, FormatStr, Num});
+ Builder.CreateCall(SnprintfFunc, {Buffer, AllocSize, FormatStr, Num});
return Buffer;
}
diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc
index a654d5a..cad1f84 100644
--- a/tools/driver/EmbeddedRunner.cc
+++ b/tools/driver/EmbeddedRunner.cc
@@ -8,11 +8,13 @@
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/Cloning.h"
+#include <cstdio>
+#include <cstdlib>
#include <cxxabi.h>
#include <typeinfo>
#include <unwind.h>
-// Static C++ runtime symbols are not available through dlsym. Register their
+// Static runtime symbols are not available through dlsym. Register their
// actual addresses so the JIT and its unwind tables use the host's runtime.
extern "C" int
#if defined(__SEH__)
@@ -24,8 +26,13 @@ __gxx_personality_v0
#endif
(int, _Unwind_Action, _Unwind_Exception_Class, _Unwind_Exception *, _Unwind_Context *);
-static void registerExceptionSymbols() {
+static void registerRuntimeSymbols() {
using llvm::sys::DynamicLibrary;
+#ifdef __MINGW32__
+ // Use the same CRT aliases as native IR linking, including its stdio formatting.
+ DynamicLibrary::AddSymbol("snprintf", reinterpret_cast<void *>(&__ms_snprintf));
+ DynamicLibrary::AddSymbol("strtoll", reinterpret_cast<void *>(&std::strtoll));
+#endif
DynamicLibrary::AddSymbol("__cxa_allocate_exception", reinterpret_cast<void *>(&__cxxabiv1::__cxa_allocate_exception));
DynamicLibrary::AddSymbol("__cxa_throw", reinterpret_cast<void *>(&__cxxabiv1::__cxa_throw));
DynamicLibrary::AddSymbol("__cxa_begin_catch", reinterpret_cast<void *>(&__cxxabiv1::__cxa_begin_catch));
@@ -52,7 +59,7 @@ int runEmbedded(const cps::CodeGen &CG) {
llvm::errs() << "error: cannot load process symbols: " << Error << "\n";
return 1;
}
- registerExceptionSymbols();
+ registerRuntimeSymbols();
// Keep CodeGen's module and context alive while the engine owns its clone.
auto Module = llvm::CloneModule(CG.getModule());
Module->setTargetTriple(llvm::Triple(llvm::sys::getProcessTriple()));