[llvm] 3b1474c - free(nullptr) does not violate the nofree specification

Philip Reames via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 20 09:08:29 PDT 2021


Author: Philip Reames
Date: 2021-04-20T09:08:05-07:00
New Revision: 3b1474cab26b27c4d71a2c21cb45a62eefdacb6a

URL: https://github.com/llvm/llvm-project/commit/3b1474cab26b27c4d71a2c21cb45a62eefdacb6a
DIFF: https://github.com/llvm/llvm-project/commit/3b1474cab26b27c4d71a2c21cb45a62eefdacb6a.diff

LOG: free(nullptr) does not violate the nofree specification

This fixes a subtle and nasty bug in my 86664638. The problem is that free(nullptr) is well defined (and common).

The specification for the nofree attributes talks about memory objects, and doesn't explicitly address null, but I think it's reasonable to assume that nofree doesn't disallow a call to free(nullptr). If it did, we'd have to prove nonnull on an argument to ever infer nofree which doesn't seem to be the intent.

This was found by Nuno and Alive2 over in https://reviews.llvm.org/D100141#2697374.

Differential Revision: https://reviews.llvm.org/D100779

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
    llvm/test/Transforms/InstCombine/malloc-free-delete.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 8664c2fb76da..724e2122ff60 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2799,8 +2799,10 @@ Instruction *InstCombinerImpl::visitFree(CallInst &FI) {
   if (isa<ConstantPointerNull>(Op))
     return eraseInstFromFunction(FI);
 
-  // If we free a pointer we've been explicitly told won't be freed, this
-  // would be full UB and thus we can conclude this is unreachable. Cases:
+  
+  // If we free a non-null pointer we've been explicitly told won't be freed,
+  // this would be full UB and thus we can conclude that the argument must
+  // be null at the point of the free. Cases:
   // 1) freeing a pointer which is explicitly nofree
   // 2) calling free from a call site marked nofree (TODO: can generalize
   //    for non-arguments)
@@ -2810,8 +2812,9 @@ Instruction *InstCombinerImpl::visitFree(CallInst &FI) {
     if (A->hasAttribute(Attribute::NoFree) ||
         FI.hasFnAttr(Attribute::NoFree) ||
         FI.getFunction()->hasFnAttribute(Attribute::NoFree)) {
-      // Leave a marker since we can't modify the CFG here.
-      CreateNonTerminatorUnreachable(&FI);
+      Value *Null = ConstantPointerNull::get(cast<PointerType>(A->getType()));
+      Value *Cond = Builder.CreateICmpEQ(A, Null);
+      Builder.CreateAssumption(Cond);
       return eraseInstFromFunction(FI);
     }
 

diff  --git a/llvm/test/Transforms/InstCombine/malloc-free-delete.ll b/llvm/test/Transforms/InstCombine/malloc-free-delete.ll
index 8036526a9ac8..876d5a14108d 100644
--- a/llvm/test/Transforms/InstCombine/malloc-free-delete.ll
+++ b/llvm/test/Transforms/InstCombine/malloc-free-delete.ll
@@ -391,32 +391,45 @@ if.end:                                           ; preds = %entry, %if.then
   ret void
 }
 
-; Freeing a no-free pointer -> full UB
+; Freeing a no-free pointer -> %foo must be null
 define void @test13(i8* nofree %foo) {
 ; CHECK-LABEL: @test13(
-; CHECK-NEXT:    store i1 true, i1* undef, align 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8* [[FOO:%.*]], null
+; CHECK-NEXT:    call void @llvm.assume(i1 [[TMP1]])
 ; CHECK-NEXT:    ret void
 ;
   call void @free(i8* %foo)
   ret void
 }
 
-; Freeing a no-free pointer -> full UB
+; Freeing a no-free pointer -> %foo must be null
 define void @test14(i8* %foo) nofree {
 ; CHECK-LABEL: @test14(
-; CHECK-NEXT:    store i1 true, i1* undef, align 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8* [[FOO:%.*]], null
+; CHECK-NEXT:    call void @llvm.assume(i1 [[TMP1]])
 ; CHECK-NEXT:    ret void
 ;
   call void @free(i8* %foo)
   ret void
 }
 
-; free call marked no-free -> full UB
+; free call marked no-free ->  %foo must be null
 define void @test15(i8* %foo) {
 ; CHECK-LABEL: @test15(
-; CHECK-NEXT:    store i1 true, i1* undef, align 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8* [[FOO:%.*]], null
+; CHECK-NEXT:    call void @llvm.assume(i1 [[TMP1]])
 ; CHECK-NEXT:    ret void
 ;
   call void @free(i8* %foo) nofree
   ret void
 }
+
+; freeing a nonnull nofree pointer -> full UB
+define void @test16(i8* nonnull nofree %foo) {
+; CHECK-LABEL: @test16(
+; CHECK-NEXT:    call void @llvm.assume(i1 false)
+; CHECK-NEXT:    ret void
+;
+  call void @free(i8* %foo)
+  ret void
+}


        


More information about the llvm-commits mailing list