Skip to content

Commit 0dc6e72

Browse files
committed
Made build_struct_gep safe. Fixes rust-lang#156
1 parent 79961f4 commit 0dc6e72

File tree

2 files changed

+82
-16
lines changed

2 files changed

+82
-16
lines changed

src/builder.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,57 @@ impl<'ctx> Builder<'ctx> {
197197
PointerValue::new(value)
198198
}
199199

200-
// REVIEW: Shouldn't this take a StructValue? Or does it still need to be PointerValue<StructValue>?
201-
// I think it's the latter. This might not be as unsafe as regular GEP. Should check to see if it lets us
202-
// go OOB. Maybe we could use the PointerValue<StructValue>'s struct info to do bounds checking...
203-
/// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future.
204-
pub unsafe fn build_struct_gep(&self, ptr: PointerValue<'ctx>, index: u32, name: &str) -> PointerValue<'ctx> {
205-
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
200+
/// Builds a GEP instruction on a struct pointer. Returns `Err(())` if input `PointerValue` doesn't
201+
/// point to a struct or if index is out of bounds.
202+
///
203+
/// # Example
204+
///
205+
/// ```no_run
206+
/// use inkwell::AddressSpace;
207+
/// use inkwell::context::Context;
208+
///
209+
/// let context = Context::create();
210+
/// let builder = context.create_builder();
211+
/// let module = context.create_module("struct_gep");
212+
/// let void_type = context.void_type();
213+
/// let i32_ty = context.i32_type();
214+
/// let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic);
215+
/// let field_types = &[i32_ty.into(), i32_ty.into()];
216+
/// let struct_ty = context.struct_type(field_types, false);
217+
/// let struct_ptr_ty = struct_ty.ptr_type(AddressSpace::Generic);
218+
/// let fn_type = void_type.fn_type(&[i32_ptr_ty.into(), struct_ptr_ty.into()], false);
219+
/// let fn_value = module.add_function("", fn_type, None);
220+
/// let entry = context.append_basic_block(fn_value, "entry");
221+
///
222+
/// builder.position_at_end(entry);
223+
///
224+
/// let i32_ptr = fn_value.get_first_param().unwrap().into_pointer_value();
225+
/// let struct_ptr = fn_value.get_last_param().unwrap().into_pointer_value();
226+
///
227+
/// assert!(builder.build_struct_gep(i32_ptr, 0, "struct_gep").is_err());
228+
/// assert!(builder.build_struct_gep(i32_ptr, 10, "struct_gep").is_err());
229+
/// assert!(builder.build_struct_gep(struct_ptr, 0, "struct_gep").is_ok());
230+
/// assert!(builder.build_struct_gep(struct_ptr, 1, "struct_gep").is_ok());
231+
/// assert!(builder.build_struct_gep(struct_ptr, 2, "struct_gep").is_err());
232+
/// ```
233+
pub fn build_struct_gep(&self, ptr: PointerValue<'ctx>, index: u32, name: &str) -> Result<PointerValue<'ctx>, ()> {
234+
let ptr_ty = ptr.get_type();
235+
let pointee_ty = ptr_ty.get_element_type();
206236

207-
let value = LLVMBuildStructGEP(self.builder, ptr.as_value_ref(), index, c_string.as_ptr());
237+
if !pointee_ty.is_struct_type() {
238+
return Err(());
239+
}
208240

209-
PointerValue::new(value)
241+
let struct_ty = pointee_ty.into_struct_type();
242+
243+
if index >= struct_ty.count_fields() {
244+
return Err(());
245+
}
246+
247+
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
248+
let value = unsafe { LLVMBuildStructGEP(self.builder, ptr.as_value_ref(), index, c_string.as_ptr()) };
249+
250+
Ok(PointerValue::new(value))
210251
}
211252

212253
/// Builds an instruction which calculates the difference of two pointers.

tests/all/test_builder.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
extern crate inkwell;
2-
3-
use self::inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, OptimizationLevel};
4-
use self::inkwell::context::Context;
5-
use self::inkwell::values::BasicValue;
1+
use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, OptimizationLevel};
2+
use inkwell::context::Context;
3+
use inkwell::values::BasicValue;
64

75
// use std::ffi::CString;
86
use std::ptr::null;
@@ -567,7 +565,7 @@ fn test_vector_binary_ops() {
567565
let p1_vec = fn_value.get_first_param().unwrap().into_vector_value();
568566
let p2_vec = fn_value.get_nth_param(1).unwrap().into_vector_value();
569567
let p3_vec = fn_value.get_nth_param(2).unwrap().into_vector_value();
570-
let compared_vec = builder.build_float_compare(self::inkwell::FloatPredicate::OLT, p1_vec, p2_vec, "compared_vec");
568+
let compared_vec = builder.build_float_compare(inkwell::FloatPredicate::OLT, p1_vec, p2_vec, "compared_vec");
571569
let multiplied_vec = builder.build_int_mul(compared_vec, p3_vec, "multiplied_vec");
572570
builder.build_return(Some(&multiplied_vec));
573571
assert!(fn_value.verify(true));
@@ -686,7 +684,7 @@ fn test_alignment_bytes() {
686684
}
687685

688686
#[llvm_versions(8.0..=latest)]
689-
fn run_memcpy_on<'ctx>(context: &'ctx Context, module: &self::inkwell::module::Module<'ctx>, alignment: u32) -> Result<(), &'static str> {
687+
fn run_memcpy_on<'ctx>(context: &'ctx Context, module: &inkwell::module::Module<'ctx>, alignment: u32) -> Result<(), &'static str> {
690688
let i32_type = context.i32_type();
691689
let i64_type = context.i64_type();
692690
let array_len = 4;
@@ -750,7 +748,7 @@ fn test_memcpy() {
750748
}
751749

752750
#[llvm_versions(8.0..=latest)]
753-
fn run_memmove_on<'ctx>(context: &'ctx Context, module: &self::inkwell::module::Module<'ctx>, alignment: u32) -> Result<(), &'static str> {
751+
fn run_memmove_on<'ctx>(context: &'ctx Context, module: &inkwell::module::Module<'ctx>, alignment: u32) -> Result<(), &'static str> {
754752
let i32_type = context.i32_type();
755753
let i64_type = context.i64_type();
756754
let array_len = 4;
@@ -984,3 +982,30 @@ fn test_cmpxchg() {
984982
let result = builder.build_cmpxchg(ptr_value, zero_value, neg_one_value, AtomicOrdering::Monotonic, AtomicOrdering::Monotonic);
985983
assert!(result.is_err());
986984
}
985+
986+
#[test]
987+
fn test_safe_struct_gep() {
988+
let context = Context::create();
989+
let builder = context.create_builder();
990+
let module = context.create_module("struct_gep");
991+
let void_type = context.void_type();
992+
let i32_ty = context.i32_type();
993+
let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic);
994+
let field_types = &[i32_ty.into(), i32_ty.into()];
995+
let struct_ty = context.struct_type(field_types, false);
996+
let struct_ptr_ty = struct_ty.ptr_type(AddressSpace::Generic);
997+
let fn_type = void_type.fn_type(&[i32_ptr_ty.into(), struct_ptr_ty.into()], false);
998+
let fn_value = module.add_function("", fn_type, None);
999+
let entry = context.append_basic_block(fn_value, "entry");
1000+
1001+
builder.position_at_end(entry);
1002+
1003+
let i32_ptr = fn_value.get_first_param().unwrap().into_pointer_value();
1004+
let struct_ptr = fn_value.get_last_param().unwrap().into_pointer_value();
1005+
1006+
assert!(builder.build_struct_gep(i32_ptr, 0, "struct_gep").is_err());
1007+
assert!(builder.build_struct_gep(i32_ptr, 10, "struct_gep").is_err());
1008+
assert!(builder.build_struct_gep(struct_ptr, 0, "struct_gep").is_ok());
1009+
assert!(builder.build_struct_gep(struct_ptr, 1, "struct_gep").is_ok());
1010+
assert!(builder.build_struct_gep(struct_ptr, 2, "struct_gep").is_err());
1011+
}

0 commit comments

Comments
 (0)