Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions c2rust-transpile/src/translator/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,41 @@ impl<'c> Translation<'c> {
method_name: &str,
args: &[CExprId],
) -> TranslationResult<WithStmts<Box<Expr>>> {
// The two operands can be distinct same-width types (e.g. `size_t`/`usize`
// vs `unsigned long`/`u64`), so coerce both to the output (pointee) type,
// which is also where `overflowing_*`'s result is stored.
let out_arg = *args.get(2).ok_or_else(|| {
TranslationError::generic("`convert_overflow_arith` must have exactly 3 arguments")
})?;
let result_ty_id = match self
.ast_context
.resolve_type(
self.ast_context
.index_unwrap_parens(out_arg)
.kind
.get_type()
.ok_or_else(|| {
TranslationError::generic("overflow builtin output has no type")
})?,
)
.kind
{
CTypeKind::Pointer(pointee) => pointee.ctype,
_ => {
return Err(TranslationError::generic(
"overflow builtin output is not a pointer",
))
}
};
let result_ty = self.convert_type(result_ty_id)?;

let args = self.convert_exprs(ctx.used(), args, None)?;
args.and_then_try(|args| {
let [a, b, c]: [_; 3] = args
.try_into()
.map_err(|_| "`convert_overflow_arith` must have exactly 3 arguments")?;
let a = mk().cast_expr(a, result_ty.clone());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gcc is being very loose with the type system here:

These built-in functions promote the first two operands into infinite precision signed type and perform addition on those promoted operands. The result is then cast to the type the third pointer argument points to and stored there. [...] The first built-in function allows arbitrary integral types for operands and the result type must be pointer to some integral type other than enumerated or boolean type

I'm not sure casting to the result type is correct here.

let b = mk().cast_expr(b, result_ty);
let overflowing = mk().method_call_expr(a, method_name, vec![b]);
let sum_name = self.renamer.borrow_mut().pick_name("c2rust_result");
let over_name = self.renamer.borrow_mut().pick_name("c2rust_overflowed");
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/builtins/src/overflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stddef.h>

void overflow_builtins(const unsigned buffer_size, int buffer[const])
{
int i = 0;
size_t sz = 1000;
unsigned long ul = 7;
size_t out_sz = 0;
unsigned long out_ul = 0;

// Operands of distinct same-width types (size_t vs unsigned long).
buffer[i++] = __builtin_mul_overflow(sz, ul, &out_sz);
buffer[i++] = (int)out_sz;
buffer[i++] = __builtin_add_overflow(sz, ul, &out_sz);
buffer[i++] = (int)out_sz;
buffer[i++] = __builtin_sub_overflow(ul, sz, &out_ul);
buffer[i++] = (int)out_ul;

// Actually overflowing.
buffer[i++] = __builtin_mul_overflow((size_t)-1, (unsigned long)2, &out_sz);
buffer[i++] = (int)out_sz;
}
17 changes: 17 additions & 0 deletions tests/unit/builtins/src/test_builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::alloca::rust_alloca_hello;
use crate::atomics::{rust_atomics_entry, rust_new_atomics};
use crate::math::{rust_ffs, rust_ffsl, rust_ffsll, rust_isfinite, rust_isinf_sign, rust_isnan};
use crate::mem_x_fns::{rust_assume_aligned, rust_mem_x};
use crate::overflow::rust_overflow_builtins;
use std::ffi::{c_char, c_double, c_int, c_long, c_longlong, c_uint};

#[link(name = "test")]
Expand All @@ -18,6 +19,7 @@ unsafe extern "C" {
fn isfinite(_: c_double) -> c_int;
fn isnan(_: c_double) -> c_int;
fn isinf_sign(_: c_double) -> c_int;
fn overflow_builtins(_: c_uint, _: *mut c_int);
}

const BUFFER_SIZE: usize = 1024;
Expand Down Expand Up @@ -62,6 +64,21 @@ pub fn test_new_atomics() {
}
}

#[test]
pub fn test_overflow_builtins() {
let mut buffer = [0; BUFFER_SIZE];
let mut rust_buffer = [0; BUFFER_SIZE];

unsafe {
overflow_builtins(BUFFER_SIZE as u32, buffer.as_mut_ptr());
rust_overflow_builtins(BUFFER_SIZE as u32, rust_buffer.as_mut_ptr());
}

for index in 0..BUFFER_SIZE {
assert_eq!(buffer[index], rust_buffer[index]);
}
}

#[test]
pub fn test_mem_fns() {
let const_string = "I am ten!\0";
Expand Down
Loading