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

Add diagnostic for stack allocations of 1 GB or more #119798

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
27 changes: 27 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::iter;

use rustc_hir::CRATE_HIR_ID;
use rustc_index::IndexVec;
use rustc_index::bit_set::BitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::{UnwindTerminateReason, traversal};
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::{bug, mir, span_bug};
use rustc_session::lint;
use rustc_target::callconv::{FnAbi, PassMode};
use tracing::{debug, instrument};

Expand All @@ -30,6 +32,8 @@ use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
use self::operand::{OperandRef, OperandValue};
use self::place::PlaceRef;

const MIN_DANGEROUS_ALLOC_SIZE: u64 = 1024 * 1024 * 1024 * 1; // 1 GB

// Used for tracking the state of generated basic blocks.
enum CachedLlbb<T> {
/// Nothing created yet.
Expand Down Expand Up @@ -245,6 +249,21 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let layout = start_bx.layout_of(fx.monomorphize(decl.ty));
assert!(!layout.ty.has_erasable_regions());

if layout.size.bytes() >= MIN_DANGEROUS_ALLOC_SIZE {
let (size_quantity, size_unit) = human_readable_bytes(layout.size.bytes());
cx.tcx().node_span_lint(
lint::builtin::DANGEROUS_STACK_ALLOCATION,
CRATE_HIR_ID,
decl.source_info.span,
|lint| {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could add more context, like in the lint description, about some common platform limits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AFAIK, it's limited in some ways by (possibly a bug in )LLVM and so most platforms are affected.

lint.primary_message(format!(
"allocation of size: {:.2} {} exceeds most system architecture limits",
iSwapna marked this conversation as resolved.
Show resolved Hide resolved
size_quantity, size_unit
));
},
);
}

if local == mir::RETURN_PLACE {
match fx.fn_abi.ret.mode {
PassMode::Indirect { .. } => {
Expand Down Expand Up @@ -310,6 +329,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
}

/// Formats a number of bytes into a human readable SI-prefixed size.
/// Returns a tuple of `(quantity, units)`.
pub fn human_readable_bytes(bytes: u64) -> (u64, &'static str) {
static UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
let i = ((bytes.checked_ilog2().unwrap_or(0) / 10) as usize).min(UNITS.len() - 1);
(bytes >> (10 * i), UNITS[i])
}
iSwapna marked this conversation as resolved.
Show resolved Hide resolved

/// Produces, for each argument, a `Value` pointing at the
/// argument's value. As arguments are places, these are always
/// indirect.
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ declare_lint_pass! {
CONFLICTING_REPR_HINTS,
CONST_EVALUATABLE_UNCHECKED,
CONST_ITEM_MUTATION,
DANGEROUS_STACK_ALLOCATION,
DEAD_CODE,
DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
DEPRECATED,
Expand Down Expand Up @@ -711,6 +712,50 @@ declare_lint! {
"detect assignments that will never be read"
}

declare_lint! {
/// The `dangerous_stack_allocation` lint detects stack allocations that
/// are 1 GB or more.
///
/// ### Example
///
/// ``` rust
/// fn func() {
/// const CAP: usize = std::u32::MAX as usize;
/// let mut x: [u8; CAP>>1] = [0; CAP>>1];
/// x[2] = 123;
/// println!("{}", x[2]);
/// }
///
/// fn main() {
/// std::thread::Builder::new()
/// .stack_size(3 * 1024 * 1024 * 1024)
/// .spawn(func)
/// .unwrap()
/// .join()
/// .unwrap();
/// }
/// ```
///
/// {{produces}}
///
/// ```text
/// warning: allocation of size: 1 GiB exceeds most system architecture limits
/// --> $DIR/large-stack-size-issue-83060.rs:7:9
/// |
/// LL | let mut x: [u8; CAP>>1] = [0; CAP>>1];
/// | ^^^^^
/// |
/// = note: `#[warn(dangerous_stack_allocation)]` on by default
/// ```
/// ### Explanation
///
/// Large arrays may cause stack overflow due to the limited size of the
/// stack on most platforms.
pub DANGEROUS_STACK_ALLOCATION,
Warn,
"detects dangerously large stack allocations at the limit of most architectures"
}

declare_lint! {
/// The `dead_code` lint detects unused, unexported items.
///
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/sanitizer/large-stack-size-issue-83060.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This test checks that allocating a stack size of 1GB or more results in a warning
//@build-pass
Copy link
Contributor Author

@iSwapna iSwapna Dec 15, 2024

Choose a reason for hiding this comment

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

Not sure if we should turn the warning and test with build-fail ( since CI core dumps) or the warning needs to be an error. The failure is in doc-tests. I believe it's the latter, but want to be sure. @estebank

//@ only-64bit

fn func() {
const CAP: usize = std::u32::MAX as usize;
let mut x: [u8; CAP>>1] = [0; CAP>>1];
//~^ warning: allocation of size: 1 GiB exceeds most system architecture limits
//~| NOTE on by default
x[2] = 123;
println!("{}", x[2]);
}

fn main() {
std::thread::Builder::new()
.stack_size(3 * 1024 * 1024 * 1024)
.spawn(func)
.unwrap()
.join()
.unwrap();
}
10 changes: 10 additions & 0 deletions tests/ui/sanitizer/large-stack-size-issue-83060.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
warning: allocation of size: 1 GiB exceeds most system architecture limits
--> $DIR/large-stack-size-issue-83060.rs:7:9
|
LL | let mut x: [u8; CAP>>1] = [0; CAP>>1];
| ^^^^^
|
= note: `#[warn(dangerous_stack_allocation)]` on by default

warning: 1 warning emitted

Loading