Skip to content

Commit fbf5360

Browse files
committed
Update document for clarifications
1 parent 1aee64e commit fbf5360

File tree

1 file changed

+154
-27
lines changed

1 file changed

+154
-27
lines changed

clang/docs/BoundsSafety.rst

Lines changed: 154 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,15 @@ not adopted the same programming model.
128128
approaches to reduce the adoption burden while maintaining the ABI. In this
129129
model, local variables of pointer type are implicitly treated as wide pointers,
130130
allowing them to carry bounds information without requiring explicit bounds
131-
annotations. This approach does not impact the ABI, as local variables are
132-
hidden from the ABI. Pointers associated with any other variables are treated as
133-
single object pointers (i.e., ``__single``), ensuring that they always have the
134-
tightest bounds by default and offering a strong bounds safety guarantee.
131+
annotations. Please note that this approach doesn't apply to function parameters
132+
which are considered ABI-visible. As local variables are typically hidden from
133+
the ABI, this approach has a marginal impact on it. In addition,
134+
``-fbounds-safety`` employs compile-time restrictions to prevent implicit wide
135+
pointers from silently breaking the ABI (see `ABI implications of default bounds
136+
annotations`_). Pointers associated with any other variables, including function
137+
parameters, are treated as single object pointers (i.e., ``__single``), ensuring
138+
that they always have the tightest bounds by default and offering a strong
139+
bounds safety guarantee.
135140

136141
By implementing default bounds annotations based on ABI visibility, a
137142
considerable portion of C code can operate without modifications within this
@@ -208,8 +213,9 @@ meaning they do not have ABI implications.
208213
elements of pointee type. ``N`` is an expression of integer type which can be
209214
a simple reference to declaration, a constant including calls to constant
210215
functions, or an arithmetic expression that does not have side effect. The
211-
annotation cannot apply to pointers to incomplete types or types without size
212-
such as ``void *``.
216+
``__counted_by`` annotation cannot apply to pointers to incomplete types or
217+
types without size such as ``void *``. Instead, ``__sized_by`` can be used to
218+
describe the byte count.
213219
* ``__sized_by(N)`` : The pointer points to memory that contains ``N`` bytes.
214220
Just like the argument of ``__counted_by``, ``N`` is an expression of integer
215221
type which can be a constant, a simple reference to a declaration, or an
@@ -432,9 +438,10 @@ The ``__ptrcheck_abi_assume_*ATTR*()`` macros are defined as pragmas in the
432438
toolchain header (See `Portability with toolchains that do not support the
433439
extension`_ for more details about the toolchain header):
434440

435-
```C
441+
.. code-block:: C
442+
436443
#define __ptrcheck_abi_assume_single() \
437-
_Pragma("clang abi_ptr_attr set(single)")
444+
_Pragma("clang abi_ptr_attr set(single)")
438445

439446
#define __ptrcheck_abi_assume_indexable() \
440447
_Pragma("clang abi_ptr_attr set(indexable)")
@@ -444,22 +451,22 @@ extension`_ for more details about the toolchain header):
444451

