aboutsummaryrefslogtreecommitdiff
path: root/clang/docs/SafeBuffers.rst
blob: da75907e174a006a879dbf567b6b08692910199a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
================
C++ Safe Buffers
================

.. contents::
   :local:


Introduction
============

Clang can be used to harden your C++ code against buffer overflows, an otherwise
common security issue with C-based languages.

The solution described in this document is an integrated programming model as
it combines:

- a family of opt-in Clang warnings (``-Wunsafe-buffer-usage``) emitted at
  during compilation to help you update your code to encapsulate and propagate
  the bounds information associated with pointers;
- runtime assertions implemented as part of
  (`libc++ hardening modes <https://libcxx.llvm.org/Hardening.html>`_)
  that eliminate undefined behavior as long as the coding convention
  is followed and the bounds information is therefore available and correct.

The goal of this work is to enable development of bounds-safe C++ code. It is
not a "push-button" solution; depending on your codebase's existing
coding style, significant (even if largely mechanical) changes to your code
may be necessary. However, it allows you to achieve valuable safety guarantees
on security-critical parts of your codebase.

This solution is under active development. It is already useful for its purpose
but more work is being done to improve ergonomics and safety guarantees
and reduce adoption costs.

The solution aligns in spirit with the "Ranges" safety profile
that was `proposed <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3274r0.pdf>`_
by Bjarne Stroustrup for standardization alongside other C++ safety features.


Pre-Requisites
==============

In order to achieve bounds safety, your codebase needs to have access to
well-encapsulated bounds-safe container, view, and iterator types.
If your project uses libc++, standard container and view types such as
``std::vector`` and ``std::span`` can be made bounds-safe by enabling
the "fast" `hardening mode <https://libcxx.llvm.org/Hardening.html>`_
(passing ``-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST``) to your
compiler) or any of the stricter hardening modes.

In order to harden iterators, you'll need to also obtain a libc++ binary
built with ``_LIBCPP_ABI_BOUNDED_ITERATORS`` -- which is a libc++ ABI setting
that needs to be set for your entire target platform if you need to maintain
binary compatibility with the rest of the platform.

A relatively fresh version of C++ is recommended. In particular, the very useful
standard view class ``std::span`` requires C++20.

Other implementations of the C++ standard library may provide different
flags to enable such hardening.

If you're using custom containers and views, they will need to be hardened
this way as well, but you don't necessarily need to do this ahead of time.

This approach can theoretically be applied to plain C codebases,
assuming that safe primitives are developed to encapsulate all buffer accesses,
acting as "hardened custom containers" to replace raw pointers.
However, such approach would be very unergonomic in C, and safety guarantees
will be lower due to lack of good encapsulation technology. A better approach
to bounds safety for non-C++ programs,
`-fbounds-safety <https://clang.llvm.org/docs/BoundsSafety.html>`_,
is currently in development.

Technically, safety guarantees cannot be provided without hardening
the entire technology stack, including all of your dependencies.
However, applying such hardening technology to even a small portion
of your code may be significantly better than nothing.


The Programming Model for C++
=============================

Assuming that hardened container, view, and iterator classes are available,
what remains is to make sure they are used consistently in your code.
Below we define the specific coding convention that needs to be followed
in order to guarantee safety and how the compiler technology
around ``-Wunsafe-buffer-usage`` assists with that.


Buffer operations should never be performed over raw pointers
-------------------------------------------------------------

Every time a memory access is made, a bounds-safe program must guarantee
that the range of accessed memory addresses falls into the boundaries
of the memory allocated for the object that's being accessed.
In order to establish such a guarantee, the information about such valid range
of addresses -- the **bounds information** associated with the accessed address
-- must be formally available every time a memory access is performed.

A raw pointer does not naturally carry any bounds information.
The bounds information for the pointer may be available *somewhere*, but
it is not associated with the pointer in a formal manner, so a memory access
performed through a raw pointer cannot be automatically verified to be
bounds-safe by the compiler.

