ⓘ NOTE: This is a draft document by the Open Source Security Foundation (OpenSSF) Best Practices Working Group. Help to improve it on Github.
Compile time security analysis and runtime mitigation implemented in compilers both depend on the compiler being able to see the flow of data between different points in a program, across functions and modules. This is quite a challenge in C and C++ because both languages allow passing around opaque references, thus losing information about objects. To work around this problem, both GCC and Clang implement attributes to annotate source code, especially functions and data structures, to allow them to do better analysis of source code. These annotations are not only beneficial for security, but they also help the compilers make better optimization decisions, often resulting in better code.
Both GCC and Clang recognize the __attribute__
keyword to annotate source code. Both compilers also provide a __has_attribute()
macro function that returns 1 if the attribute name passed to it is supported and 0 otherwise. For example __has_attribute(malloc)
would return 1 in the latest GCC and Clang. The full syntax description for the __attribute__
keyword is available in the GCC documentation1.
The C++112 and C233 standards specify a new attribute specifier sequence to standardize the __attribute__
functionality. The syntax is simply [[prefix::attribute]]
, where the prefix
specifies the namespace (e.g. gnu
for a number of attributes described in this document) and attribute
is the name of the attribute. This style is recommended whenever it is possible to build your applications to target these standards.
When declaring functions, attributes may be added to the function at the end of the declaration, like so:
extern void *custom_allocator (size_t sz) [[gnu::malloc]] [[alloc_size (1)]];
At function definition, function attributes come right before the function name:
void * [[gnu::malloc]] [[gnu::alloc_size (1)]] custom_allocator (size_t sz);
Some function attributes can accept parameters that have specific meanings. Parameters can be numbers that indicate the position of the argument to the function; 1 indicates the first argument, 2 the second and so on. Parameters can also be keywords or names of identifiers that have been declared earlier in the program.
Table 1: Recommended attributes
Attribute | Supported since | Type | Description |
---|---|---|---|
malloc |
GCC 2.95.3 Clang 13.0.0 |
Function | Mark custom allocation functions that return non-aliased (possibly NULL) pointers. |
malloc ( deallocator ) |
GCC 11.0.0 Clang 21.0.0 |
Function | Associates deallocator as the valid deallocator for the storage allocated by marked function. |
ownership_returns( allocation-type ) |
Clang 20.1.0 | Function | Associate pointers returned by custom allocation function with allocation-type . |
ownership_takes( allocation-type , ptr-index ) |
Clang 20.1.0 | Function | Mark function as valid deallocator for allocation-type . |
ownership_holds( allocation-type , ptr-index ) |
Clang 20.1.0 | Function | Mark function taking responsibility of deallocation for allocation-type . |
alloc_size( size-index ) alloc_size( size-index-1 , size-index-2 ) |
GCC 2.95.3 Clang 4.0.0 |
Function | Mark positional arguments holding the allocation size that the returned pointer points to. |
access(mode, ref-pos) access(mode, ref-pos, size-pos) |
GCC 10 | Function | Indicate how the function uses argument at ref-pos . mode could be read_only , read_write , write_only or none . size-pos , if mentioned, is the argument indicating the size of object at ref-pos . |
fd_arg(N) |
GCC 13 | Function | Argument N is an open file descriptor. |
fd_arg_read(N) |
GCC 13 | Function | Argument N is an open file descriptor that can be read from. |
fd_arg_write(N) |
GCC 13 | Function | Argument N is an open file descriptor that can be written to. |
noreturn |
GCC 2.95.3 Clang 4.0.0 |
Function | The function does not return. |
tainted_args |
GCC 12 | Function or function pointer | Function needs sanitization of its arguments. Used by -fanalyzer=taint |
Attributes influence not only diagnostics generated by the compiler but also the resultant code. As a result, annotating code with attributes will have an impact on performance, although the impact may go either way. The annotation may allow compilers to add more traps for additional security and be conservative about some optimizations, thus impacting performance of output code. At the same time however, it may allow compilers to make some favorable optimization decisions, resulting in generation of smaller and faster running code and often, better code layout.
Attribute | Supported since | Type | Description |
---|---|---|---|
malloc |
GCC 2.95.3 Clang 13.0.0 |
Function | Mark custom allocation functions that return non-aliased (possibly NULL) pointers. |
malloc ( deallocator ) |
GCC 11.0.0 Clang 21.0.0 |
Function | Associates deallocator as the valid deallocator for the storage allocated by marked function. |
malloc ( deallocator , ptr-index ) |
GCC 11.0.0 | Function | Same as above but also denotes the positional argument here the pointer must be passed. |
ownership_returns( allocation-type ) |
Clang 20.1.0 | Function | Associate pointers returned by custom allocation function with allocation-type . |
ownership_takes( allocation-type , ptr-index ) |
Clang 20.1.0 | Function | Mark function as valid deallocator for allocation-type . |
ownership_holds( allocation-type , ptr-index ) |
Clang 20.1.0 | Function | Mark function taking responsibility of deallocation for allocation-type . |
The malloc
attribute in GCC4 and Clang5 indicates that the function acts like a memory allocation function, meaning it returns a pointer to allocated storage that is disjoint (non-aliased) from the storage for any other object accessible to the caller.
Using the attribute with no arguments is designed to allow the compiler to rely on that the returned pointer does not alias any other valid pointers at the time of return and does not contain pointers to existing objects for program optimization. Functions like malloc
and calloc
have this property because they return a pointer to uninitialized or zeroed-out, newly obtained storage. However, functions like realloc
do not have this property, as they may return pointers to storage containing pointers to existing objects. Additionally, GCC uses the malloc
attribute to infer that the marked function returns null only infrequently, allowing callers to be optimized based on that assumption.
In GCC, the malloc (
deallocator
)
and malloc (
deallocator
,
ptr-index
)
forms associate the pointer returned by the marked function with the specified deallocator
function. Here, deallocator
is a function name that must have been declared before it can be referenced in the attribute and ptr-index
denotes the positional argument to deallocator
where the pointer must be passed in order to deallocate the storage. Using these forms of the malloc
attribute interacts with the GCC -Wmismatched-dealloc
and -Wmismatched-new-delete
warnings6 and GCC’s static analyzer (-fanalyzer
7) to allow it to catch:
-Wanalyzer-mismatching-deallocation
) if there is an execution path in which the result of an allocation call is passed to a different deallocator
.-Wanalyzer-double-free
) if there is an execution path in which a value is passed more than once to a deallocation call.-Wanalyzer-possible-null-dereference
) if there are execution paths in which an unchecked result of an allocation call is dereferenced or passed to a function requiring a non-null argument.-Wanalyzer-use-after-free
) if there is an execution path in which the memory passed by pointer to a deallocation call is used after the deallocation.-Wanalyzer-malloc-leak
) if if there is an execution path in which the result of an allocation call goes out of scope without being passed to the deallocation function.-Wanalyzer-free-of-non-heap
) if a deallocation function is used on a global or on-stack variable.Clang supports both forms of the malloc
attribute but does not yet implement the -Wmismatched-dealloc
and -Wmismatched-new-delete
warnings. Instead, Clang provides the ownership_returns
, ownership_takes
, and ownership_holds
attributes8: that interact with the Clang static analyzer9.
In Clang, the ownership_returns(
allocation-type
)
associates the pointer returned by the marked function with an allocation-type
. Here, allocation-type
is any string which will subsequently be used to detect mismatched allocations in cases where the pointer is passed to a deallocator marked with another allocation-type
. The allocation-type
malloc
has a special meaning and causes the Clang static analyzer to treat the associated pointer as though the allocated storage would have been allocatated using the standard malloc()
function, and can subsequently be safely deallocated with the standard free()
function.
The Clang ownership_takes(
allocation-type
,
ptr-index
)
attribute marks a function as a deallocator for pointers of allocation-type
and ownership_holds(
allocation-type
,
ptr-index
)
marks a function as taking over the ownership of a pointer of allocation-type
and will deallocate it at some unspecified point in the future. Here, ptr-index
denotes the positional argument to where the pointer must be passed in order to deallocate or take ownerwship of the storage.
Using the the ownership_returns
, ownership_takes
, and ownership_holds
attributes allows the Clang static analyzer to catch:
unix.MismatchedDeallocator
) if there is an execution path in which the result of an allocation call of type allocation-type
is passed to a function annotated with ownership_takes
or ownership_holds
with a different allocation type.unix.Malloc
, cplusplis.NewDelete
) if there is an execution path in which a value is passed more than once to a function annotated with ownership_takes
or ownership_holds
.unix.Malloc
, cplusplis.NewDelete
) if there is an execution path in which the memory passed by pointer to a function annotated with ownership_takes
is used after the call. Using memory passed to a function annotated with ownership_holds
is considered valid.unix.Malloc
, cplusplus.NewDeleteLeaks
) if if there is an execution path in which the result of an allocation call goes out of scope without being passed to a function annotated with ownership_takes
or ownership_holds
.malloc()
arguments involving sizeof
(unix.MallocSizeof
) if the size of the pointer type the returned pointer does not match the size indicated by sizeof
expression passed as argument to the allocation function.size
parameters to allocation functions (optin.taint.TaintedAlloc
) if the size
parameter originates from a tained source and the analyzer cannot prove that the size parameter is within reasonable bounds (<= SIZE_MAX/4
).GCC malloc
, malloc (
deallocator
)
, and malloc (
deallocator
, ptr-index
)
:
void my_free(void *ptr);
// Denotes that my_malloc will return with a dynamically allocated piece of memory which must be freed using my_free.
void *my_malloc(size_t size) __attribute__ ((malloc, malloc (my_free, 1)));
Note that to benefit both from the associated optimizations and improved detection of memory errors functions should be marked with both the form of the attribute without arguments and the form of the attribute with one or two arguments. [Extended example at Compiler Explorer]
Clang ownership_returns
, ownership_takes
, and ownership_holds
:
// Denotes that my_malloc will return with a pointer to storage of labeled as "my_allocation" .
void *my_malloc(size_t size) __attribute((malloc, ownership_returns(my_allocation)));
// Denotes that my_free will deallocate storage pointed to by ptr that has been labeled "my_allocation".
void my_free(void *ptr) __attribute((ownership_takes(my_allocation, 1)));
// Denotes that my_hold will take over the ownership of storage pointed to by ptr that has been labeled "my_allocation".
void my_hold(void *ptr) __attribute((ownership_holds(my_allocation, 1)));
Attribute | Supported since | Type | Description |
---|---|---|---|
alloc_size( size-index ) alloc_size( size-index-1 , size-index-2 ) |
GCC 2.95.3 Clang 4.0.0 |
Function | Mark positional arguments holding the allocation size that the returned pointer points to. |
The alloc_size
attribute in GCC10 and Clang11 indicates that the functional return value points to a memory allocation and the specified positional arguments hold the size of that allocation. The compiler uses this information to improve the correctness of information obtained through the __builtin_object_size
and __builtin_dynamic_object_size
12. builtins. This can improve the accuracy of source fortification for unsafe libc usage and buffer overflows as these builtins are used by __FORTIFY_SOURCE
to determine correct object bounds.
The alloc_size(
size-index
)
form acts as a hint to the compiler that the size of the allocation is denoted by the positional argument at size-index
(using one-based indexing). This form can be used for functions with a malloc
-like API.
The alloc_size(
size-index-1
,
size-index-2
)
form acts as a hint to the compiler that the size of the allocation is denoted by the product of the positional arguments at size-index-1
and size-index-2
. This form can be used for functions with a calloc
-like API.
In Clang, the size information hints provided via alloc_size
attribute only affects __builtin_object_size
and __builtin_dynamic_object_size
calls for pointer variables that are declared const
. In GCC the provided size information hints also affect __builtin_object_size
and __builtin_dynamic_object_size
calls for non-const
pointer variables.
// Denotes that my_malloc will return with a pointer to storage capable of holding up to size bytes.
void *my_malloc(size_t size) __attribute__((alloc_size(1)));
// Denotes that my_realloc will return with a pointer to storage capable of holding up to size bytes.
void *my_realloc(void* ptr, size_t size) __attribute__((alloc_size(2)));
// Denotes that my_calloc will return with a pointer to storage capable of holding up to n * size bytes.
void *my_calloc(size_t n, size_t size) __attribute__((alloc_size(1, 2)));
// The following assertions will evaluate to true in both GCC and Clang
void *const p = my_malloc(100);
assert(__builtin_object_size(p, 0) == 100);
void *const q = my_calloc(20, 5);
assert(__builtin_object_size(q, 0) == 100);
// The following assertions will evaluate to true in GCC
void *r = my_malloc(100);
assert(__builtin_object_size(r, 0) == 100);
void *s = my_calloc(20, 5);
assert(__builtin_object_size(s, 0) == 100);
[Extended example at Compiler Explorer]
GCC team, Attribute Syntax, GCC manual, 2024-01-17 ↩
ISO/IEC, Programming languages — C++ (“C++11”), ISO/IEC 14882, 2011. Note: The official ISO/IEC specification is paywalled and therefore not publicly available. The final specification draft is publicly available. ↩
ISO/IEC, Programming languages — C (“C23”), ISO/IEC 9899:2023, 2023. Note: The official ISO/IEC specification is not available. ↩
GCC team, Using the GNU Compiler Collection (GCC): 6.35.1 Common Function Attributes: malloc, GCC Manual, 2024-08-01. ↩
LLVM team, Attributes in Clang: malloc, Clang Compiler User’s Manual, 2025-03-04. ↩
GCC team, Using the GNU Compiler Collection (GCC): 3.9 Options to Request or Suppress Warnings: -Wmismatched-dealloc, GCC Manual, 2024-08-01. ↩
GCC team, Using the GNU Compiler Collection (GCC): 3.10 Options That Control Static Analysis, GCC Manual, 2024-08-01. ↩
LLVM team, Attributes in Clang: ownership_holds, ownership_returns, ownership_takes, Clang Compiler User’s Manual, 2025-03-04. ↩
LLVM team, Clang Static Analyzer: 1. Available Checkers¶, Clang Compiler User’s Manual, 2025-03-04. ↩
GCC team, Using the GNU Compiler Collection (GCC): 6.35.1 Common Function Attributes: alloc_size, GCC Manual, 2024-08-01. ↩
LLVM team, Attributes in Clang: alloc_size, Clang Compiler User’s Manual, 2025-03-04. ↩
GCC team, Using the GNU Compiler Collection (GCC): 6.62 Object Size Checking, GCC Manual, 2024-08-01. ↩