Skip to content

[llvm-slicer] memmove source init sliced away #471

@Pipi9221

Description

@Pipi9221

@mchalupa Hi, thanks for maintaining dg
I may have found a slicing correctness issue and wanted to report it with a minimal reproducer below.

[llvm-slicer] memmove source initialization is sliced away under -c __assert_fail (semantic regression)

Summary

Slicing with criterion __assert_fail can keep llvm.memmove but remove the prior
llvm.memcpy that initializes its source bytes. The sliced program then reads
uninitialized memory and fails an assertion, while the original/unsliced program
passes.

This is a semantic regression introduced by slicing (orig PASS, sliced FAIL).

Environment

  • dg commit: dc3615d03fedfce62f37604b45d22e31c9d7e557
  • slicer: llvm-slicer (built from this dg checkout)
  • clang: Ubuntu clang version 14.0.0-1ubuntu1.1
  • lli: Ubuntu LLVM version 14.0.0
  • OS: Linux ... WSL2 x86_64 (kernel 5.15.167.4-microsoft-standard-WSL2)

Minimal Reproducer

#include <assert.h>
#include <string.h>

int main(void) {
    char src[] = "hello";
    char buf[16];

    if (1) {
        memcpy(buf, src, 6);
        memmove(buf + 2, buf, 3);
    }

    if (0) {
        buf[0] = 'x';
    }

    int probe_ok = (buf[2] == 'h' && buf[3] == 'e' && buf[4] == 'l');
    assert(probe_ok);
    return 0;
}

Reproduction Steps

cat > /tmp/min_memmove_deadcode.c <<'EOF'
#include <assert.h>
#include <string.h>
int main(void) {
    char src[] = "hello";
    char buf[16];
    if (1) {
        memcpy(buf, src, 6);
        memmove(buf + 2, buf, 3);
    }
    if (0) {
        buf[0] = 'x';
    }
    int probe_ok = (buf[2] == 'h' && buf[3] == 'e' && buf[4] == 'l');
    assert(probe_ok);
    return 0;
}
EOF

clang-14 -O0 -g -c -emit-llvm /tmp/min_memmove_deadcode.c -o /tmp/min_memmove_deadcode.bc
lli-14 /tmp/min_memmove_deadcode.bc

llvm-slicer -c __assert_fail /tmp/min_memmove_deadcode.bc -o /tmp/min_memmove_deadcode.sliced.bc
lli-14 /tmp/min_memmove_deadcode.sliced.bc

Actual Result

  • lli-14 /tmp/min_memmove_deadcode.bc passes.
  • lli-14 /tmp/min_memmove_deadcode.sliced.bc fails:
Assertion `probe_ok' failed.

Expected Result

The slice should preserve all dependencies relevant to __assert_fail reachability
and condition evaluation. This example should not become failing after slicing.

IR Evidence

Original IR contains:

call void @llvm.memcpy.p0i8.p0i8.i64(..., i64 6, ...)
call void @llvm.memcpy.p0i8.p0i8.i64(..., i64 6, ...)
call void @llvm.memmove.p0i8.p0i8.i64(..., i64 3, ...)

Sliced IR contains only:

call void @llvm.memmove.p0i8.p0i8.i64(..., i64 3, ...)

The source-initializing memcpy is removed, so memmove reads uninitialized bytes.

Why this looks like a slicer bug

probe_ok depends on buf[2..4], which are written by memmove(buf+2, buf, 3).
Those values depend on buf[0..2], which are defined by the prior memcpy.
Dropping that initialization breaks memory data dependencies and changes semantics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions