Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 111 additions & 3 deletions docs/Compiler-Hardening-Guides/Compiler-Annotations-for-C-and-C++.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,16 @@ Using the `ownership_returns`, `ownership_takes`, and `ownership_holds` attribut

#### Example usage

GCC `malloc`, `malloc (`_`deallocator`_`)`, and `malloc (`_`deallocator`_, _`ptr-index`_`)` :
GCC `malloc`, `malloc (`_`deallocator`_`)`, and `malloc (`_`deallocator`_, _`ptr-index`_`)` in C++11 / C23 attribute syntax:

~~~c
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) [[gnu::malloc]] [[gnu::malloc(my_free, 1)]];
~~~

In `__attribute__` keyword syntax:

~~~c
void my_free(void *ptr);
Expand All @@ -103,7 +112,20 @@ 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](https://godbolt.org/z/bc97ahbnd)]

Clang `ownership_returns`, `ownership_takes`, and `ownership_holds`:
Clang `ownership_returns`, `ownership_takes`, and `ownership_holds` in C++11 / C23 attribute syntax:

~~~c
// Denotes that my_malloc will return with a pointer to storage labeled as "my_allocation".
void * my_malloc(size_t size) [[gnu::malloc]] [[clang::ownership_returns(my_allocation)]];

// Denotes that my_free will deallocate storage pointed to by ptr that has been labeled "my_allocation".
voidmy_free(void *ptr) [[clang::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) [[clang::ownership_holds(my_allocation, 1)]];
~~~

In `__attribute__` keyword syntax:

~~~c
// Denotes that my_malloc will return with a pointer to storage of labeled as "my_allocation" .
Expand Down Expand Up @@ -141,6 +163,21 @@ In Clang, the size information hints provided via `alloc_size` attribute only af

#### Example usage

In C++11 / C23 attribute syntax:

~~~c
// Denotes that my_malloc will return with a pointer to storage capable of holding up to size bytes.
void * my_malloc(size_t size) [[gnu::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) [[gnu::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) [[gnu::alloc_size(1, 2)]];
~~~

In `__attribute__` keyword syntax:

~~~c
// 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)));
Expand Down Expand Up @@ -197,6 +234,33 @@ In the `read_only` and `read_write` access modes the object referenced by the po

### Example usage

In C++11 / C23 attribute syntax:

~~~c
// Denotes that puts performs read-only access on the memory pointed to by ptr.
int puts (const char* ptr) [[gnu::access(read_only, 1)]];

// Denotes that strcat performs read-write access on the memory pointed to by destination and read-only access on the memory pointed to by source.
char* strcat (char* destination, const char* source) [[gnu::access(read_write, 1)]] [[gnu::access(read_only, 2)]];

// Denotes that strcpy performs write-only access on the memory pointed to by destination and read-only access on the memory pointed to by source.
char* strcpy (char* destination, const char* source) [[gnu::access(write_only, 1)]] [[gnu::access(read_only, 2)]];

// Denotes that fgets performs write-only access up to n characters on the memory pointed to by buff and read-write access on the memory pointed to by stream.
int] fgets (char* buff, int n, FILE* stream) [[gnu::access(write_only, 1, 2)]] [[gnu::access(read_write, 3)];

// Denotes that print_buffer performs read-only access up to size characters on memory pointed to by buffer.
void print_buffer(const char *buffer, size_t size) [[gnu::access(read_only, 1, 2)]];

// Denotes that fill_buffer performs write-only access up to size characters on memory pointed to by buffer.
void fill_buffer(char *buffer, size_t size) [[gnu::access(write_only, 1, 2)]];

// Denotes that to_uppercase performs read-write access up to size characters on memory pointed to by buffer.
void to_uppercase(char *buffer, size_t size) [[gnu::access(read_write, 1, 2)]];
~~~

In `__attribute__` keyword syntax:

~~~c
// Denotes that puts performs read-only access on the memory pointed to by ptr.
int puts (const char* ptr) __attribute__ ((access (read_only, 1)));
Expand All @@ -207,7 +271,7 @@ char* strcat (char* destination, const char* source) __attribute__ ((access (rea
// Denotes that strcpy performs write-only access on the memory pointed to by destination and read-only access on the memory pointed to by source.
char* strcpy (char* destination, const char* source) __attribute__ ((access (write_only, 1), access (read_only, 2)));

// Denotes that fgets performs write-only access up n character on the memory pointed to by buff and read-write access on the memory pointed to by stream.
// Denotes that fgets performs write-only access up to n character on the memory pointed to by buff and read-write access on the memory pointed to by stream.
int fgets (char* buff, int n, FILE* stream) __attribute__ ((access (write_only, 1, 2), access (read_write, 3)));

// Denotes that print_buffer performs read-only access up to size characters on memory pointed to by buffer.
Expand Down Expand Up @@ -250,6 +314,21 @@ The `fd_arg_write(`_`fd-index`_`)` form is like `fd_arg` but also requires that

#### Example usage

In C++11 / C23 attribute syntax:

~~~c
// Denotes that use_file expects fd to be a valid and open file descriptor
void use_file (int fd) [[gnu::fd_arg(1)]] ;

// Denotes that write_to_file expects fd to be a valid, open, and writable file descriptor
void write_to_file (int fd, void *src, size_t size) [[gnu::fd_arg_write(1)]];

// Denotes that read_from_file expects fd to be a valid, open, and readable file descriptor
void read_from_file (int fd, void *dst, size_t size) [[gnu::fd_arg_read(1)]];
~~~

In `__attribute__` keyword syntax:

~~~c
// Denotes that use_file expects fd to be a valid and open file descriptor
void use_file (int fd) __attribute__ ((fd_arg (1)));
Expand Down Expand Up @@ -281,6 +360,22 @@ Users should be careful not to assume that registers saved by the calling functi

#### Example usage

In C++11 / C23 attribute syntax:

~~~c
// Denotes that fatal will never return
void fatal () [[noreturn]];

void
fatal (...)
{
... /* Print error message. */ ...
exit (1);
}
~~~

In `__attribute__` keyword syntax:

~~~c
// Denotes that fatal will never return
void fatal () __attribute__ ((noreturn));
Expand Down Expand Up @@ -331,6 +426,15 @@ Prior to GCC 14.1.0 the GCC analyzer's _taint mode_ had to be explicitly enabled

### Example usage

In C++11 / C23 attribute syntax:

~~~c
// Marks arguments to do_with_untrusted as requiring sanitization
void do_with_untrusted_input(int untrusted_input) [[gnu::tainted_args]];
~~~

In `__attribute__` keyword syntax:

~~~c
// Marks arguments to do_with_untrusted as requiring sanitization
void do_with_untrusted_input(int untrusted_input) __attribute__ ((tainted_args));
Expand All @@ -341,4 +445,8 @@ void do_with_untrusted_input(int untrusted_input) __attribute__ ((tainted_args))
[^gcc-tainted-args]: GCC team, [Using the GNU Compiler Collection (GCC): 6.35.1 Common Function Attributes: tainted_args](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-tainted_005fargs-function-attribute), GCC Manual, 2025-08-08.
[^Malcolm23]: Malcolm, David. [Enable "taint" state machine with -fanalyzer without requiring -fanalyzer-checker=taint](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103533#c9), GCC Bug 103533, 2023-12-01.

## License

Copyright 2024, OpenSSF contributors, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)

## References