[clang] [clang][ThreadSafety]: fix descrepency between capability attributes (PR #139343)

Cory Fields via cfe-commits cfe-commits at lists.llvm.org
Fri May 9 17:39:08 PDT 2025


https://github.com/theuni created https://github.com/llvm/llvm-project/pull/139343

Fix the case where `release_generic_capability` did not correctly release when used as a reverse capability as enabled by commit 6a68efc959.

I noticed this when trying to implement a reverse lock.

My my project still uses the old `UNLOCK_FUNCTION` macro which maps to `unlock_function`. With that attribute, the [MutexUnlock test-case seen here](https://github.com/llvm/llvm-project/blob/main/clang/test/SemaCXX/warn-thread-safety-analysis.cpp#L3126) does not work.

I'm not at all familiar with the clang code so I have no idea if this is the correct fix, but it fixes my problem. Hopefully it's helpful.

Here's a minimal reproducer:

```c++
class __attribute__((capability(""))) Mutex {
 public:
  const Mutex& operator!() const { return *this; }
};

class  __attribute__((scoped_lockable)) MutexLock {
public:
  MutexLock(Mutex *mu) __attribute__((acquire_capability(mu))) {}
  ~MutexLock() __attribute__((release_capability())){}
};

  class  __attribute__((scoped_lockable)) MutexLockOld {
  public:
      MutexLockOld(Mutex *mu) __attribute__((exclusive_lock_function(mu))) {}
      ~MutexLockOld() __attribute__((unlock_function())){}
  };

class  __attribute__((scoped_lockable)) MutexLockGeneric {
public:
    MutexLockGeneric(Mutex *mu) __attribute__((acquire_capability(mu))) {}
    ~MutexLockGeneric() __attribute__((release_generic_capability())){}
};

class  __attribute__((scoped_lockable)) MutexUnlock {
public:
  MutexUnlock(Mutex *mu) __attribute__((release_capability(mu))){}
  ~MutexUnlock() __attribute__((release_capability())){}
};

class  __attribute__((scoped_lockable)) MutexUnlockOld {
public:
  MutexUnlockOld(Mutex *mu) __attribute__((unlock_function(mu))){}
  ~MutexUnlockOld() __attribute__((unlock_function())){}
};

class  __attribute__((scoped_lockable)) MutexUnlockGeneric {
public:
  MutexUnlockGeneric(Mutex *mu) __attribute__((release_generic_capability(mu))){}
  ~MutexUnlockGeneric() __attribute__((release_generic_capability())){}
};


Mutex mut;
void req() __attribute__((requires_capability(mut))){}
void req2() __attribute__((exclusive_locks_required(mut))){}
void not_req() __attribute__((requires_capability(!mut))){}

void good()
{
    MutexLock lock(&mut);
    req();
    {
        MutexUnlock reverse_lock(&mut);
        not_req();
    }
    req();
}

void bad()
{
    MutexLockGeneric lock(&mut);
    req();
    {
        MutexUnlockGeneric reverse_lock(&mut);
        not_req();
    }
    req();
    req2();
}

void bad2()
{
    MutexLockOld lock(&mut);
    req();
    {
        MutexUnlockOld reverse_lock(&mut);
        not_req();
    }
    req();
    req2();
}
```

Result:
```bash
clangtest.cpp:67:5: warning: calling function 'req' requires holding  'mut' exclusively [-Wthread-safety-analysis]
   67 |     req();
      |     ^
clangtest.cpp:68:5: warning: calling function 'req2' requires holding  'mut' exclusively [-Wthread-safety-analysis]
   68 |     req2();
      |     ^
clangtest.cpp:79:5: warning: calling function 'req' requires holding  'mut' exclusively [-Wthread-safety-analysis]
   79 |     req();
      |     ^
clangtest.cpp:80:5: warning: calling function 'req2' requires holding  'mut' exclusively [-Wthread-safety-analysis]
   80 |     req2();
```

>From 987b3ebb582e3dc243e48dc2522bce95c7f8b886 Mon Sep 17 00:00:00 2001
From: Cory Fields <cory-nospam- at coryfields.com>
Date: Fri, 9 May 2025 23:19:34 +0000
Subject: [PATCH] [clang][ThreadSafety]: fix descrepency between capability
 attributes

Fix the case where release_generic_capability did not correctly release when
used as a reverse capability as enabled by commit 6a68efc959.
---
 clang/lib/Analysis/ThreadSafety.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index 7e86af6b4a317..a963bcda0c2d0 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -2026,6 +2026,8 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
       ScopedEntry->addExclusiveUnlock(M);
     for (const auto &M : SharedLocksToRemove)
       ScopedEntry->addSharedUnlock(M);
+    for (const auto &M : GenericLocksToRemove)
+      ScopedEntry->addExclusiveUnlock(M);
     Analyzer->addLock(FSet, std::move(ScopedEntry));
   }
 }



More information about the cfe-commits mailing list