Skip to content

[LangRef] Document accessing memory outside of object is UB. #128429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 60 additions & 50 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -729,8 +729,8 @@ units that do not include the definition.
As SSA values, global variables define pointer values that are in scope
(i.e. they dominate) all basic blocks in the program. Global variables
always define a pointer to their "content" type because they describe a
region of memory, and all memory objects in LLVM are accessed through
pointers.
region of memory, and all :ref:`allocated object<allocatedobjects>` in LLVM are
accessed through pointers.

Global variables can be marked with ``unnamed_addr`` which indicates
that the address is not significant, only the content. Constants marked
Expand Down Expand Up @@ -2169,7 +2169,8 @@ For example:
A ``nofree`` function is explicitly allowed to free memory which it
allocated or (if not ``nosync``) arrange for another thread to free
memory on it's behalf. As a result, perhaps surprisingly, a ``nofree``
function can return a pointer to a previously deallocated memory object.
function can return a pointer to a previously deallocated
:ref:`allocated object<allocatedobjects>`.
``noimplicitfloat``
Disallows implicit floating-point code. This inhibits optimizations that
use floating-point code and floating-point registers for operations that are
Expand Down Expand Up @@ -3280,31 +3281,43 @@ This information is passed along to the backend so that it generates
code for the proper architecture. It's possible to override this on the
command line with the ``-mtriple`` command line option.


.. _allocatedobjects:

Allocated Objects
-----------------

An allocated object, memory object, or simply object, is a region of a memory
space that is reserved by a memory allocation such as :ref:`alloca <i_alloca>`,
heap allocation calls, and global variable definitions. Once it is allocated,
the bytes stored in the region can only be read or written through a pointer
that is :ref:`based on <pointeraliasing>` the allocation value. If a pointer
that is not based on the object tries to read or write to the object, it is
undefined behavior. Trying to read or write memory outside of an allocated
object, including accesses partially outside an allocated object, is undefined
behavior.

The following properties hold for all allocated objects:

- no allocated object may cross the unsigned address space boundary (including
the pointer after the end of the object),
- the size of all allocated objects must be non-negative and not exceed the
largest signed integer that fits into the index type.

.. _objectlifetime:

Object Lifetime
----------------------

A memory object, or simply object, is a region of a memory space that is
reserved by a memory allocation such as :ref:`alloca <i_alloca>`, heap
allocation calls, and global variable definitions.
Once it is allocated, the bytes stored in the region can only be read or written
through a pointer that is :ref:`based on <pointeraliasing>` the allocation
value.
If a pointer that is not based on the object tries to read or write to the
object, it is undefined behavior.

A lifetime of a memory object is a property that decides its accessibility.
Unless stated otherwise, a memory object is alive since its allocation, and
dead after its deallocation.
It is undefined behavior to access a memory object that isn't alive, but
operations that don't dereference it such as
:ref:`getelementptr <i_getelementptr>`, :ref:`ptrtoint <i_ptrtoint>` and
:ref:`icmp <i_icmp>` return a valid result.
This explains code motion of these instructions across operations that
impact the object's lifetime.
A stack object's lifetime can be explicitly specified using
:ref:`llvm.lifetime.start <int_lifestart>` and
A lifetime of an :ref:`allocated object<allocatedobjects>` is a property that
decides its accessibility. Unless stated otherwise, an allocated object is alive
since its allocation, and dead after its deallocation. It is undefined behavior
to access an allocated object that isn't alive, but operations that don't
dereference it such as :ref:`getelementptr <i_getelementptr>`,
:ref:`ptrtoint <i_ptrtoint>` and :ref:`icmp <i_icmp>` return a valid result.
This explains code motion of these instructions across operations that impact
the object's lifetime. A stack object's lifetime can be explicitly specified
using :ref:`llvm.lifetime.start <int_lifestart>` and
:ref:`llvm.lifetime.end <int_lifeend>` intrinsic function calls.

.. _pointeraliasing:
Expand Down Expand Up @@ -4484,11 +4497,10 @@ Here are some examples of multidimensional arrays:

There is no restriction on indexing beyond the end of the array implied
by a static type (though there are restrictions on indexing beyond the
bounds of an allocated object in some cases). This means that
single-dimension 'variable sized array' addressing can be implemented in
LLVM with a zero length array type. An implementation of 'pascal style
arrays' in LLVM could use the type "``{ i32, [0 x float]}``", for
example.
bounds of an :ref:`allocated object<allocatedobjects>` in some cases). This
means that single-dimension 'variable sized array' addressing can be implemented
in LLVM with a zero length array type. An implementation of 'pascal style
arrays' in LLVM could use the type "``{ i32, [0 x float]}``", for example.

