Skip to content

Commit d482884

Browse files
committed
feat: add index out of bounds check
1 parent f14abbc commit d482884

File tree

10 files changed

+172
-1
lines changed

10 files changed

+172
-1
lines changed

src/ast/builder/llvmbuilder.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,20 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for LLVMBuilder<'a, 'ctx> {
17511751
let f = self.get_llvm_value(f).unwrap().into_function_value();
17521752
f.get_name().to_str().unwrap() == "main"
17531753
}
1754+
1755+
fn build_global_string_ptr(&self, s: &str, name: &str) -> ValueHandle {
1756+
let s = self.builder.build_global_string_ptr(s, name).unwrap();
1757+
self.get_llvm_value_handle(&s.as_any_value_enum())
1758+
}
1759+
1760+
fn build_unreachable(&self) {
1761+
_ = self.builder.build_unreachable();
1762+
}
1763+
1764+
fn is_debug(&self) -> bool {
1765+
self.debug
1766+
}
1767+
17541768
fn tag_generator_ctx_as_root(&self, f: ValueHandle, ctx: &mut Ctx<'a>) {
17551769
let f = self.get_llvm_value(f).unwrap().into_function_value();
17561770
let allocab = f.get_first_basic_block().unwrap();
@@ -2371,7 +2385,7 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for LLVMBuilder<'a, 'ctx> {
23712385
if *self.optimized.borrow() {
23722386
return;
23732387
}
2374-
if !self.debug {
2388+
if !self.debug && self.optlevel as u32 >= 1 {
23752389
self.module.strip_debug_info();
23762390
}
23772391
self.module.verify().unwrap_or_else(|e| {

src/ast/builder/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub trait IRBuilder<'a, 'ctx> {
3939
fn get_global_var_handle(&self, name: &str) -> Option<ValueHandle>;
4040
fn new_subscope(&self, start: Pos);
4141
fn get_sp_handle(&self) -> ValueHandle;
42+
fn is_debug(&self) -> bool;
4243
fn add_global(
4344
&self,
4445
name: &str,
@@ -329,6 +330,8 @@ pub trait IRBuilder<'a, 'ctx> {
329330
fn is_main(&self, f: ValueHandle) -> bool;
330331
fn await_task(&self, ctx: &mut Ctx<'a>, task: ValueHandle) -> ValueHandle;
331332
fn await_ret(&self, ctx: &mut Ctx<'a>, ret: ValueHandle);
333+
fn build_global_string_ptr(&self, s: &str, name: &str) -> ValueHandle;
334+
fn build_unreachable(&self);
332335
}
333336

334337
/// ValueHandle is an index used to separate the low level generatted code inside [BuilderEnum] from the respective high level ast node

src/ast/builder/no_op_builder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,4 +631,16 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for NoOpBuilder<'a, 'ctx> {
631631
0
632632
}
633633
fn await_ret(&self, _ctx: &mut Ctx<'a>, _ret: ValueHandle) {}
634+
635+
fn is_debug(&self) -> bool {
636+
false
637+
}
638+
639+
fn build_global_string_ptr(&self, _s: &str, _name: &str) -> ValueHandle {
640+
0
641+
}
642+
643+
fn build_unreachable(&self) {
644+
// 什么都不做
645+
}
634646
}

src/ast/node/primary.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::cell::RefCell;
12
use std::sync::Arc;
23

34
use super::node_result::NodeResultBuilder;
45
use super::*;
56

67
use crate::ast::builder::BuilderEnum;
78
use crate::ast::builder::IRBuilder;
9+
use crate::ast::builder::IntPredicate;
810
use crate::ast::builder::ValueHandle;
911
use crate::ast::ctx::Ctx;
1012
use crate::ast::ctx::MacroReplaceNode;
@@ -366,6 +368,69 @@ impl Node for ArrayElementNode {
366368

367369
let elemptr: ValueHandle = {
368370
let index: &[ValueHandle; 1] = &[index_val];
371+
372+
// 在debug模式下添加越界检查
373+
if ctx.config.assert_index_out_of_bounds {
374+
// 获取数组大小
375+
let size_ptr = builder
376+
.build_struct_gep(arr, 2, "size_ptr", &pltype.borrow(), ctx)
377+
.unwrap();
378+
let size = builder.build_load(
379+
size_ptr,
380+
"arr_size",
381+
&PLType::Primitive(PriType::I64),
382+
ctx,
383+
);
384+
385+
// 创建比较:index >= size 或 index < 0
386+
let cmp_ge = builder.build_int_compare(
387+
IntPredicate::SGE,
388+
index_val,
389+
size,
390+
"index_ge_size",
391+
);
392+
let cmp_lt = builder.build_int_compare(
393+
IntPredicate::SLT,
394+
index_val,
395+
builder.int_value(&PriType::I64, 0, true),
396+
"index_lt_zero",
397+
);
398+
let out_of_bounds = builder.build_or(cmp_ge, cmp_lt, "out_of_bounds");
399+
400+
// 获取当前函数
401+
let current_func = ctx.function.unwrap();
402+
let error_block = builder.append_basic_block(current_func, "arr_index_error");
403+
let continue_block =
404+
builder.append_basic_block(current_func, "arr_index_continue");
405+
406+
builder.build_conditional_branch(out_of_bounds, error_block, continue_block);
407+
408+
// 错误处理块
409+
builder.position_at_end_block(error_block);
410+
411+
// 获取printf函数
412+
let printf_fn = builder
413+
.get_function("pl_index_out_of_bounds")
414+
.unwrap_or_else(|| {
415+
let ret_type = PLType::Void;
416+
let param_type = PLType::Primitive(PriType::I64);
417+
builder.add_function(
418+
"pl_index_out_of_bounds",
419+
&[param_type.clone(), param_type],
420+
ret_type,
421+
ctx,
422+
)
423+
});
424+
425+
// 调用printf输出错误信息
426+
builder.build_call(printf_fn, &[index_val, size], &PLType::Void, ctx, None);
427+
428+
builder.build_unreachable();
429+
430+
// 继续执行块
431+
builder.position_at_end_block(continue_block);
432+
}
433+
369434
let real_arr: ValueHandle = builder
370435
.build_struct_gep(arr, 1, "real_arr", &pltype.borrow(), ctx)
371436
.unwrap();

src/ast/test.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,58 @@ fn test_tail_call_opt() {
653653
drop(l);
654654
}
655655

656+
#[test]
657+
fn test_assert_index_out_of_bounds() {
658+
let l = crate::utils::plc_new::tests::TEST_COMPILE_MUTEX
659+
.lock()
660+
.unwrap();
661+
set_test_asset();
662+
let out = "testout3";
663+
let exe = PathBuf::from(out);
664+
#[cfg(target_os = "windows")]
665+
let exe = exe.with_extension("exe");
666+
_ = remove_file(&exe);
667+
use std::{path::PathBuf, process::Command};
668+
669+
use crate::ast::compiler::{compile, Options};
670+
671+
let docs = MemDocs::default();
672+
let db = Database::default();
673+
let input = MemDocsInput::new(
674+
&db,
675+
Arc::new(Mutex::new(docs)),
676+
"test/arr_bounds/main.pi".to_string(),
677+
Default::default(),
678+
ActionType::Compile,
679+
None,
680+
None,
681+
);
682+
compile(
683+
&db,
684+
input,
685+
out.to_string(),
686+
Options {
687+
optimization: crate::ast::compiler::HashOptimizationLevel::Less,
688+
genir: true,
689+
printast: false,
690+
flow: false,
691+
fmt: false,
692+
jit: false,
693+
debug: false,
694+
..Default::default()
695+
},
696+
);
697+
let exe = crate::utils::canonicalize(&exe)
698+
.unwrap_or_else(|_| panic!("static compiled file not found {:?}", exe));
699+
eprintln!("exec: {:?}", exe);
700+
let o = Command::new(exe.to_str().unwrap())
701+
.output()
702+
.expect("failed to execute compiled program");
703+
// should trigger index out of bounds, so status should be non-zero
704+
assert!(!o.status.success(), "should trigger index out of bounds");
705+
drop(l);
706+
}
707+
656708
#[cfg(test)]
657709
pub(crate) fn set_test_asset() {
658710
use std::time::SystemTime;

src/utils/read_config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ pub struct Config {
8484
/// and it's decided by the position of kagari.toml file
8585
#[serde(skip)]
8686
pub root: String,
87+
88+
/// Assert Index Out Of Bounds, default is false
89+
#[serde(default)]
90+
pub assert_index_out_of_bounds: bool,
8791
}
8892

8993
/// ConfigWrapper wraps a config, which represents all configuration of an entry node of a program.

test/Kagari.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
project = "project1"
22
entry = "main.pi"
33

4+
assert_index_out_of_bounds = true
45

56
[deps]
67
project2 = { path = "project2" }

test/arr_bounds/Kagari.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
entry = "main.pi"
2+
project = "arr_bounds"
3+
4+
assert_index_out_of_bounds = true

test/arr_bounds/main.pi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() i64 {
2+
let a = [1];
3+
let d = a[1];
4+
return 0;
5+
}

vm/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ fn pl_panic() {
4141
exit(1);
4242
}
4343

44+
#[is_runtime]
45+
fn pl_index_out_of_bounds(index: i64, len: i64) {
46+
println!(
47+
"index out of bounds occured! index: {}, len: {}",
48+
index, len
49+
);
50+
let bt = Backtrace::new();
51+
println!("{:?}", bt);
52+
exit(1);
53+
}
54+
4455
#[is_runtime]
4556
fn __cast_panic() {
4657
println!("invalid cast occured!");

0 commit comments

Comments
 (0)