In C++, every local variable of struct (or class) type has its constructor
run at the start of the scope, and the destructor runs at the end of the
scope. This idiom is useful for avoiding resource leaks, such as memory leaks.

In C, we don't have constructors and destructors in the language, but the
same idioms exist. For example:

       {
         struct string_buffer buffer;
         sb_init (&buffer); /* constructor call */
         sb_append (&buffer, ...);
         // sb_free (&buffer); /* destructor call */
       }

If the destructor call is commented out, we have a memory leak.

It is tedious to do the data flow analysis by hand, to avoid such a memory
leak. But clang can help us with that. With the patch below, we get a
warning:

  warning: memory resource 'buffer.data' is still held at the end of function 
[-Wthread-safety-analysis]

The prerequisites are simple:
  - Use clang ≥ 15.
  - Use the warning option -Wthread-safety.


2024-09-25  Bruno Haible  <br...@clisp.org>

        string-buffer: Enable resource leak warnings from clang.
        * m4/gnulib-common.m4 (gl_COMMON_BODY): Define the macros
        _GL_ATTRIBUTE_CAPABILITY_TYPE, _GL_ATTRIBUTE_ACQUIRE_CAPABILITY,
        _GL_ATTRIBUTE_RELEASE_CAPABILITY.
        * lib/string-buffer.h (sb_heap_allocated_pointer_t): New type.
        (struct string_buffer): Use it.
        (sb_init): Mark with _GL_ATTRIBUTE_ACQUIRE_CAPABILITY.
        (sb_free, sb_dupfree): Mark with _GL_ATTRIBUTE_RELEASE_CAPABILITY.
        * lib/string-buffer.c: Silence the -Wthread-safety warnings in this
        compilation unit.

diff --git a/lib/string-buffer.c b/lib/string-buffer.c
index 221cd9191e..ed34e6b78b 100644
--- a/lib/string-buffer.c
+++ b/lib/string-buffer.c
@@ -29,6 +29,12 @@ extern int sb_ensure_more_bytes (struct string_buffer 
*buffer,
 #include <stdlib.h>
 #include <string.h>
 
+/* The warnings about memory resource 'buffer->data' in this file are not
+   relevant.  Silence them.  */
+#if __clang_major__ >= 3
+# pragma clang diagnostic ignored "-Wthread-safety"
+#endif
+
 void
 sb_init (struct string_buffer *buffer)
 {
diff --git a/lib/string-buffer.h b/lib/string-buffer.h
index 9220ab301d..5476197d52 100644
--- a/lib/string-buffer.h
+++ b/lib/string-buffer.h
@@ -19,7 +19,8 @@
 #ifndef _STRING_BUFFER_H
 #define _STRING_BUFFER_H
 
-/* This file uses _GL_ATTRIBUTE_MALLOC.  */
+/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_CAPABILITY_TYPE,
+   _GL_ATTRIBUTE_ACQUIRE_CAPABILITY, _GL_ATTRIBUTE_RELEASE_CAPABILITY.  */
 #if !_GL_CONFIG_H_INCLUDED
  #error "Please include config.h first."
 #endif
@@ -29,10 +30,13 @@
 
 #include "attribute.h"
 
+typedef char * _GL_ATTRIBUTE_CAPABILITY_TYPE ("memory resource")
+        sb_heap_allocated_pointer_t;
+
 /* A string buffer type.  */
 struct string_buffer
 {
-  char *data;
+  sb_heap_allocated_pointer_t data;
   size_t length;     /* used bytes, <= allocated */
   size_t allocated;  /* allocated bytes */
   bool error;        /* true if there was an error */
@@ -44,7 +48,8 @@ extern "C" {
 #endif
 
 /* Initializes BUFFER to the empty string.  */
-extern void sb_init (struct string_buffer *buffer);
+extern void sb_init (struct string_buffer *buffer)
+  _GL_ATTRIBUTE_ACQUIRE_CAPABILITY (buffer->data);
 
 /* Appends the contents of STR to BUFFER.
    Returns 0, or -1 in case of out-of-memory error.  */
@@ -81,13 +86,14 @@ extern int sb_appendf (struct string_buffer *buffer,
   ;
 
 /* Frees the memory held by BUFFER.  */
-extern void sb_free (struct string_buffer *buffer);
+extern void sb_free (struct string_buffer *buffer)
+  _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 /* Returns the contents of BUFFER, and frees all other memory held
    by BUFFER.  Returns NULL upon failure or if there was an error earlier.
    It is the responsibility of the caller to free() the result.  */
 extern char * sb_dupfree (struct string_buffer *buffer)
-  _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE;
+  _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 #ifdef __cplusplus
 }
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index d985522893..e40bb9ddc4 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,5 +1,5 @@
 # gnulib-common.m4
-# serial 103
+# serial 104
 dnl Copyright (C) 2007-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -842,6 +842,35 @@ AC_DEFUN([gl_COMMON_BODY]
 #  define _GL_UNUSED_LABEL
 # endif
 #endif
+
+/* The following attributes enable detection of multithread-safety problems
+   and resource leaks at compile-time, by clang ≥ 15, when the warning option
+   -Wthread-safety is enabled.  For usage, see
+   <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>.  */
+#ifndef _GL_ATTRIBUTE_CAPABILITY_TYPE
+# if __clang_major__ >= 15
+#  define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept) \
+     __attribute__ ((__capability__ (concept)))
+#else
+#  define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept)
+# endif
+#endif
+#ifndef _GL_ATTRIBUTE_ACQUIRE_CAPABILITY
+# if __clang_major__ >= 15
+#  define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource) \
+     __attribute__ ((__acquire_capability__ (resource)))
+# else
+#  define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource)
+# endif
+#endif
+#ifndef _GL_ATTRIBUTE_RELEASE_CAPABILITY
+# if __clang_major__ >= 15
+#  define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource) \
+     __attribute__ ((__release_capability__ (resource)))
+# else
+#  define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource)
+# endif
+#endif
 ])
   AH_VERBATIM([c_linkage],
 [/* In C++, there is the concept of "language linkage", that encompasses




Reply via email to