That said, the Safe Buffers programming model does **not** try to eliminate
**all** pointer usage. Instead it assumes that most pointers point to
individual objects, not buffers, and therefore they typically aren't
associated with buffer overflow risks. For that reason, in order to identify
the code that requires manual intervention, it is desirable to initially shift
the focus away from the pointers themselves, and instead focus on their
**usage patterns**.

The compiler warning ``-Wunsafe-buffer-usage`` is built to assist you
with this step of the process. A ``-Wunsafe-buffer-usage`` warning is
emitted whenever one of the following **buffer operations** are performed
on a raw pointer:

- array indexing with ``[]``,
- pointer arithmetic,
- bounds-unsafe standard C functions such as ``std::memcpy()``,
- C++ smart pointer operations such as ``std::unique_ptr<T[N]>::operator[]()``,
  which unfortunately cannot be made fully safe within the rules of
  the C++ standard (as of C++23).

This is sufficient for identifying each raw buffer pointer in the program at
**at least one point** during its lifetime across your software stack.

For example, both of the following functions are flagged by
``-Wunsafe-buffer-usage`` because ``pointer`` gets identified as an unsafe
buffer pointer. Even though the second function does not directly access
the buffer, the pointer arithmetic operation inside it may easily be
the only formal "hint" in the program that the pointer does indeed point
to a buffer of multiple objects::

    int get_last_element(int *pointer, size_t size) {
      return ptr[sz - 1]; // warning: unsafe buffer access
    }

    int *get_last_element_ptr(int *pointer, size_t size) {
      return ptr + (size - 1); // warning: unsafe pointer arithmetic
    }


All buffers need to be encapsulated into safe container and view types
----------------------------------------------------------------------

It immediately follows from the previous requirement that once an unsafe pointer
is identified at any point during its lifetime, it should be immediately wrapped
into a safe container type (if the allocation site is "nearby") or a safe
view type (if the allocation site is "far away"). Not only memory accesses,
but also non-access operations such as pointer arithmetic need to be covered
this way in order to benefit from the respective runtime bounds checks.

If a **container** type (``std::array``, ``std::vector``, ``std::string``)
is used for allocating the buffer, this is the best-case scenario because
the container naturally has access to the correct bounds information for the
buffer, and the runtime bounds checks immediately kick in. Additionally,
the container type may provide automatic lifetime management for the buffer
(which may or may not be desirable).

If a **view** type is used (``std::span``, ``std::string_view``), this typically
means that the bounds information for the "adopted" pointer needs to be passed
to the view's constructor manually. This makes runtime checks immediately
kick in with respect to the provided bounds information, which is an immediate
improvement over the raw pointer. However, this situation is still fundamentally
insufficient for security purposes, because **bounds information provided
this way cannot be guaranteed to be correct**.

For example, the function ``get_last_element()`` we've seen in the previous
section can be made **slightly** safer this way::

    int get_last_element(int *pointer, size_t size) {
      std::span<int> sp(pointer, size);
      return sp[size - 1]; // warning addressed
    }

Here ``std::span`` eliminates the potential concern that the operation
``size - 1`` may overflow when ``sz`` is equal to ``0``, leading to a buffer
"underrun". However, such program does not provide a guarantee that
the variable ``sz`` correctly represents the **actual** size fo the buffer
pointed to by ``ptr``. The ``std::span`` constructed this way may be ill-formed.
It may fail to protect you from overrunning the original buffer.

The following example demonstrates one of the most dangerous anti-patterns
of this nature::

    void convert_data(int *source_buf, size_t source_size,
                      int *target_buf, size_t target_size) {
      // Terrible: mismatched pointer / size.
      std::span<int> target_span(target_buf, source_size);
      // ...
    }

