Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ABI code generates oversize loads when returning with a cast #45543

Open
arielb1 opened this issue Oct 26, 2017 · 10 comments
Open

ABI code generates oversize loads when returning with a cast #45543

arielb1 opened this issue Oct 26, 2017 · 10 comments
Assignees
Labels
A-codegen Area: Code generation A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@arielb1
Copy link
Contributor

arielb1 commented Oct 26, 2017

Meta

$ rustc --version
rustc 1.22.0-nightly (185cc5f26 2017-10-02)

STR

#![crate_type="rlib"]

#[no_mangle]
pub extern "C" fn foo(x: &(i8, i8, i8)) -> (i8, i8, i8) {
    *x
}

target is x86_64-unknown-linux-gnu

Expected Result

Code generated should not have UB

Actual Result

The generated no-opt LLVM IR is:

; ModuleID = 'cast.cgu-0.rs'
source_filename = "cast.cgu-0.rs"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: nounwind uwtable
define i32 @foo({ i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* noalias readonly dereferenceable(3)) unnamed_addr #0 {
start:
  %_3 = alloca { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }
  ; COMMENT MINE: %_0 has size 3
  %_0 = alloca { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }
  %1 = bitcast { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* %0 to i8*
  %2 = bitcast { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* %_3 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* %1, i64 3, i32 1, i1 false)
  %3 = bitcast { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* %_3 to i8*
  %4 = bitcast { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* %_0 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %3, i64 3, i32 1, i1 false)
  ; COMMENT MINE it is cast to an i32 here
  %5 = bitcast { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }* %_0 to i32*
  ; COMMENT MINE and then loaded as through it was size 4
  %6 = load i32, i32* %5, align 1
  ret i32 %6
}

; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #1

attributes #0 = { nounwind uwtable "probe-stack"="__rust_probestack" }
attributes #1 = { argmemonly nounwind }

This loads 4 bytes of an alloca of size 3. I couldn't trivially turn this to an end-to-end mis-code-generation (this can't be used to load from anything but the return pointer, so you can't do the standard "load at end of page" trick), but I can't see how can oversize loads can be anything but UB.

cc @eddyb - I think one solution would be to pad the return pointer alloca to always have enough space for the cast, aka:

  %_0_store = alloca {{ i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }, [1 x i8]}
  %_0 = getelementptr inbounds {{ i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }, [1 x i8]}, {{ i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }, [1 x i8]}* %_0_store, i32 0, i32 1

I think the problem load is the one in

let load = bcx.load(
, but I can't be sure.

@arielb1 arielb1 added A-codegen Area: Code generation A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-nominated T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 26, 2017
@arielb1
Copy link
Contributor Author

arielb1 commented Oct 26, 2017

This also happens when casting arguments - currently arguments can be "arbitrary" temporaries, so the return value fix won't work, but this still can't directly be used for the "end of page" segfaults:

#![crate_type="rlib"]

#[repr(C)]
#[derive(Copy, Clone)]
pub struct U24(u8, u8, u8);

extern "C" {
    fn bar(x: U24) -> U24;
}

pub extern "C" fn foo(x: U24) -> U24 {
    unsafe { bar(x) }
}

generates:

; ModuleID = 'cast2.cgu-0.rs'
source_filename = "cast2.cgu-0.rs"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%U24 = type { i8, [0 x i8], i8, [0 x i8], i8, [0 x i8] }

; cast2::foo
; Function Attrs: nounwind uwtable
define i32 @_ZN5cast23foo17h1f3d5ac3e0c9cc02E(i32) unnamed_addr #0 {
start:
  %abi_cast1 = alloca i32
  %_3 = alloca %U24
  %x = alloca %U24
  %_0 = alloca %U24
  %abi_cast = alloca i32
  %arg0 = alloca %U24
  store i32 %0, i32* %abi_cast
  %1 = bitcast %U24* %arg0 to i8*
  %2 = bitcast i32* %abi_cast to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* %2, i64 3, i32 1, i1 false)
  %3 = bitcast %U24* %arg0 to i8*
  %4 = bitcast %U24* %x to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* %3, i64 3, i32 1, i1 false)
  %5 = bitcast %U24* %x to i8*
  %6 = bitcast %U24* %_3 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %5, i64 3, i32 1, i1 false)
  %7 = bitcast %U24* %_3 to i32*
  %8 = load i32, i32* %7, align 1 ; bad load #1
  %9 = call i32 @bar(i32 %8)
  store i32 %9, i32* %abi_cast1
  %10 = bitcast %U24* %_0 to i8*
  %11 = bitcast i32* %abi_cast1 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %10, i8* %11, i64 3, i32 1, i1 false)
  br label %bb1