445452
#define __ptrcheck_abi_assume_unsafe_indexable() \
446453
_Pragma("clang abi_ptr_attr set(unsafe_indexable)")
447-
```
454+
448455

449456
ABI implications of default bounds annotations
450457
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
451458

452-
Although modifying types of a local variable doesn't impact the ABI, taking the
453-
address of such a modified type could create a pointer type that has an ABI
454-
mismatch. Looking at the following example, ``int *local`` is implicitly ``int
455-
*__bidi_indexable`` and thus the type of ``&local`` is a pointer to ``int
456-
*__bidi_indexable``. On the other hand, in ``void foo(int **)``, the parameter
457-
type is a pointer to ``int *__single`` (i.e., ``void foo(int *__single
458-
*__single)``) (or a pointer to ``int *__unsafe_indexable`` if it's from a system
459-
header). The compiler reports an error for casts between pointers whose elements
460-
have incompatible pointer attributes. This way, ``-fbounds-safety`` prevents
461-
pointers that are implicitly ``__bidi_indexable`` from silently escaping thereby
462-
breaking the ABI.
459+
Although simply modifying types of a local variable doesn't normally impact the
460+
ABI, taking the address of such a modified type could create a pointer type that
461+
has an ABI mismatch. Looking at the following example, ``int *local`` is
462+
implicitly ``int *__bidi_indexable`` and thus the type of ``&local`` is a
463+
pointer to ``int *__bidi_indexable``. On the other hand, in ``void foo(int
464+
**)``, the parameter type is a pointer to ``int *__single`` (i.e., ``void
465+
foo(int *__single *__single)``) (or a pointer to ``int *__unsafe_indexable`` if
466+
it's from a system header). The compiler reports an error for casts between
467+
pointers whose elements have incompatible pointer attributes. This way,
468+
``-fbounds-safety`` prevents pointers that are implicitly ``__bidi_indexable``
469+
from silently escaping thereby breaking the ABI.
463470

464471
.. code-block:: c
465472
@@ -472,16 +479,136 @@ breaking the ABI.
472479
foo(&local);
473480
}
474481
482+
A local variable may still be exposed to the ABI if ``typeof()`` takes the type
483+
of local variable to define an interface as shown in the following example.
484+
485+
.. code-block:: C
486+
487+
// bar.c
488+
void bar(int *) { ... }
489+
490+
// foo.c
491+
void foo(void) {
492+
int *p; // implicitly `int *__bidi_indexable p`
493+
extern void bar(typeof(p)); // creates an interface of type
494+
// `void bar(int *__bidi_indexable)`
495+
}
496+
497+
Doing this may break the ABI if the parameter is not ``__bidi_indexable`` at the
498+
definition of function ``bar()`` which is likely the case because parameters are
499+
``__single`` by default without an explicit annotation.
500+
501+
In order to avoid an implicitly wide pointer from silently breaking the ABI, the
502+
compiler reports a warning when ``typeof()`` is used on an implicit wide pointer
503+
at any ABI visible context (e.g., function prototype, struct definition, etc.).
504+
505+
.. _Default pointer types in typeof:
506+
507+
Default pointer types in ``typeof()``
508+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
509+
510+
When ``typeof()`` takes an expression, it respects the bounds annotation on
511+
the expression type, including the bounds annotation is implcit. For example,
512+
the global variable ``g`` in the following code is implicitly ``__single`` so
513+
``typeof(g)`` gets ``char *__single``. The similar is true for the parameter
514+
``p``, so ``typeof(p)`` returns ``void *__single``. The local variable ``l`` is
515+
implicitly ``__bidi_indexable``, so ``typeof(l)`` becomes
516+
``int *__bidi_indexable``.
517+
518+
.. code-block:: C
519+
520+
char *g; // typeof(g) == char *__single
521+
522+
void foo(void *p) {
523+
// typeof(p) == void *__single
524+
525+
int *l; // typeof(l) == int *__bidi_indexable
526+
}
527+
528+
When the type of expression has an "external" bounds annotation, e.g.,
529+
``__sized_by``, ``__counted_by``, etc., the compiler may report an error on
530+
``typeof`` if the annotation creates a dependency with another declaration or
531+
variable. For example, the compiler reports an error on ``typeof(p1)`` shown in
532+
the following code because allowing it can potentially create another type
533+
dependent on the parameter ``size`` in a different context (Please note that an
534+
external bounds annotation on a parameter may only refer to another parameter of
535+
the same function). On the other hand, ``typeof(p2)`` works resulting in ``int
536+
*__counted_by(10)``, since it doesn't depend on any other declaration.
537+
538+
.. TODO: add a section describing constraints on external bounds annotations
539+
540+
.. code-block:: C
541+
542+
void foo(int *__counted_by(size) p1, size_t size) {
543+
// typeof(p1) == int *__counted_by(size)
544+
// -> a compiler error as it tries to create another type
545+
// dependent on `size`.
546+
547+
int *__counted_by(10) p2; // typeof(p2) == int *__counted_by(10)
548+
// -> no error
549+
550+
}
551+
552+
When ``typeof()`` takes a type name, the compiler doesn't apply an implicit
553+
bounds annotation on the named pointer types. For example, ``typeof(int*)``
554+
returns ``int *`` without any bounds annotation. A bounds annotation may be
555+
added after the fact depending on the context. In the following example,
556+
``typeof(int *)`` returns ``int *`` so it's equivalent as the local variable is
557+
declared as ``int *l``, so it eventually becomes implicitly
558+
``__bidi_indexable``.
559+
560+
.. code-block:: c
561+
562+
void foo(void) {
563+
typeof(int *) l; // `int *__bidi_indexable` (same as `int *l`)
564+
}
565+
566+
The programmers can still explicitly add a bounds annotation on the types named
567+
inside ``typeof``, e.g., ``typeof(int *__bidi_indexable)``, which evaluates to
568+
``int *__bidi_indexable``.
569+
570+
475571
Default pointer types in ``sizeof()``
476572
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
477573

478-
A pointer type in ``sizeof()`` does not have an implicit bounds annotation. When
479-
a bounds attribute is not specified, the evaluated pointer type is treated
480-
identically to a plain C pointer type. Therefore, ``sizeof(int*)`` remains the
481-
same with or without ``-fbounds-safety``. That said, programmers can explicitly
482-
add attribute to the types, e.g., ``sizeof(int *__bidi_indexable)``, in which
483-
case the sizeof evaluates to the size of type ``int *__bidi_indexable`` (the
484-
value equivalent to ``3 * sizeof(int*)``).
574+
When ``sizeof()`` takes a type name, the compiler doesn't apply an implicit
575+
bounds annotation on the named pointer types. This means if a bounds annotation
576+
is not specified, the evaluated pointer type is treated identically to a plain C
577+
pointer type. Therefore, ``sizeof(int*)`` remains the same with or without
578+
``-fbounds-safety``. That said, programmers can explicitly add attribute to the
579+
types, e.g., ``sizeof(int *__bidi_indexable)``, in which case the sizeof
580+
evaluates to the size of type ``int *__bidi_indexable`` (the value equivalent to
581+
``3 * sizeof(int*)``).
582+
583+
When ``sizeof()`` takes an expression, i.e., ``sizeof(expr``, it behaves as
584+
``sizeof(typeof(expr))``, except that ``sizeof(expr)`` does not report an error
585+
with ``expr`` that has a type with an external bounds annotation dependent on
586+
another declaration, whereas ``typeof()`` on the same expression would be an
587+
error as described in :ref:`Default pointer types in typeof`.
588+
The following example describes this behavior.
589+
590+
.. code-block:: c
591+
592+
void foo(int *__counted_by(size) p, size_t size) {
593+
// sizeof(p) == sizeof(int *__counted_by(size)) == sizeof(int *)
594+
// typeof(p): error
595+
};
596+
597+
Default pointer types in ``alignof()``
598+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
599+
600+
``alignof()`` only takes a type name as the argument and it doesn't take an
601+
expression. Similar to ``sizeof()`` and ``typeof``, the compiler doesn't apply
602+
an implicit bounds annotation on the pointer types named inside ``alignof()``.
603+
Therefore, ``alignof(T *)`` remains the same with or without
604+
``-fbounds-safety``, evaluating into the alignment of the raw pointer ``T *``.
605+
The programmers can explicitly add a bounds annotation to the types, e.g.,
606+
``alignof(int *__bidi_indexable)``, which returns the alignment of ``int
607+
*__bidi_indexable``. A bounds annotation including an internal bounds annotation
608+
(i.e., ``__indexable`` and ``__bidi_indexable``) doesn't affect the alignment of
609+
the original pointer. Therefore, ``alignof(int *__bidi_indexable)`` is equal to
610+
``alignof(int *)``.
611+
485612

486613
Default pointer types used in C-style casts
487614
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)