The second parameter of ``std::span`` should never be the **desired** size
of the buffer. It should always be the **actual** size of the buffer.
Such code often indicates that the original code has already contained
a vulnerability -- and the use of a safe view class failed to prevent it.

If ``target_span`` actually needs to be of size ``source_size``, a significantly
safer way to produce such a span would be to build it with the correct size
first, and then resize it to the desired size by calling ``.first()``::

    void convert_data(int *source_buf, size_t source_size,
                      int *target_buf, size_t target_size) {
      // Safer.
      std::span<int> target_span(target_buf, target_size).first(source_size);
      // ...
    }

However, these are still half-measures. This code still accepts the
bounds information from the caller in an **informal** manner, and such bounds
information cannot be guaranteed to be correct.

In order to mitigate problems of this nature in their entirety,
the third guideline is imposed.


Encapsulation of bounds information must be respected continuously
------------------------------------------------------------------

The allocation site of the object is the only reliable source of bounds
information for that object. For objects with long lifespans across
multiple functions or even libraries in the software stack, it is essential
to formally preserve the original bounds information as it's being passed
from one piece of code to another.

Standard container and view classes are designed to preserve bounds information
correctly **by construction**. However, they offer a number of ways to "break"
encapsulation, which may cause you to temporarily lose track of the correct
bounds information:

- The two-parameter constructor ``std::span(ptr, size)`` allows you to
  assemble an ill-formed ``std::span``;
- Conversely, you can unwrap a container or a view object into a raw pointer
  and a raw size by calling its ``.data()`` and ``.size()`` methods.
- The overloaded ``operator&()`` found on container and iterator classes
  acts similarly to ``.data()`` in this regard; operations such as
  ``&span[0]`` and ``&*span.begin()`` are effectively unsafe.

Additional ``-Wunsafe-buffer-usage`` warnings are emitted when encapsulation
of **standard** containers is broken in this manner. If you're using
non-standard containers, you can achieve a similar effect with facilities
described in the next section: :ref:`customization`.

For example, our previous attempt to address the warning in
``get_last_element()`` has actually introduced a new warning along the way,
that notifies you about the potentially incorrect bounds information
passed into the two-parameter constructor of ``std::span``::

    int get_last_element(int *pointer, size_t size) {
      std::span<int> sp(pointer, size); // warning: unsafe constructor
      return sp[size - 1];
    }

In order to address this warning, you need to make the function receive
the bounds information from the allocation site in a formal manner.
The function doesn't necessarily need to know where the allocation site is;
it simply needs to be able to accept bounds information **when** it's available.
You can achieve this by refactoring the function to accept a ``std::span``
as a parameter::

    int get_last_element(std::span<int> sp) {
      return sp[size - 1];
    }

This solution puts the responsibility for making sure the span is well-formed
on the **caller**. They should do the same, so that eventually the
responsibility is placed on the allocation site!

Such definition is also very ergonomic as it naturally accepts arbitrary
standard containers without any additional code at the call site::

    void use_last_element() {
      std::vector<int> vec { 1, 2, 3 };
      int x = get_last_element(vec);  // x = 3
    }

Such code is naturally bounds-safe because bounds-information is passed down
from the allocation site to the buffer access site. Only safe operations
are performed on container types. The containers are never "unforged" into
raw pointer-size pairs and never "reforged" again. This is what ideal
bounds-safe C++ code looks like.


.. _customization:

Backwards Compatibility, Interoperation with Unsafe Code, Customization
=======================================================================

Some of the code changes described above can be somewhat intrusive.
For example, changing a function that previously accepted a pointer and a size
separately, to accept a ``std::span`` instead, may require you to update
every call site of the function. This is often undesirable and sometimes
completely unacceptable when backwards compatibility is required.

In order to facilitate **incremental adoption** of the coding convention
described above, as well as to handle various unusual situations, the compiler
provides two additional facilities to give the user more control over
``-Wunsafe-buffer-usage`` diagnostics:

