about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--instrumentation/split-compares-pass.so.cc139
-rwxr-xr-xtest/test-fpExtra.sh39
-rw-r--r--test/test-fp_Infcases.c124
-rw-r--r--test/test-fp_NaNcases.c86
-rw-r--r--test/test-fp_minusZerocases.c35
5 files changed, 399 insertions, 24 deletions
diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc
index 95485be9..c06118c0 100644
--- a/instrumentation/split-compares-pass.so.cc
+++ b/instrumentation/split-compares-pass.so.cc
@@ -882,6 +882,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
     // BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
     if (sizeInBits > 64) { continue; }
 
+    IntegerType *intType = IntegerType::get(C, op_size);
     const unsigned int precision = sizeInBits == 32    ? 24
                                    : sizeInBits == 64  ? 53
                                    : sizeInBits == 128 ? 113
@@ -913,14 +914,99 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
     BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst));
 
     /* create the integers from floats directly */
-    Instruction *b_op0, *b_op1;
-    b_op0 = CastInst::Create(Instruction::BitCast, op0,
+    Instruction *bpre_op0, *bpre_op1;
+    bpre_op0 = CastInst::Create(Instruction::BitCast, op0,
                              IntegerType::get(C, op_size));
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op0);
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), bpre_op0);
 
-    b_op1 = CastInst::Create(Instruction::BitCast, op1,
+    bpre_op1 = CastInst::Create(Instruction::BitCast, op1,
                              IntegerType::get(C, op_size));
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op1);
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), bpre_op1);
+
+
+    /* Check if any operand is NaN.
+     * If so, all comparisons except unequal (which yields true) yield false */
+
+    /* build mask for NaN */
+    const unsigned long long NaN_lowend = mask_exponent << precision;
+    //    errs() << "Fractions: IntFractionTy size " <<
+    //     IntFractionTy->getPrimitiveSizeInBits() << ", op_size " << op_size <<
+    //     ", mask_fraction 0x";
+    //    errs().write_hex(mask_fraction);
+    //    errs() << ", precision " << precision <<
+    //     ", NaN_lowend 0x";
+    //    errs().write_hex(NaN_lowend); errs() << "\n";
+
+    /* Check op0 for NaN */
+    /* Shift left 1 Bit, ignore sign bit */
+    Instruction *nan_op0, *nan_op1;
+    nan_op0 = BinaryOperator::Create(
+        Instruction::Shl, bpre_op0,
+        ConstantInt::get(bpre_op0->getType(), 1));
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), nan_op0);
+
+    /* compare to NaN interval */
+    Instruction *is_op0_nan =
+        CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op0, ConstantInt::get(intType, NaN_lowend) );
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+                             is_op0_nan);
+
+    /* Check op1 for NaN */
+    /* Shift right 1 Bit, ignore sign bit */
+    nan_op1 = BinaryOperator::Create(
+        Instruction::Shl, bpre_op1,
+        ConstantInt::get(bpre_op1->getType(), 1));
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), nan_op1);
+
+    /* compare to NaN interval */
+    Instruction *is_op1_nan =
+        CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op1, ConstantInt::get(intType, NaN_lowend) );
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+                             is_op1_nan);
+
+    /* combine checks */
+    Instruction *is_nan = BinaryOperator::Create(
+        Instruction::Or, is_op0_nan, is_op1_nan);
+    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+                             is_nan);
+
+    /* the result of the comparison, when at least one op is NaN
+       is true only for the "NOT EQUAL" predicates. */
+    bool NaNcmp_result =
+        FcmpInst->getPredicate() == CmpInst::FCMP_ONE ||
+        FcmpInst->getPredicate() == CmpInst::FCMP_UNE;
+
+    BasicBlock *nonan_bb =
+        BasicBlock::Create(C, "noNaN", end_bb->getParent(), end_bb);
+
+    BranchInst::Create(end_bb, nonan_bb);
+
+    auto term = bb->getTerminator();
+    /* if no operand is NaN goto nonan_bb else to handleNaN_bb */
+    BranchInst::Create(end_bb, nonan_bb, is_nan, bb);
+    term->eraseFromParent();
+
+    /*** now working in nonan_bb ***/
+
+    /* Treat -0.0 as equal to +0.0, that is for -0.0 make it +0.0 */
+    Instruction *b_op0, *b_op1;
+    Instruction *isMzero_op0, *isMzero_op1;
+    const unsigned long long MinusZero = 1UL << (sizeInBits - 1U);
+    const unsigned long long PlusZero = 0;
+
+    isMzero_op0 =
+        CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op0, ConstantInt::get(intType, MinusZero));
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op0);
+
+    isMzero_op1 =
+        CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op1, ConstantInt::get(intType, MinusZero));
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op1);
+
+    b_op0 = SelectInst::Create(isMzero_op0, ConstantInt::get(intType, PlusZero), bpre_op0);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), b_op0);
+
+    b_op1 = SelectInst::Create(isMzero_op1, ConstantInt::get(intType, PlusZero), bpre_op1);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), b_op1);
 
     /* isolate signs of value of floating point type */
 
@@ -931,21 +1017,21 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
     s_s0 =
         BinaryOperator::Create(Instruction::LShr, b_op0,
                                ConstantInt::get(b_op0->getType(), op_size - 1));
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s0);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), s_s0);
     t_s0 = new TruncInst(s_s0, Int1Ty);
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s0);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), t_s0);
 
     s_s1 =
         BinaryOperator::Create(Instruction::LShr, b_op1,
                                ConstantInt::get(b_op1->getType(), op_size - 1));
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s1);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), s_s1);
     t_s1 = new TruncInst(s_s1, Int1Ty);
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s1);
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()), t_s1);
 
     /* compare of the sign bits */
     icmp_sign_bit =
         CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1);
-    bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()),
+    nonan_bb->getInstList().insert(BasicBlock::iterator(nonan_bb->getTerminator()),
                              icmp_sign_bit);
 
     /* create a new basic block which is executed if the signedness bits are
@@ -962,9 +1048,9 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
 
     BranchInst::Create(end_bb, middle_bb);
 
-    auto term = bb->getTerminator();
+    term = nonan_bb->getTerminator();
     /* if the signs are different goto end_bb else to signequal_bb */
-    BranchInst::Create(signequal_bb, end_bb, icmp_sign_bit, bb);
+    BranchInst::Create(signequal_bb, end_bb, icmp_sign_bit, nonan_bb);
     term->eraseFromParent();
 
     /* insert code for equal signs */
@@ -1261,7 +1347,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
 
     }
 
-    PHINode *PN = PHINode::Create(Int1Ty, 3, "");
+    PHINode *PN = PHINode::Create(Int1Ty, 4, "");
 
     switch (FcmpInst->getPredicate()) {
 
@@ -1269,37 +1355,45 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
       case CmpInst::FCMP_OEQ:
         /* unequal signs cannot be equal values */
         /* goto false branch */
-        PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb);
+        PN->addIncoming(ConstantInt::get(Int1Ty, 0), nonan_bb);
         /* unequal exponents cannot be equal values, too */
         PN->addIncoming(ConstantInt::get(Int1Ty, 0), signequal_bb);
         /* fractions comparison */
         PN->addIncoming(icmp_fraction_result, middle2_bb);
+        /* NaNs */
+        PN->addIncoming(ConstantInt::get(Int1Ty, NaNcmp_result), bb);
         break;
       case CmpInst::FCMP_ONE:
       case CmpInst::FCMP_UNE:
         /* unequal signs are unequal values */
         /* goto true branch */
-        PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
+        PN->addIncoming(ConstantInt::get(Int1Ty, 1), nonan_bb);
         /* unequal exponents are unequal values, too */
         PN->addIncoming(icmp_exponent_result, signequal_bb);
         /* fractions comparison */
         PN->addIncoming(icmp_fraction_result, middle2_bb);
+        /* NaNs */
+        PN->addIncoming(ConstantInt::get(Int1Ty, NaNcmp_result), bb);
         break;
       case CmpInst::FCMP_OGT:
       case CmpInst::FCMP_UGT:
         /* if op1 is negative goto true branch,
            else go on comparing */
-        PN->addIncoming(t_s1, bb);
+        PN->addIncoming(t_s1, nonan_bb);
         PN->addIncoming(icmp_exponent_result, signequal2_bb);
         PN->addIncoming(PN2, middle2_bb);
+        /* NaNs */
+        PN->addIncoming(ConstantInt::get(Int1Ty, NaNcmp_result), bb);
         break;
       case CmpInst::FCMP_OLT:
       case CmpInst::FCMP_ULT:
         /* if op0 is negative goto true branch,
            else go on comparing */
-        PN->addIncoming(t_s0, bb);
+        PN->addIncoming(t_s0, nonan_bb);
         PN->addIncoming(icmp_exponent_result, signequal2_bb);
         PN->addIncoming(PN2, middle2_bb);
+        /* NaNs */
+        PN->addIncoming(ConstantInt::get(Int1Ty, NaNcmp_result), bb);
         break;
       default:
         continue;
@@ -1341,18 +1435,15 @@ bool SplitComparesTransform::runOnModule(Module &M) {
 
   if (enableFPSplit) {
 
+    simplifyFPCompares(M);
     count = splitFPCompares(M);
 
-    /*
-        if (!be_quiet) {
+    if (!be_quiet && !debug) {
 
           errs() << "Split-floatingpoint-compare-pass: " << count
-                 << " FP comparisons split\n";
+                 << " FP comparisons splitted\n";
 
-        }
-
-    */
-    simplifyFPCompares(M);
+    }
 
   }
 
diff --git a/test/test-fpExtra.sh b/test/test-fpExtra.sh
new file mode 100755
index 00000000..aecc6258
--- /dev/null
+++ b/test/test-fpExtra.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+. ./test-pre.sh
+
+test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
+  $ECHO "$GREY[*] llvm_mode laf-intel/compcov testing splitting floating point types with Nan, infinity, minusZero"
+  for testcase in ./test-fp_minusZerocases.c ./test-fp_Infcases.c ./test-fp_NaNcases.c; do
+  #for testcase in ./test-fp_cases.c ./test-fp_Infcases.c ./test-fp_NaNcases.c ./test-fp_minusZerocases.c ; do
+    for I in float double "long double"; do
+    #for I in double; do
+      for BITS in 64 32 16 8; do
+      #for BITS in 64; do
+        bin="$testcase-split-$I-$BITS.compcov" 
+#AFL_DONT_OPTIMIZE=1 AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_COMPARES_BITW=$BITS AFL_LLVM_LAF_SPLIT_COMPARES=1 AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -DFLOAT_TYPE="$I" -S "$testcase"
+#AFL_DONT_OPTIMIZE=1 AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_COMPARES_BITW=$BITS AFL_LLVM_LAF_SPLIT_COMPARES=1 AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -DFLOAT_TYPE="$I" -S -emit-llvm "$testcase"
+AFL_DONT_OPTIMIZE=1 AFL_LLVM_INSTRUMENT=AFL AFL_DEBUG=1 AFL_LLVM_LAF_SPLIT_COMPARES_BITW=$BITS AFL_LLVM_LAF_SPLIT_COMPARES=1 AFL_LLVM_LAF_SPLIT_FLOATS=1 ../afl-clang-fast -DFLOAT_TYPE="$I" -o "$bin" "$testcase" > test.out 2>&1;
+        if ! test -e "$bin"; then
+            cat test.out
+            $ECHO "$RED[!] llvm_mode laf-intel/compcov float splitting failed! ($testcase with type $I split to $BITS)!";
+            CODE=1
+            break
+        fi
+        if ! "$bin"; then
+            $ECHO "$RED[!] llvm_mode laf-intel/compcov float splitting resulted in miscompilation (type $I split to $BITS)!";
+            CODE=1
+            break
+        fi
+        rm -f "$bin" test.out || true
+      done
+    done
+  done
+  rm -f test-fp_cases*.compcov test.out
+
+} || {
+  $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test"
+  INCOMPLETE=1
+}
+
+. ./test-post.sh
diff --git a/test/test-fp_Infcases.c b/test/test-fp_Infcases.c
new file mode 100644
index 00000000..88a89ead
--- /dev/null
+++ b/test/test-fp_Infcases.c
@@ -0,0 +1,124 @@
+/* test cases for floating point comparison transformations
+ * compile with -DFLOAT_TYPE=float
+ *          or  -DFLOAT_TYPE=double
+ *          or  -DFLOAT_TYPE="long double"
+ */
+
+#include <assert.h>
+#define _GNU_SOURCE
+#include <math.h> /* for NaNs and infinity values */
+
+int main() {
+
+  volatile FLOAT_TYPE a, b;
+
+#ifdef INFINITY
+  FLOAT_TYPE inf = (FLOAT_TYPE) INFINITY;
+#else
+  FLOAT_TYPE inf = 1.0 / 0.0; /* produces infinity */
+#endif
+  FLOAT_TYPE negZero = 1.0 / -inf;
+  FLOAT_TYPE posZero = 0.0;
+
+  /* plus infinity */
+  a = (1.0 / 0.0);	/* positive infinity */
+  b = (1.0 / 0.0);	/* positive infinity */
+  assert(!(a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert((a >= b));
+  assert(!(a != b));
+  assert((a == b));
+
+  b = -(1.0 / 0.0);	/* negative infinity */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert((a > b));
+  assert((a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert((a > b));
+  assert((a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 0.0;	/* positive 0 */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert((a > b));
+  assert((a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = -42.0;
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert((a > b));
+  assert((a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 42.0;
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert((a > b));
+  assert((a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  /* negative infinity */
+  a = -(1.0 / 0.0);	
+  b = (1.0 / 0.0);	/* positive infinity */
+  assert((a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = -(1.0 / 0.0);	/* negative infinity */
+  assert(!(a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert((a >= b));
+  assert(!(a != b));
+  assert((a == b));
+
+  b = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  assert((a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 0.0;	/* positive 0 */
+  assert((a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = -42.0;
+  assert((a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 42.0;
+  assert((a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+}
+
diff --git a/test/test-fp_NaNcases.c b/test/test-fp_NaNcases.c
new file mode 100644
index 00000000..78d32711
--- /dev/null
+++ b/test/test-fp_NaNcases.c
@@ -0,0 +1,86 @@
+/* test cases for floating point comparison transformations
+ * compile with -DFLOAT_TYPE=float
+ *          or  -DFLOAT_TYPE=double
+ *          or  -DFLOAT_TYPE="long double"
+ */
+
+#include <assert.h>
+#define _GNU_SOURCE
+#include <math.h> /* for NaNs and infinity values */
+
+int main() {
+
+  volatile FLOAT_TYPE a, b;
+
+  /* NaN */
+#ifdef NAN
+  a = (FLOAT_TYPE) NAN; /* produces NaN */
+#else
+  a = 0.0 / 0.0; /* produces NaN */
+#endif
+#ifdef INFINITY
+  FLOAT_TYPE inf = (FLOAT_TYPE) INFINITY;
+#else
+  FLOAT_TYPE inf = 1.0 / 0.0; /* produces infinity */
+#endif
+  FLOAT_TYPE negZero = 1.0 / -inf;
+  FLOAT_TYPE posZero = 0.0;
+  b = a;
+
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 0.0;
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = 42.0;
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = -42.0;
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = (1.0 / 0.0);	/* positive infinity */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+  b = -(1.0 / 0.0);	/* negative infinity */
+  assert(!(a < b));
+  assert(!(a <= b));
+  assert(!(a > b));
+  assert(!(a >= b));
+  assert((a != b));
+  assert(!(a == b));
+
+}
+
diff --git a/test/test-fp_minusZerocases.c b/test/test-fp_minusZerocases.c
new file mode 100644
index 00000000..00beef2e
--- /dev/null
+++ b/test/test-fp_minusZerocases.c
@@ -0,0 +1,35 @@
+/* test cases for floating point comparison transformations
+ * compile with -DFLOAT_TYPE=float
+ *          or  -DFLOAT_TYPE=double
+ *          or  -DFLOAT_TYPE="long double"
+ */
+
+#include <assert.h>
+#define _GNU_SOURCE
+#include <math.h> /* for NaNs and infinity values */
+
+int main() {
+
+  volatile FLOAT_TYPE a, b;
+
+  /* negative zero */
+  a = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  b = 0.0;	/* positive 0 */
+  assert(!(a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert((a >= b));
+  assert(!(a != b));
+  assert((a == b));
+
+  a = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  b = 1.0 / -(1.0 / 0.0);	/* negative 0 */
+  assert(!(a < b));
+  assert((a <= b));
+  assert(!(a > b));
+  assert((a >= b));
+  assert(!(a != b));
+  assert((a == b));
+
+}
+