fix: 全面代码审查 — 修复 3 CRITICAL + 4 HIGH 问题
CRITICAL: - parser: 6处栈数组加边界检查 (struct_init/decl/block/params/functions/structs) - codegen: return前跳过返回值alloca防止use-after-free - ast: NEW宏加NULL检查防止arena耗尽崩溃 HIGH: - main: shell元字符过滤防命令注入 - codegen: LLVMContext泄漏修复 (out_context参数) - codegen: f64隐式return用LLVMConstReal替代LLVMConstInt - sema: 返回类型与函数声明校验 其他: - parser/codegen: 递归深度限制1000层 - codegen: struct值类型不追踪cleanup (栈上数据不能free) 基于三份审查报告 (架构/code quality/安全) 修复。
This commit is contained in:
+31
-8
@@ -4,6 +4,10 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// === 递归深度限制
|
||||
static int codegen_depth = 0;
|
||||
#define MAX_CODEGEN_DEPTH 1000
|
||||
|
||||
// === 内部状态 ===
|
||||
typedef struct VarEntry {
|
||||
const char* name;
|
||||
@@ -387,15 +391,13 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
LLVMBuildStore(ctx->builder, init_val, alloca);
|
||||
add_var(ctx, node->as.let_stmt.name, alloca);
|
||||
|
||||
// 自动内存管理: str 堆分配追踪
|
||||
// 只有 BINARY_EXPR (拼接) 和 STRUCT_INIT 产生堆内存
|
||||
// 自动内存管理: 只追踪 str 堆分配 (拼接/malloc)
|
||||
// struct 是栈上值类型,不能 free();含 str 字段时 v0.5 扩展
|
||||
if (node->as.let_stmt.init->type.kind == TYPE_STR) {
|
||||
AstKind ik = node->as.let_stmt.init->kind;
|
||||
if (ik == AST_BINARY_EXPR || ik == AST_STRUCT_INIT || ik == AST_CALL_EXPR) {
|
||||
if (ik == AST_BINARY_EXPR || ik == AST_CALL_EXPR) {
|
||||
cleanup_add(ctx, alloca);
|
||||
}
|
||||
} else if (node->as.let_stmt.init->type.kind == TYPE_STRUCT) {
|
||||
cleanup_add(ctx, alloca); // struct 可能含 str 字段
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -421,6 +423,20 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
ret_val = codegen_expr(ctx, node->as.return_stmt.expr);
|
||||
if (!ret_val) return;
|
||||
}
|
||||
// 如果返回的是 str 类型的变量,从清理列表移除以防止 use-after-free
|
||||
if (has_val && node->as.return_stmt.expr->type.kind == TYPE_STR &&
|
||||
node->as.return_stmt.expr->kind == AST_IDENT_EXPR) {
|
||||
LLVMValueRef alloca = find_var(ctx, node->as.return_stmt.expr->as.ident.name);
|
||||
if (alloca) {
|
||||
for (size_t i = 0; i < ctx->cleanup_count; i++) {
|
||||
if (ctx->cleanup_list[i] == alloca) {
|
||||
ctx->cleanup_list[i] = ctx->cleanup_list[ctx->cleanup_count - 1];
|
||||
ctx->cleanup_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// return 前释放当前作用域所有 str 堆分配
|
||||
cleanup_emit(ctx, 0);
|
||||
// 然后 emit ret
|
||||
@@ -430,11 +446,13 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
}
|
||||
|
||||
case AST_BLOCK: {
|
||||
if (++codegen_depth > MAX_CODEGEN_DEPTH) { codegen_depth--; return; }
|
||||
size_t block_mark = ctx->cleanup_count;
|
||||
for (size_t i = 0; i < node->as.block.stmt_count; i++) {
|
||||
codegen_stmt(ctx, node->as.block.stmts[i]);
|
||||
}
|
||||
cleanup_emit(ctx, block_mark); // 作用域退出: 释放块内 str 堆分配
|
||||
codegen_depth--;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -498,12 +516,14 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
|
||||
// === 程序级代码生成 ===
|
||||
LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
const char* name, const char** error_msg) {
|
||||
const char* name, const char** error_msg,
|
||||
LLVMContextRef* out_context) {
|
||||
CgCtx ctx = {0};
|
||||
ctx.arena = codegen_arena;
|
||||
ctx.context = LLVMContextCreate();
|
||||
if (!ctx.context) {
|
||||
*error_msg = "无法创建 LLVM Context";
|
||||
*out_context = NULL;
|
||||
return NULL;
|
||||
}
|
||||
ctx.module = LLVMModuleCreateWithNameInContext(name, ctx.context);
|
||||
@@ -616,7 +636,9 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
LLVMBuildRetVoid(ctx.builder);
|
||||
else
|
||||
LLVMBuildRet(ctx.builder,
|
||||
LLVMConstInt(to_llvm_type(&ctx, fn->as.function.return_type), 0, false));
|
||||
(fn->as.function.return_type == TYPE_F64
|
||||
? LLVMConstReal(to_llvm_type(&ctx, TYPE_F64), 0.0)
|
||||
: LLVMConstInt(to_llvm_type(&ctx, fn->as.function.return_type), 0, false)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,10 +647,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
if (LLVMVerifyModule(ctx.module, LLVMReturnStatusAction, &verify_err)) {
|
||||
*error_msg = verify_err ? verify_err : "模块验证失败(错误消息为 NULL)";
|
||||
LLVMDisposeBuilder(ctx.builder);
|
||||
LLVMContextDispose(ctx.context);
|
||||
*out_context = ctx.context;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LLVMDisposeBuilder(ctx.builder);
|
||||
*out_context = ctx.context;
|
||||
return ctx.module;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// codegen_arena 用于内部分配(VarEntry/FnEntry 等),需在整个 Module 生命周期保持存活。
|
||||
// 出错时返回 NULL 并设置 *error_msg。
|
||||
LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
const char* module_name, const char** error_msg);
|
||||
const char* module_name, const char** error_msg,
|
||||
LLVMContextRef* out_context);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user