- ``#pragma clang unsafe_buffer_usage`` to mark code as unsafe and **suppress**
  ``-Wunsafe-buffer-usage`` warnings in that code.
- ``[[clang::unsafe_buffer_usage]]`` to annotate potential sources of
  discontinuity of bounds information -- thus introducing
  **additional** ``-Wunsafe-buffer-usage`` warnings.

In this section we describe these facilities in detail and show how they can
help you with various unusual situations.

Suppress unwanted warnings with ``#pragma clang unsafe_buffer_usage``
---------------------------------------------------------------------

If you really need to write unsafe code, you can always suppress all
``-Wunsafe-buffer-usage`` warnings in a section of code by surrounding
that code with the ``unsafe_buffer_usage`` pragma. For example, if you don't
want to address the warning in our example function ``get_last_element()``,
here is how you can suppress it::

    int get_last_element(int *pointer, size_t size) {
      #pragma clang unsafe_buffer_usage begin
      return ptr[sz - 1]; // warning suppressed
      #pragma clang unsafe_buffer_usage end
    }

This behavior is analogous to ``#pragma clang diagnostic`` (`documentation
<https://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas>`_)
However, ``#pragma clang unsafe_buffer_usage`` is specialized and recommended
over ``#pragma clang diagnostic`` for a number of technical and non-technical
reasons. Most importantly, ``#pragma clang unsafe_buffer_usage`` is more
suitable for security audits because it is significantly simpler and
describes unsafe code in a more formal manner. On the contrary,
``#pragma clang diagnostic`` comes with a push/pop syntax (as opposed to
the begin/end syntax) and it offers ways to suppress warnings without
mentioning them by name (such as ``-Weverything``), which can make it
difficult to determine at a glance whether the warning is suppressed
on any given line of code.

There are a few natural reasons to use this pragma:

- In implementations of safe custom containers. You need this because ultimately
  ``-Wunsafe-buffer-usage`` cannot help you verify that your custom container
  is safe. It will naturally remind you to audit your container's implementation
  to make sure it has all the necessary runtime checks, but ultimately you'll
  need to suppress it once the audit is complete.
- In performance-critical code where bounds-safety-related runtime checks
  cause an unacceptable performance regression. The compiler can theoretically
  optimize them away (eg. replace a repeated bounds check in a loop with
  a single check before the loop) but it is not guaranteed to do that.
- For incremental adoption purposes. If you want to adopt the coding convention
  gradually, you can always surround an entire file with the
  ``unsafe_buffer_usage`` pragma and then "make holes" in it whenever
  you address warnings on specific portions of the code.
- In the code that interoperates with unsafe code. This may be code that
  will never follow the programming model (such as plain C  code that will
  never be converted to C++) or with the code that simply haven't been converted
  yet.

Interoperation with unsafe code may require a lot of suppressions.
You are encouraged to introduce "unsafe wrapper functions" for various unsafe
operations that you need to perform regularly.

For example, if you regularly receive pointer/size pairs from unsafe code,
you may want to introduce a wrapper function for the unsafe span constructor::

    #pragma clang unsafe_buffer_usage begin

    template <typename T>
    std::span<T> unsafe_forge_span(T *pointer, size_t size) {
      return std::span(pointer, size);
    }

    #pragma clang unsafe_buffer_usage end

Such wrapper function can be used to suppress warnings about unsafe span
constructor usage in a more ergonomic manner::

    void use_unsafe_c_struct(unsafe_c_struct *s) {
      // No warning here.
      std::span<int> sp = unsafe_forge_span(s->pointer, s->size);
      // ...
    }

The code remains unsafe but it also continues to be nicely readable, and it
proves that ``-Wunsafe-buffer-usage`` has done it best to notify you about
the potential unsafety. A security auditor will need to keep an eye on such
unsafe wrappers. **It is still up to you to confirm that the bounds information
passed into the wrapper is correct.**


