rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.98k stars 12.54k forks source link

Segmentation fault #117461

Closed valentinegb closed 10 months ago

valentinegb commented 10 months ago

I'm very sorry for how little information this bug report has, hopefully just, initially. I'm really not sure what exactly is going wrong here.

I have an Advent of Code project (the whole thing is here) where all the years and days are organized into their own files. This is the code I have in the file dy6.rs (day 6):

use std::str::FromStr;

use anyhow::{anyhow, bail};

#[derive(Debug)]
struct Instruction {
    kind: InstructionKind,
    start: (usize, usize),
    end: (usize, usize),
}

#[derive(Debug)]
enum InstructionKind {
    TurnOn,
    TurnOff,
    Toggle,
}

impl FromStr for Instruction {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let kind = match s {
            i if i.starts_with("turn on") => InstructionKind::TurnOn,
            i if i.starts_with("turn off") => InstructionKind::TurnOff,
            i if i.starts_with("toggle") => InstructionKind::Toggle,
            _ => {
                bail!(r#"string did not start with "turn on", "turn off", or "toggle""#);
            }
        };

        let start_i = match kind {
            InstructionKind::TurnOn => 8,
            InstructionKind::TurnOff => 9,
            InstructionKind::Toggle => 7,
        };
        let through = s
            .find("through")
            .ok_or(anyhow!(r#"instruction should contain "through""#))?;
        let start = s[start_i..(through - 1)]
            .split_once(',')
            .ok_or(anyhow!("coord pairs should be separated with ','"))?;
        let start: (usize, usize) = (start.0.parse()?, start.1.parse()?);
        let end = s[(through + 8)..]
            .split_once(',')
            .ok_or(anyhow!("coord pairs should be separated with ','"))?;
        let end: (usize, usize) = (end.0.parse()?, end.1.parse()?);

        Ok(Instruction { kind, start, end })
    }
}

pub(super) fn pt1(input: String) {
    let mut grid = [[false; 1000]; 1000];

    for instruction in input.split('\n') {
        let instruction = Instruction::from_str(instruction).unwrap();

        for column in instruction.start.0..=instruction.end.0 {
            for row in instruction.start.1..=instruction.end.1 {
                match instruction.kind {
                    InstructionKind::TurnOn => grid[column][row] = true,
                    InstructionKind::TurnOff => grid[column][row] = false,
                    InstructionKind::Toggle => grid[column][row] = !grid[column][row],
                }
            }
        }
    }

    let mut lit_lights: u32 = 0;

    for column in grid {
        for light_lit in column {
            if light_lit {
                lit_lights += 1;
            }
        }
    }

    println!("{lit_lights}");
}

pub(super) fn pt2(input: String) {
    let mut grid: [[u32; 1000]; 1000] = [[0; 1000]; 1000];

    for instruction in input.split('\n') {
        let instruction = Instruction::from_str(instruction).unwrap();

        for column in instruction.start.0..=instruction.end.0 {
            for row in instruction.start.1..=instruction.end.1 {
                match instruction.kind {
                    InstructionKind::TurnOn => grid[column][row] += 1,
                    InstructionKind::TurnOff => {
                        if grid[column][row] > 0 {
                            grid[column][row] -= 1
                        }
                    }
                    InstructionKind::Toggle => grid[column][row] += 2,
                }
            }
        }
    }

    let mut total_brightness: u32 = 0;

    for column in grid {
        for brightness in column {
            total_brightness += brightness;
        }
    }

    println!("{total_brightness}");
}

The function pt1() (part 1) works as expected, but pt2() crashes with a segmentation fault! I tried figuring out where exactly it's failing, and it seems like it's crashing just before pt2() is called.

zsh: segmentation fault  cargo run -- 2015 6 2 "this happens no matter what text is here"

This happens for me on both stable and nightly.

Meta

rustc --version --verbose:

rustc 1.73.0 (cc66ad468 2023-10-03)
binary: rustc
commit-hash: cc66ad468955717ab92600c770da8c1601a4ff33
commit-date: 2023-10-03
host: aarch64-apple-darwin
release: 1.73.0
LLVM version: 17.0.2

No backtrace available.

saethlin commented 10 months ago

The problem is this

    let mut grid: [[u32; 1000]; 1000] = [[0; 1000]; 1000];

This attempts to create a 4 MB allocation on the stack, which is pushing your luck when the default stack size is 8 MB. You get a segfault because we don't have stack protectors on aarch64.

This should work:

    let mut grid: Vec<[u32; 1000]> = vec![[0; 1000]; 1000];
valentinegb commented 10 months ago

Yep, that was it. Thank you! There really ought to be a better error for this though...

saethlin commented 10 months ago

There will be, when we get support for stack probes on aarch64: https://github.com/rust-lang/rust/issues/77071