.. _t_struct:

Expand Down Expand Up @@ -11098,12 +11110,9 @@ operation (that is, the alignment of the memory address). It is the
responsibility of the code emitter to ensure that the alignment information is
correct. Overestimating the alignment results in undefined behavior.
Underestimating the alignment may produce less efficient code. An alignment of
1 is always safe. The maximum possible alignment is ``1 << 32``. An alignment
value higher than the size of the loaded type implies memory up to the
alignment value bytes can be safely loaded without trapping in the default
address space. Access of the high bytes can interfere with debugging tools, so
should not be accessed if the function has the ``sanitize_thread`` or
``sanitize_address`` attributes.
1 is always safe. The maximum possible alignment is ``1 << 32``. Access of the
high bytes can interfere with debugging tools, so should not be accessed if the
function has the ``sanitize_thread`` or ``sanitize_address`` attributes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"store" has similar language.

The language about sanitize_thread/sanitize_address isn't relevant if you're removing the allowance to load bytes past the end.

This was added in 7020f25 ; CC @arsenm .

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe @chandlerc requested the bit about the sanitizers


The alignment is only optional when parsing textual IR; for in-memory IR, it is
always present. An omitted ``align`` argument means that the operation has the
Expand Down Expand Up @@ -11720,9 +11729,8 @@ Note that ``getelementptr`` with all-zero indices is always considered to be
As a corollary, the only pointer in bounds of the null pointer in the default
address space is the null pointer itself.

These rules are based on the assumption that no allocated object may cross
the unsigned address space boundary, and no allocated object may be larger
than half the pointer index type space.
These rules are based on the assumption for
:ref:`allocated object<allocatedobjects>`.

If ``inbounds`` is present on a ``getelementptr`` instruction, the ``nusw``
attribute will be automatically set as well. For this reason, the ``nusw``
Expand Down Expand Up @@ -26318,7 +26326,7 @@ Memory Use Markers
------------------

This class of intrinsics provides information about the
:ref:`lifetime of memory objects <objectlifetime>` and ranges where variables
:ref:`lifetime of allocated objects <objectlifetime>` and ranges where variables
are immutable.

.. _int_lifestart:
Expand Down Expand Up @@ -26386,8 +26394,8 @@ Syntax:
Overview:
"""""""""

The '``llvm.lifetime.end``' intrinsic specifies the end of a memory object's
lifetime.
The '``llvm.lifetime.end``' intrinsic specifies the end of a
:ref:`allocated object's lifetime<objectlifetime>`.

Arguments:
""""""""""
Expand Down Expand Up @@ -26417,7 +26425,8 @@ with ``poison``.

Syntax:
"""""""
This is an overloaded intrinsic. The memory object can belong to any address space.
This is an overloaded intrinsic. The :ref:`allocated object<allocatedobjects>`
can belong to any address space.

::

Expand All @@ -26427,7 +26436,7 @@ Overview:
"""""""""

The '``llvm.invariant.start``' intrinsic specifies that the contents of
a memory object will not change.
an :ref:`allocated object<allocatedobjects>` will not change.

Arguments:
""""""""""
Expand All @@ -26448,7 +26457,8 @@ unchanging.

Syntax:
"""""""
This is an overloaded intrinsic. The memory object can belong to any address space.
This is an overloaded intrinsic. The :ref:`allocated object<allocatedobjects>`
can belong to any address space.

::

Expand All @@ -26458,7 +26468,7 @@ Overview:
"""""""""

The '``llvm.invariant.end``' intrinsic specifies that the contents of a
memory object are mutable.
:ref:`allocated object<allocatedobjects>` are mutable.

Arguments:
""""""""""
Expand All @@ -26478,9 +26488,9 @@ This intrinsic indicates that the memory is mutable again.

Syntax:
"""""""
This is an overloaded intrinsic. The memory object can belong to any address
space. The returned pointer must belong to the same address space as the
argument.
This is an overloaded intrinsic. The :ref:`allocated object<allocatedobjects>`
can belong to any address space. The returned pointer must belong to the same
address space as the argument.

::

Expand Down Expand Up @@ -26514,9 +26524,9 @@ It does not read any accessible memory and the execution can be speculated.

Syntax:
"""""""
This is an overloaded intrinsic. The memory object can belong to any address
space. The returned pointer must belong to the same address space as the
argument.
This is an overloaded intrinsic. The :ref:`allocated object<allocatedobjects>`
can belong to any address space. The returned pointer must belong to the same
address space as the argument.

::

Expand Down