Flag bounds information discontinuities with ``[[clang::unsafe_buffer_usage]]``
-------------------------------------------------------------------------------

The clang attribute ``[[clang::unsafe_buffer_usage]]``
(`attribute documentation
<https://clang.llvm.org/docs/AttributeReference.html#unsafe-buffer-usage>`_)
allows the user to annotate various objects, such as functions or member
variables, as incompatible with the Safe Buffers programming model.
You are encouraged to do that for arbitrary reasons, but typically the main
reason to do that is when an unsafe function needs to be provided for
backwards compatibility.

For example, in the previous section we've seen how the example function
``get_last_element()`` needed to have its parameter types changed in order
to preserve the continuity of bounds information when receiving a buffer pointer
from the caller. However, such a change breaks both API and ABI compatibility.
The code that previously used this function will no longer compile, nor link,
until every call site of that function is updated. You can reclaim the
backwards compatibility -- in terms of both API and ABI -- by adding
a "compatibility overload"::

    int get_last_element(std::span<int> sp) {
      return sp[size - 1];
    }

    [[clang::unsafe_buffer_usage]] // Please use the new function.
    int get_last_element(int *pointer, size_t size) {
      // Avoid code duplication - simply invoke the safe function!
      // The pragma suppresses the unsafe constructor warning.
      #pragma clang unsafe_buffer_usage begin
      return get_last_element(std::span(pointer, size));
      #pragma clang unsafe_buffer_usage end
    }


Such an overload allows the surrounding code to continue to work.
It is both source-compatible and binary-compatible. It is also strictly safer
than the original function because the unsafe buffer access through raw pointer
is replaced with a safe ``std::span`` access no matter how it's called. However,
because it requires the caller to pass the pointer and the size separately,
it violates our "bounds information continuity" principle. This means that
the callers who care about bounds safety needs to be encouraged to use the
``std::span``-based overload instead. Luckily, the attribute
``[[clang::unsafe_buffer_usage]]`` causes a ``-Wunsafe-buffer-usage`` warning
to be displayed at every call site of the compatibility overload in order to
remind the callers to update their code::

    void use_last_element() {
      std::vector<int> vec { 1, 2, 3 };

      // no warning
      int x = get_last_element(vec);

      // warning: this overload introduces unsafe buffer manipulation
      int x = get_last_element(vec.data(), vec.size());
    }

The compatibility overload can be further simplified with the help of the
``unsafe_forge_span()`` wrapper as described in the previous section --
and it even makes the pragmas unnecessary::

    [[clang::unsafe_buffer_usage]] // Please use the new function.
    int get_last_element(int *pointer, size_t size) {
      // Avoid code duplication - simply invoke the safe function!
      return get_last_element(unsafe_forge_span(pointer, size));
    }

Notice how the attribute ``[[clang::unsafe_buffer_usage]]`` does **not**
suppress the warnings within the function on its own. Similarly, functions whose
entire definitions are covered by ``#pragma clang unsafe_buffer_usage`` do
**not** become automatically annotated with the attribute
``[[clang::unsafe_buffer_usage]]``. They serve two different purposes:

- The pragma says that the function isn't safely **written**;
- The attribute says that the function isn't safe to **use**.

Also notice how we've made an **unsafe** wrapper for a **safe** function.
This is significantly better than making a **safe** wrapper for an **unsafe**
function. In other words, the following solution is significantly more unsafe
and undesirable than the previous solution::

    int get_last_element(std::span<int> sp) {
      // You've just added that attribute, and now you need to
      // immediately suppress the warning that comes with it?
      #pragma clang unsafe_buffer_usage begin
      return get_last_element(sp.data(), sp.size());
      #pragma clang unsafe_buffer_usage end
    }


    [[clang::unsafe_buffer_usage]]
    int get_last_element(int *pointer, size_t size) {
      // This access is still completely unchecked. What's the point of having
      // perfect bounds information if you aren't performing runtime checks?
      #pragma clang unsafe_buffer_usage begin
      return ptr[sz - 1];
      #pragma clang unsafe_buffer_usage end
    }

