mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
8fea1df72a
Implements a transform pass which instruments IR such that poison semantics are made explicit. That is, it provides a (possibly partial) executable semantics for every instruction w.r.t. poison as specified in the LLVM LangRef. There are obvious parallels to the sanitizer tools, but this pass is focused purely on the semantics of LLVM IR, not any particular source language. The target audience for this tool is developers working on or targetting LLVM from a frontend. The idea is to be able to take arbitrary IR (with the assumption of known inputs), and evaluate it concretely after having made poison semantics explicit to detect cases where either a) the original code executes UB, or b) a transform pass introduces UB which didn't exist in the original program. At the moment, this is mostly the framework and still needs to be fleshed out. By reusing existing code we have decent coverage, but there's a lot of cases not yet handled. What's here is good enough to handle interesting cases though; for instance, one of the recent LFTR bugs involved UB being triggered by integer induction variables with nsw/nuw flags would be reported by the current code. (See comment in PoisonChecking.cpp for full explanation and context) Differential Revision: https://reviews.llvm.org/D64215 llvm-svn: 365536
138 lines
5.2 KiB
LLVM
138 lines
5.2 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -passes=poison-checking -S < %s | FileCheck %s
|
|
|
|
; This file contains tests to exercise the UB triggering instructions with
|
|
; a potential source of UB. The UB source is kept simple; we focus on the
|
|
; UB triggering instructions here.
|
|
|
|
define void @store(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @store(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
|
|
; CHECK-NEXT: [[P:%.*]] = getelementptr i8, i8* [[BASE:%.*]], i32 [[ADD]]
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: store i8 0, i8* [[P]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%add = add nsw i32 %a, 1
|
|
%p = getelementptr i8, i8* %base, i32 %add
|
|
store i8 0, i8* %p
|
|
ret void
|
|
}
|
|
|
|
define void @load(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @load(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
|
|
; CHECK-NEXT: [[P:%.*]] = getelementptr i8, i8* [[BASE:%.*]], i32 [[ADD]]
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[TMP4:%.*]] = load volatile i8, i8* [[P]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%add = add nsw i32 %a, 1
|
|
%p = getelementptr i8, i8* %base, i32 %add
|
|
load volatile i8, i8* %p
|
|
ret void
|
|
}
|
|
|
|
define void @atomicrmw(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @atomicrmw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
|
|
; CHECK-NEXT: [[P:%.*]] = getelementptr i8, i8* [[BASE:%.*]], i32 [[ADD]]
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[TMP4:%.*]] = atomicrmw add i8* [[P]], i8 1 seq_cst
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%add = add nsw i32 %a, 1
|
|
%p = getelementptr i8, i8* %base, i32 %add
|
|
atomicrmw add i8* %p, i8 1 seq_cst
|
|
ret void
|
|
}
|
|
|
|
define void @cmpxchg(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @cmpxchg(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
|
|
; CHECK-NEXT: [[P:%.*]] = getelementptr i8, i8* [[BASE:%.*]], i32 [[ADD]]
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[TMP4:%.*]] = cmpxchg i8* [[P]], i8 1, i8 0 seq_cst seq_cst
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%add = add nsw i32 %a, 1
|
|
%p = getelementptr i8, i8* %base, i32 %add
|
|
cmpxchg i8* %p, i8 1, i8 0 seq_cst seq_cst
|
|
ret void
|
|
}
|
|
|
|
define i32 @udiv(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @udiv(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[RES:%.*]] = udiv i32 2048, [[ADD]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%add = add nuw i32 %a, 1
|
|
%res = udiv i32 2048, %add
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @sdiv(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @sdiv(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[RES:%.*]] = sdiv i32 2048, [[ADD]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%add = add nuw i32 %a, 1
|
|
%res = sdiv i32 2048, %add
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @urem(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @urem(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[RES:%.*]] = urem i32 2048, [[ADD]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%add = add nuw i32 %a, 1
|
|
%res = urem i32 2048, %add
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @srem(i8* %base, i32 %a) {
|
|
; CHECK-LABEL: @srem(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A:%.*]], i32 1)
|
|
; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i32, i1 } [[TMP1]], 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
|
|
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true
|
|
; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]])
|
|
; CHECK-NEXT: [[RES:%.*]] = srem i32 2048, [[ADD]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%add = add nuw i32 %a, 1
|
|
%res = srem i32 2048, %add
|
|
ret i32 %res
|
|
}
|
|
|
|
|
|
|