1

I'm working on Advent of Code with Zig and I'm at day 3. I've uploaded the code I wrote. The puzzle description and the code is here: https://github.com/secondspass/adventofcodeday3 . The code is in day3_part2.zig, the input file is day3.in. I run with zig run day3_part2.zig.

I get one of two outputs when I run the code. Either the below integer overflow in the co2ScrubberRating function.

thread 174927 panic: integer overflow
/home/subil/Projects/zig_learn/adventofcode/test/day3_part2.zig:80:25: 0x23401c in co2ScrubberRating (day3_part2)
    while (i >= 0) : (i -= 1) {
                        ^
/home/subil/Projects/zig_learn/adventofcode/test/day3_part2.zig:112:42: 0x22c80e in main (day3_part2)
    var co2rating = try co2ScrubberRating(numbers.items[0..], allocator);
                                         ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:535:37: 0x224eaa in std.start.callMain (day3_part2)
            const result = root.main() catch |err| {
                                    ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:477:12: 0x208ace in std.start.callMainWithArgs (day3_part2)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:391:17: 0x207b56 in std.start.posixCallMainAndExit (day3_part2)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/home/subil/tools/zig-0.9.0/lib/std/start.zig:304:5: 0x207962 in std.start._start (day3_part2)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
Aborted (core dumped)

or I would get the following output, where the binary number on the second line is the output of the co2ScrubberRating function is a seemingly random number.

100111011110
1000000111101101010111 

And which output I get is random every time I run the zig run day3_part2.zig.

I'm passing the same ArrayList.items slice and allocator to both the oxygenGeneratorRating and the co2ScrubberRating. And if I only run one of those functions in the main (i.e. I comment out one of them), the correct output is produced for that function. But if I have both functions in the main, I get the random bad outputs from the co2ScrubberRating function. And I have no idea why it behaves like that. I guess it has something to do with the ArrayList being passed to a function a second time.

Any insight would be appreciated.

dragsubil
  • 13
  • 4

2 Answers2

0

This is because readToNumberList is returning a pointer to stack memory that goes out of scope, which is undefined behaviour and not yet caught in debug builds.

fn readNumberList(…) !*std.ArrayList(u32) {
    …
    return &numbers;

Changing the code to

fn readNumberList(…) !std.ArrayList(u32) {
    …
    return numbers;

fixes the issue.

You could also fix it by putting the ArrayList struct in heap memory:

fn readNumberList(…) !*std.ArrayList(u32) {
    const numbers = try allocator.create(std.ArrayList(u32));
    numbers.* = std.ArrayList(u32).init(allocator);
    …
    return numbers;
pfg
  • 2,348
  • 1
  • 16
  • 37
  • Also, you might be able to catch this using `valgrind` or another memory analysis tool until it is fixed in zig debug builds. – pfg Feb 14 '22 at 20:22
0

Adding to pfg's reply, wrt while (i >= 0) : (i -= 1) {...}, this is indeed a loop that will underflow when reaching the end if i is unsigned. At the end of the last iteration, when i == 0, the loop will attempt to subtract one last time, causing the underflow.

The solution is to change the condition to be i > 0 and to perform the subraction inside the body, as the first operation.

while (i > 0) {
   i -= 1;
   // ...
} 
kristoff
  • 456
  • 2
  • 3