bb1:                                              ; preds = %start
  %12 = bitcast %U24* %_0 to i32*
  %13 = load i32, i32* %12, align 1 ; bad load #2
  ret i32 %13
}

; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #1

; Function Attrs: nounwind
declare i32 @bar(i32) unnamed_addr #2

attributes #0 = { nounwind uwtable "probe-stack"="__rust_probestack" }
attributes #1 = { argmemonly nounwind }
attributes #2 = { nounwind "probe-stack"="__rust_probestack" }

@arielb1
Copy link
Contributor Author

arielb1 commented Oct 26, 2017

The example above makes doing the alloca oversize less of a good idea - we eventually want to allow passing references to struct fields directly as function parameters, where we do not want to add a "guard byte". I think the "i24" case (which I think also includes 5/6/7-byte fields) should just be handled by doing an ugly memcpy.

@eddyb
Copy link
Member

eddyb commented Oct 26, 2017

I remember fixing this, I don't understand how we're still having issues like these.

@arielb1
Copy link
Contributor Author

arielb1 commented Oct 26, 2017

@eddyb

We fixed this for stores, where the problems were obvious (stack corruption), to actually get a problem with a load is harder - you have to engage an LLVM optimization.

@TimNN TimNN added the C-bug Category: This is a bug. label Oct 31, 2017
@nikomatsakis
Copy link
Contributor

triage: P-medium

@rust-highfive rust-highfive added P-medium Medium priority and removed I-nominated labels Nov 2, 2017
@arielb1 arielb1 added P-high High priority and removed P-medium Medium priority labels Nov 2, 2017
@dotdash
Copy link
Contributor

dotdash commented Dec 7, 2017

According to #29988 (comment) this was fixed once.

@nikomatsakis nikomatsakis added the I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. label Jan 4, 2018
@dotdash dotdash self-assigned this Jan 11, 2018
@nikomatsakis
Copy link
Contributor

Question: Do we feel this needs to stay P-high? This hasn't yet cropped up in the wild as far as I know, but it'd be good to get it done so that it never does.

@dotdash, think you'll have time to investigate?

@dotdash
Copy link
Contributor

dotdash commented Jan 18, 2018 via email

dotdash added a commit to dotdash/rust that referenced this issue Feb 8, 2018
The x86_64 SysV ABI should use exact sizes for small structs passed in
registers, i.e. a struct that occupies 3 bytes should use an i24,
instead of the i32 it currently uses.

Refs rust-lang#45543
bors added a commit that referenced this issue Feb 11, 2018
Fix oversized loads on x86_64 SysV FFI calls

The x86_64 SysV ABI should use exact sizes for small structs passed in
registers, i.e. a struct that occupies 3 bytes should use an i24,
instead of the i32 it currently uses.

Refs #45543
bors added a commit that referenced this issue Feb 11, 2018
Fix oversized loads on x86_64 SysV FFI calls

The x86_64 SysV ABI should use exact sizes for small structs passed in
registers, i.e. a struct that occupies 3 bytes should use an i24,
instead of the i32 it currently uses.

Refs #45543
@nikomatsakis
Copy link
Contributor

triage: P-medium

We do not know of an instance of this occurring in the wild. It is fixed on x86_64 (right @dotdash?). Seems like it's not P-high. But @dotdash if you are able to close completely, that would be awesome!

@rust-highfive rust-highfive added P-medium Medium priority and removed P-high High priority labels Feb 15, 2018
@eddyb eddyb removed their assignment Feb 23, 2018
@steveklabnik
Copy link
Member

Triage: don't have a good way to test this out, since I don't really know anything about sparc64.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants