0

Move some documentation into unsafe_buffers.md

Move some text from unsafe_buffers_paths.txt into the markdown file
so that it may be more easily discovered. Hidden benefit of not
reading and discarding so many comment lines in each compiler
invocation.

Re-arrange and re-write sections to make flow more natural.

Change-Id: I077cf0d91cdd52a36cde9bb7c882ce3cf1fd6785
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6251406
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1418875}
This commit is contained in:
Tom Sepez
2025-02-11 13:11:51 -08:00
committed by Chromium LUCI CQ
parent af45beba85
commit d647d84dc8
2 changed files with 115 additions and 84 deletions

@ -2,52 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# The set of path prefixes that should be checked for unsafe pointer usage (see
# -Wunsafe-buffer-usage in Clang).
#
# ***
# Paths should be written as relative to the root of the source tree with
# unix-style path separators. Directory prefixes should end with `/`, such
# as `base/`.
# ***
#
# Lines that begin with `-` name path prefixes that will *not* be checked for
# unsafe-buffer-usage. They are known to do unsafe things and should be
# changed to use constructs like base::span or containers like base::HeapArray
# and std::vector instead. See https://crbug.com/40285824
#
# Lines that begin with `+` name path prefixes that have no unsafe-buffer-usage
# (or all such usage is annotated), and are protected against new unsafe pointer
# behaviour by the compiler.
#
# By default, all files are checked for unsafe-buffer-usage unless they are
# match a `-` path prefix line here. If a file matches both a `-` and `+` line,
# the `+` line takes precedence and the file will be checked.
#
# To opt individual files out of checks, place `#pragma allow_unsafe_buffers`
# anywhere in the (source or header) file, guarded by
# `#ifdef UNSAFE_BUFFERS_BUILD`. These pragmas represent the technical debt and
# security risk present in the file through unsafe pointer usage.
#
# ***
# Recommended process for removing a `-dir/` line from this file:
#
# 1. Remove the `-dir/` line from this paths file.
# a. Possibly add some subdirectories if needed to reduce scope,
# like `-dir/sub_dir/`.
# 2. Add `#pragma allow_unsafe_buffers` to every file in the directory that now
# has a compilation error, with a TODO to the tracking bug for the
# directory:
# ```
# #ifdef UNSAFE_BUFFERS_BUILD
# // TODO(crbug.com/ABC): Remove this and convert code to safer constructs.
# #pragma allow_unsafe_buffers
# #endif
# ```
# 3. Work through the files in the directory, converting pointers to spans, or
# to owning containers like HeapArray and vector. Remove the pragmas from
# the files when there is no unsafe pointer usage left in each one.
#
# See `docs/unsafe_buffers.md`.
-base/allocator/

