Skip to content

Checked C clang user manual

David Tarditi edited this page Apr 11, 2018 · 28 revisions

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 Checked C 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 test 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.

Basic usage

The extension is enabled by default, so just use Checked C version of clang as you would use clang.

New compiler flags

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.

Warnings for checking of bounds declarations

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:

  1. Prove to itself that the declared bounds are valid. In this case, the compiler is silent.
  2. Prove to itself that the declared bounds are invalid. In this case, the compiler issues an error message. The error message cannot be suppressed.
  3. 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).

Checking memory accesses

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

Checking uses of dynamic_check.

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 operation is never removed. The compiler will warn if it provides that e will always be false. You can disable this warning with

  • -Wno-checkedc