**Structs and classes**, unlike functions, cannot be overloaded. If a struct
contains an unsafe buffer (in the form of a nested array or a pointer/size pair)
then it is typically impossible to replace them with a safe container (such as
``std::array`` or ``std::span`` respectively) without breaking the layout
of the struct and introducing both source and binary incompatibilities with
the surrounding client code.

Additionally, member variables of a class cannot be naturally "hidden" from
client code. If a class needs to be used by clients who haven't updated to
C++20 yet, you cannot use the C++20-specific ``std::span`` as a member variable
type. If the definition of a struct is shared with plain C code that manipulates
member variables directly, you cannot use any C++-specific types for these
member variables.

In such cases there's usually no backwards-compatible way to use safe types
directly. The best option is usually to discourage the clients from using
member variables directly by annotating the member variables with the attribute
``[[clang::unsafe_buffer_usage]]``, and then to change the interface
of the class to provide safe "accessors" to the unsafe data.

For example, let's assume the worst-case scenario: ``struct foo`` is an unsafe
struct type fully defined in a header shared between plain C code and C++ code::

    struct foo {
      int *pointer;
      size_t size;
    };

In this case you can achieve safety in C++ code by annotating the member
variables as unsafe and encapsulating them into safe accessor methods::

    struct foo {
      [[clang::unsafe_buffer_usage]]
      int *pointer;
      [[clang::unsafe_buffer_usage]]
      size_t size;

    // Avoid showing this code to clients who are unable to digest it.
    #if __cplusplus >= 202002L
      std::span<int> get_pointer_as_span() {
        #pragma clang unsafe_buffer_usage begin
        return std::span(pointer, size);
        #pragma clang unsafe_buffer_usage end
      }

      void set_pointer_from_span(std::span<int> sp) {
        #pragma clang unsafe_buffer_usage begin
        pointer = sp.data();
        size = sp.size();
        #pragma clang unsafe_buffer_usage end
      }

      // Potentially more utility functions.
    #endif
    };

Future Work
===========

The ``-Wunsafe-buffer-usage`` technology is in active development. The warning
is largely ready for everyday use but it is continuously improved to reduce
unnecessary noise as well as cover some of the trickier unsafe operations.

Fix-It Hints for ``-Wunsafe-buffer-usage``
------------------------------------------

A code transformation tool is in development that can semi-automatically
transform large bodies of code to follow the C++ Safe Buffers programming model.
It can currently be accessed by passing the experimental flag
``-fsafe-buffer-usage-suggestions`` in addition to ``-Wunsafe-buffer-usage``.

Fixits produced this way currently assume the default approach described
in this document as they suggest standard containers and views (most notably
``std::span`` and ``std::array``) as replacements for raw buffer pointers.
This also additionally requires libc++ hardening in order to make the runtime
bounds checks actually happen.

Static Analysis to Identify Suspicious Sources of Bounds Information
--------------------------------------------------------------------

The unsafe constructor ``span(pointer, size)`` is often a necessary evil
when it comes to interoperation with unsafe code. However, passing the
correct bounds information to such constructor is often difficult.
In order to detect those ``span(target_pointer, source_size)`` anti-patterns,
path-sensitive analysis performed by `the clang static analyzer
<https://clang-analyzer.llvm.org>`_ can be taught to identify situations
when the pointer and the size are coming from "suspiciously different" sources.

Such analysis will be able to identify the source of information with
significantly higher precision than that of the compiler, making it much better
at identifying incorrect bounds information in your code while producing
significantly fewer warnings. It will also need to bypass
``#pragma clang unsafe_buffer_usage`` suppressions and "see through"
unsafe wrappers such as ``unsafe_forge_span`` -- something that
the static analyzer is naturally capable of doing.