Skip to content

unreachable code paths need to be excluded from having coverage instrumentation #20992

@andrewrk

Description

@andrewrk

Looking at the "if tower" example:

test "if tower" {
    const input_bytes = std.testing.fuzzInput(.{});
    if (input_bytes.len < 10) return;
    std.time.sleep(std.time.ns_per_ms / 3); // otherwise it finds the bug too fast!
    if (input_bytes[0] == 'A') {
        if (input_bytes[1] == 'l') {
            if (input_bytes[2] == 'e') {
                if (input_bytes[3] == 'x') {
                    if (input_bytes[4] == 'a') {
                        if (input_bytes[5] == 'n') {
                            if (input_bytes[6] == 'd') {
                                if (input_bytes[7] == 'r') {
                                    if (input_bytes[8] == 'a') {
                                        @panic("found bug");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

We see edges for every safety check:

image

We can access the raw pointer of the slices to escape the safety check, demonstrating that those edges disappear:

test "if tower" {
    const input_bytes = std.testing.fuzzInput(.{});
    if (input_bytes.len < 10) return;
    std.time.sleep(std.time.ns_per_ms / 3); // otherwise it finds the bug too fast!
    if (input_bytes.ptr[0] == 'A') {
        if (input_bytes.ptr[1] == 'l') {
            if (input_bytes.ptr[2] == 'e') {
                if (input_bytes.ptr[3] == 'x') {
                    if (input_bytes.ptr[4] == 'a') {
                        if (input_bytes.ptr[5] == 'n') {
                            if (input_bytes.ptr[6] == 'd') {
                                if (input_bytes.ptr[7] == 'r') {
                                    if (input_bytes.ptr[8] == 'a') {
                                        @panic("found bug");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

image

Those safety check edges are not interesting for code coverage because they are unreachable. The fuzzer still wants to know about any comparisons used which may have led to those unreachable branches, but we are not expecting to have code coverage for unreachable paths!

Fortunately, LLVM has a !nosanitize metadata node. Here is an example of using it on a branch:

  br i1 %19, label %cont, label %trap, !dbg !39, !nosanitize !28

...
!22 = distinct !DISubprogram(name: "main", scope: !2, file: !2, line: 2, type: !23, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !12, retainedNodes: !28)
...
!28 = !{}

So, the cmp should still have the instrumentation because it provides the cmp operands to the fuzzer, and the fuzzer is trying to find inputs that cause the unreachable path to be reached, but there should not be a PC edge annotation on the branch, because we'll know it got hit when the process panics and crashes!

Metadata

Metadata

Assignees

No one assigned

    Labels

    backend-llvmThe LLVM backend outputs an LLVM IR Module.enhancementSolving this issue will likely involve adding new logic or components to the codebase.fuzzing

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions