-
Notifications
You must be signed in to change notification settings - Fork 19
Checked C clang user manual
This page describes how to use the Checked C version of clang.
The Checked C version of clang is not ready for production use. The implementation of the Checked C extension is fairly incomplete, the compiler has only been tested on x86 and x64 Windows and x64 Linux, and testing has not been extensive. For example, we have not compiled and tested large production C and C++ code bases using the compiler. The compiler is quite similar to version 6.0.0 of clang, except that we haven't applied fixes made during the stabilization period of 6.0.0 (after the branch for 6.0.0 was taken.
The extension is enabled by default, so just use Checked C version of clang as you would use clang.
Here is the source code for "hello, world" in Checked C:
#include <stdio_checked.h>
#include <stdchecked.h>
#pragma BOUNDS_CHECKED ON
int main(int argc, nt_array_ptr<char> argv checked[] : count(argc)) {
puts("hello, world");
return 0;
}
If have a copy of the Checked C repo and clang is on your path, just go to the sample directory and run clang hello-world.c
to compile the program.
There are Checked C versions of the C standard library headers. These redeclare existing C functions with bounds-safe interfaces that describe their expected behavior. These are already on your clang system include path, so you can just include them in your program.
If you are using #pragma BOUNDS_CHECKED on
at the top-level of a C file to force the use of only checked types, you should include these header files before your top-level #pragma BOUNDS_CHECKED ON
pragma (we haven't implemented pushing/popping BOUNDS_CHECK pragmas yet, and each header file ends with `#pragma BOUNDS_CHECK OFF').
If you want to use easy-to-read names for your Checked C types and keywords (such as array_ptr
instead of _Array_ptr
) use #include <stdchecked.h>
. This include file is already on your clang system include path, so you don't need to do anything to add it to your include path. You can only use this header file if your code doesn't use a easy-to-read name as an identifier (ptr
is a popular identifier name).
If bounds checking or other Checked C runtime checks fail, your program will execute an illegal instruction. You can C signal handling to catch the illegal instruction signal and have your program exit gracefully. For example:
#include <signal.h>
void handle_error(int err) {
_Exit(1);
}
int main(int argc, _Array_ptr<_Nt_array_ptr<char>> argv : count(argc)) {
// Set up the handler for a failing runtime check. A SIGILL is raised when a Checked C
// runtime check fails.
signal(SIGILL, handle_error);
...
The flag -fcheckedc-extension
enables the Checked C language extension. It is on by default. If you need to disable the Checked C language extension, you can use the flag -fno-checkedc-extension.
The flag fdump-inferred-bounds
is meant for compiler developer use. It causes the compiler to dump the bounds that the compiler is inferring for program expressions. The dumped bounds are expressed using the internal clang IR. This is useful for seeing exactly what bounds the compiler really thinks an expression has, which makes it helpful for debugging problems related to bounds inference or incorrect bounds.
The compiler always checks whether declared bounds are valid. This is important because bounds checking is not meaningful if the declared bounds are wrong. Declared bounds are valid if they follow from existing declared bounds and other information in the program. For example, given
void f(array_ptr<int> p : count(len), int len) {
array_ptr<int> r : count(len) = p;
...
}
The declared bounds for r
of count(len)
are valid because p
has inferred bounds of count(len)
The bounds of the left-hand side of the initializer (count(len)
) imply that the bounds for the right-hand side are valid.
The compiler does one of three things when it checks a bounds declaration:
- Prove to itself that the declared bounds are valid. In this case, the compiler is silent.
- Prove to itself that the declared bounds are invalid. In this case, the compiler issues an error message. The error message cannot be suppressed.
- Be unable to prove that the declared bounds are valid or invalid. In this case, the compiler issues a warning. The static checking is not very sophisticated at this point. It does not understand simple dataflow facts across statements, for example, so the compiler could issue many warnings for bounds declarations, depending on your source code.
Our recommended approach where the compiler issues a warning to use a dynamic_bounds_cast
expression to check that the bounds are valid at run time. For example, given
array_ptr<int> p : count(len) = ...
array_ptr<int> r : count(len - 1) = p + 1;
the compiler will issue a warning for the assignment to r
. This can be avoided with:
array_ptr<int> r : count(len - 1) =
dynamic_bounds_cast<array_ptr<int>>(p + 1, count(len - 1);
However, you may not want to modify your code until the static checking gets smarter. If you do that, we strongly recommend you carefully code review the warnings, to make sure that the bounds declrations are correct. You can use the following flags to suppress warnings about checking of bounds declarations.
-
-Wno-check-bounds-decls
turns off all warnings. -
-Wno-check-bounds-decls-unchecked-scope
turns off warnings for unchecked scopes. -
-Wno-check-bounds-decls-checked-scope
turns off warnings for checked scopes (not recommended).
The compiler uses the static analysis for checking of bounds declarations to check memory accesses also. The compiler will warn when it encounters a memory access that is definitely out-of-bounds. The program will crash with a bounds checking failure if it ever reaches that memory access. You can disable this warning with
-Wno-check-memory-accesses
Checked C introduces a dynamic_check(e)
expression that always checks that e
is true at runtime. If it fails, the program exits with a runtime failure. Unlike asserts, this expression is never removed. The compiler will warn if it proves that e
will always be false. You can disable this warning with
-Wno-checkedc