@ -1,35 +1,52 @@
# Preventing OOB through Unsafe Buffers errors (aka Spanification)
# The Unsafe Buffers Clang plugin.
Out-of-bounds (OOB) security bugs commonly happen through C-style pointers which
have no bounds information associated with them. We can prevent such
bugs by always using C++ containers. Furthermore, the Clang compiler can
Our compiler contains a [plugin](../tools/clang/plugins/UnsafeBuffersPlugin.cpp)
which reports places in the code where unsafe buffer operations are present.
[TOC]
## Preventing OOB by removing Unsafe Buffers operations.
Out-of-bounds (OOB) security bugs commonly happen through C-style pointers
which have no bounds information associated with them. We can prevent such
bugs by always using C++ containers. Furthermore, the plugin will warn
warn about unsafe pointer usage that should be converted to containers.
When an unsafe usage is detected, Clang prints a warning similar to
```
error: unsafe buffer access [-Werror,-Wunsafe-buffer-usage]
```
and directs developers to this file for more information.
and directs developers to this file for more information. Several common
[Techniques](#container-based-ecosystem) for fixing these issues are presented
later in this document.
Clang documentation includes a guide to working with unsafe-buffer-usage
warnings here: https://clang.llvm.org/docs/SafeBuffers.html
[TOC]
## Preventing OOB by removing unsafe libc calls.
OOB bugs also commonly happen through C-style library calls such as
memcpy() and memset(). In order to encourage safer alternatives, the
Clang compiler can warn about unsafe calls which should be converted
to safer C++ alternatives.
memcpy() and memset() where the programmer is responsible for specifying
an (unchecked) length. In order to encourage safer alternatives, the
plugin can warn about unsafe calls which should be converted to safer
C++ alternatives. When an unsafe libc call is detected, Clang prints
a warning similar to
```
error: function 'memcpy' is unsafe [-Werror,-Wunsafe-buffer-usage-in-libc-call]
```
These warnings are not yet enabled by default for chromium builds.
Suppressions are being incrementally added prior to enabling these
warnings.
## Unsafe buffer suppressions
## Unsafe buffer warning suppressions
Our [compiler](../tools/clang/plugins/UnsafeBuffersPlugin.cpp) enables
the `-Wunsafe-buffer-usage` warning on all files by default. Because the
Chromium codebase is not yet compliant with these warnings, there are
mechanisms to opt out code on a directory, file, or per-occurence basis.
Because the Chromium codebase is not yet compliant with these warnings,
there are mechanisms to opt out code on a directory, file, or per-occurence
basis.
By default, all files are checked for unsafe-buffer-usage.
### Opting out entire directories
Entire directories are opted out of unsafe buffer usage warnings through
the [`//build/config/unsafe_buffers_paths.txt`](../build/config/unsafe_buffers_paths.txt)
@ -39,6 +56,46 @@ indicate that often 85%+ of files in a directory already happen to be
compliant, so file-by-file suppression allows this code to be subject
to enforcement.
This mechanism opts directories out of all warning categories (unsafe
buffers and unsafe libc calls).
#### Syntax of Unsafe Buffer Paths file
Note: Paths should be written as relative to the root of the source tree with
unix-style path separators. Directory prefixes should end with `/`, such
as `base/`.
Empty lines are ignored.
The `#` character introduces a comment until the end of the line.
Lines that begin with `-` are immediately followed by path prefixes that
will *not* be checked for unsafe-buffer-usage. They are known to do unsafe
things and should be changed to use constructs like base::span or containers
like base::HeapArray and std::vector instead. See https://crbug.com/40285824
Lines that begin with `+` are immediately followed by path prefixes that will
be checked for unsafe-buffer-usage. These have no such usage (or all such
usage is annotated), and are protected against new unsafe pointer behaviour
by the compiler. Generally, `+` lines are used to enable checks for
sub-directories of a path that has previously disabled checks (with a
`-` line).
If a file matches both a `-` and `+` line, the longest matching prefix takes
precedence.
#### Removing directories from Unsafe Buffers Paths file
The recommended process for removing a `-dir/` line from this file is:
1. Remove the `-dir/` line from this paths file. Possibly add some subdirectories
now needed to reduce scope, like `-dir/sub_dir/`.
2. Add `#pragma allow_unsafe_buffers` to every file in the directory that now
has a compilation error (see the next section).
### Opting out individual files
Individual files are opted out of unsafe pointer usage warnings though
the use of the following snippet, which is to be placed immediately
following the copyright header in a source file.
@ -49,6 +106,35 @@ following the copyright header in a source file.
#endif
```
The above mechanism also suppress unsafe libc call warnings in addition
to the unsafe buffer warnings.
To prevent back-sliding on files which have been made safe with respect
to unsafe buffers, there is now a per-file pragma which suppresses the
libc warnings while still enforcing the unsafe buffer warnings.
```
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/ABC): Remove libc calls to fix these warnings.
#pragma allow_unsafe_libc_calls
#endif
```
An initial set of files containing allow\_unsafe\_libc\_calls has been
uploaded; please keep these in place until the pending libc enforcement
is enabled for Chromium.
#### Removing pragmas from individual files.
The recommended process for removing pragmas from individual files is:
1. 1. Remove the pragma from the file.
2. Use the compiler warnings now generated to identify the individual
expressions to suppress (see the next section).
### Opting out individual expressions
Individual expressions or blocks of code are opted out by using the
`UNSAFE_BUFFERS()` macro as defined in [`//base/compiler_specific.h`[(../base/compiler_specific.h)
file. These should be rare once a project is fully converted, except
@ -65,25 +151,13 @@ the `UNSAFE_BUFFERS()` macro, but allows easier searching for code in need
of revision. Add TODO() comment, along the lines of
`// TODO(crbug.com/xxxxxx): resolve safety issues`.
## Unsafe libc call suppressions.
This mechanism opts expressions out of all warning categories (unsafe
buffers and unsafe libc calls).
The above mechanisms also suppress unsafe libc call warnings in addition
to the unsafe buffer warnings.
#### Removing UNSAFE_TODO() from individual expressions
To prevent back-sliding on files which have been made safe with respect
to unsafe buffers, there is now a per-file pragma which suppresses the
libc warnings while still enforcing the unsafe buffer warnings.
```
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/ABC): Remove this and convert code to safer constructs.
#pragma allow_unsafe_libc_calls
#endif
```
An initial set of files containing these suppressions will be uploaded
presently; please keep these in place until the pending libc enforcement
is enabled for Chromium.
We seek to convert code to use owning containers like HeapArray and vector,
as explained in the next section.
## Container-based ecosystem
@ -188,9 +262,11 @@ For arrays where the size is determined by the compiler (e.g.
should be used along with the `auto` keyword:
`auto arr = std::to_array<int>({1, 3, 5});`
## Avoid reinterpret_cast
### Avoid reinterpret_cast
### Writing to a byte span
Casts to bytes are common and can be handled as follows.
#### Writing to a byte span
A common idiom in older code is to write into a byte array by casting
the array into a pointer to a larger type (such as `uint32_t` or `float`)
@ -255,7 +331,7 @@ void write_floats(base::span<uint8_t> out, std::vector<const float> floats) {
}
```
### Reading from a byte span
#### Reading from a byte span
Instead of turning a `span<const uint8_t>` into a pointer of a larger type,
which can cause Undefined Behaviour, read values out of the byte span and
@ -487,7 +563,7 @@ Use a range, with `span()` providing a view of a subset of the range:
auto it = std::ranges::find(base::span(vec).subspan(offset), 20);
```
# Functions with array pointer parameters
### Functions with array pointer parameters
Functions that receive a pointer argument into an array may read
or write out of bounds of that array if subsequent manual size
@ -509,7 +585,7 @@ access. For instance, replace `memcpy()`, `std::copy()` and
`std::ranges::copy()` with `base::span::copy_from()`. And
replace `memset()` with `std::ranges::fill()`.
# Aligned memory
### Aligned memory
An aligned heap allocation can be constructed into a `base::HeapArray` through
the `base::AlignedUninit<T>(size, alignment)` function in
@ -537,3 +613,4 @@ auto [a, s] = base::AlignedUninitCharArray<float>(size, alignment);
base::AlignedHeapArray<char> array = std::move(a);
base::span<float> span = s;
```