2
0
mirror of https://github.com/boostorg/context.git synced 2026-01-19 04:02:17 +00:00
Files
context/doc/stack.qbk
PeterYang12 3484575878 Add shadow stack support for fcontext
Shadow stack is part of Intel's Control-Flow Enforcement Technology.

    Whenever a function is called, the return address is pushed onto both
    the regular stack and the shadow stack. When that function returns, the
    return addresses are popped off both stacks and compared; if they fail
    to match, #CP raised.

    Backport this commit from https://github.com/php/php-src/pull/9283
    With this commit, we create shadow stack with syscall map_shadow_stack
    (no.451) for each fiber context and switch the shadow stack accordingly
    during fcontext switch.

Signed-off-by: PeterYang12 <yuhan.yang@intel.com>
Signed-off-by: chen-hu-97 <hu1.chen@intel.com>
2022-09-29 22:58:22 -07:00

385 lines
13 KiB
Plaintext

[/
Copyright Oliver Kowalke 2014.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt
]
[#stack]
[section:stack Stack allocation]
The memory used by the stack is allocated/deallocated via a __stack_allocator__
which is required to model a __stack_allocator_concept__.
[heading __stack_allocator_concept__]
A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements
shown in the following table, in which `a` is an object of a
__stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::size_t`:
[table
[[expression][return type][notes]]
[
[`a(size)`]
[]
[creates a stack allocator]
]
[
[`a.allocate()`]
[`stack_context`]
[creates a stack]
]
[
[`a.deallocate( sctx)`]
[`void`]
[deallocates the stack created by `a.allocate()`]
]
]
[important The implementation of `allocate()` might include logic to protect
against exceeding the context's available stack size rather than leaving it as
undefined behaviour.]
[important Calling `deallocate()` with a `stack_context` not set by `allocate()`
results in undefined behaviour.]
[note Depending on the architecture `allocate()` stores an address from the
top of the stack (growing downwards) or the bottom of the stack (growing
upwards).]
[section:protected_fixedsize Class ['protected_fixedsize]]
__boost_context__ provides the class __protected_fixedsize__ which models
the __stack_allocator_concept__.
It appends a guard page at the end of each stack to protect against exceeding
the stack. If the guard page is accessed (read or write operation) a
segmentation fault/access violation is generated by the operating system.
[important Using __protected_fixedsize__ is expensive. That is, launching a
new coroutine with a new stack is expensive; the allocated stack is just as
efficient to use as any other stack.]
[note The appended `guard page` is [*not] mapped to physical memory, only
virtual addresses are used.]
#include <boost/context/protected_fixedsize.hpp>
template< typename traitsT >
struct basic_protected_fixedsize {
typedef traitT traits_type;
basic_protected_fixesize(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_protected_fixedsize< stack_traits > protected_fixedsize
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer
to the stack and its actual size in `sctx`. Depending
on the architecture (the stack grows downwards/upwards) the stored address is
the highest/lowest address of the stack.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[section:pooled_fixedsize Class ['pooled_fixedsize_stack]]
__boost_context__ provides the class __pooled_fixedsize__ which models
the __stack_allocator_concept__.
In contrast to __protected_fixedsize__ it does not append a guard page at the
end of each stack. The memory is managed internally by
[@http://www.boost.org/doc/libs/release/libs/pool/doc/html/boost/pool.html `boost::pool<>`].
#include <boost/context/pooled_fixedsize_stack.hpp>
template< typename traitsT >
struct basic_pooled_fixedsize_stack {
typedef traitT traits_type;
basic_pooled_fixedsize_stack(std::size_t stack_size = traits_type::default_size(), std::size_t next_size = 32, std::size_t max_size = 0);
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_pooled_fixedsize_stack< stack_traits > pooled_fixedsize_stack;
[heading `basic_pooled_fixedsize_stack(std::size_t stack_size, std::size_t next_size, std::size_t max_size)`]
[variablelist
[[Preconditions:] [`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= stack_size)`
and `0 < nest_size`.]]
[[Effects:] [Allocates memory of at least `stack_size` Bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack. Argument `next_size` determines the number of stacks to
request from the system the first time that `*this` needs to allocate system
memory. The third argument `max_size` controls how many memory might be
allocated for stacks - a value of zero means no uper limit.]]
]
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= stack_size)`.]]
[[Effects:] [Allocates memory of at least `stack_size` Bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid,
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[section:fixedsize Class ['fixedsize_stack]]
__boost_context__ provides the class __fixedsize__ which models
the __stack_allocator_concept__.
In contrast to __protected_fixedsize__ it does not append a guard page at the
end of each stack. The memory is simply managed by `std::malloc()` and
`std::free()`.
#include <boost/context/fixedsize_stack.hpp>
template< typename traitsT >
struct basic_fixedsize_stack {
typedef traitT traits_type;
basic_fixesize_stack(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_fixedsize_stack< stack_traits > fixedsize_stack;
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[#segmented]
[section:segmented Class ['segmented_stack]]
__boost_context__ supports usage of a __segmented__, e. g. the size of
the stack grows on demand. The coroutine is created with a minimal stack size
and will be increased as required.
Class __segmented__ models the __stack_allocator_concept__.
In contrast to __protected_fixedsize__ and __fixedsize__ it creates a
stack which grows on demand.
[note Segmented stacks are currently only supported by [*gcc] from version
[*4.7] [*clang] from version [*3.4] onwards. In order to use a
__segmented_stack__ __boost_context__ must be built with
property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] and
applying `BOOST_USE_SEGMENTED_STACKS` at b2/bjam command line.]
[note Segmented stacks can only be used with __cc__ (using
[link implementation __ucontext__])].
#include <boost/context/segmented_stack.hpp>
template< typename traitsT >
struct basic_segmented_stack {
typedef traitT traits_type;
basic_segmented_stack(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_segmented_stack< stack_traits > segmented_stack;
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [Allocates memory of at least `size` Bytes and stores a pointer to
the stack and its actual size in `sctx`. Depending on the architecture (the
stack grows downwards/upwards) the stored address is the highest/lowest
address of the stack.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[note If the library is compiled for segmented stacks, __segmented_stack__ is the only
available stack allocator.]
[endsect]
[section:stack_traits Class ['stack_traits]]
['stack_traits] models a __stack_traits__ providing a way to access certain
properites defined by the enironment. Stack allocators use __stack_traits__ to
allocate stacks.
#include <boost/context/stack_traits.hpp>
struct stack_traits {
static bool is_unbounded() noexcept;
static std::size_t page_size() noexcept;
static std::size_t default_size() noexcept;
static std::size_t minimum_size() noexcept;
static std::size_t maximum_size() noexcept;
}
[heading `static bool is_unbounded()`]
[variablelist
[[Returns:] [Returns `true` if the environment defines no limit for the size of
a stack.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t page_size()`]
[variablelist
[[Returns:] [Returns the page size in bytes.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t default_size()`]
[variablelist
[[Returns:] [Returns a default stack size, which may be platform specific.
If the stack is unbounded then the present implementation returns the maximum of
`64 kB` and `minimum_size()`.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t minimum_size()`]
[variablelist
[[Returns:] [Returns the minimum size in bytes of stack defined by the
environment (Win32 4kB/Win64 8kB, defined by rlimit on POSIX).]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t maximum_size()`]
[variablelist
[[Preconditions:] [`is_unbounded()` returns `false`.]]
[[Returns:] [Returns the maximum size in bytes of stack defined by the
environment.]]
[[Throws:] [Nothing.]]
]
[endsect]
[section:stack_context Class ['stack_context]]
__boost_context__ provides the class __stack_context__ which will contain
the stack pointer and the size of the stack.
In case of a __segmented__, __stack_context__ contains some extra control
structures.
struct stack_context {
void * sp;
std::size_t size;
// might contain additional control structures
// for segmented stacks
}
[heading `void * sp`]
[variablelist
[[Value:] [Pointer to the beginning of the stack.]]
]
[heading `std::size_t size`]
[variablelist
[[Value:] [Actual size of the stack.]]
]
[endsect]
[section:valgrind Support for valgrind]
Running programs that switch stacks under valgrind causes problems.
Property (b2 command-line) `valgrind=on` let valgrind treat the memory regions
as stack space which suppresses the errors. Users must define `BOOST_USE_VALGRIND`
before including any Boost.Context headers when linking against Boost binaries
compiled with `valgrind=on`.
[endsect]
[section:sanitizers Support for sanitizers]
Sanitizers (GCC/Clang) are confused by the stack switches.
The library is required to be compiled with property (b2 command-line)
`context-impl=ucontext` and compilers santizer options.
Users must define `BOOST_USE_ASAN` before including any Boost.Context headers
when linking against Boost binaries.
[endsect]
[section:stack_protect Support for stack protection]
Compiler switch `-fstack-protector` changes the default context switching logic.
Users must define `BOOST_CONTEXT_TLS_STACK_PROTECTOR` before including any
Boost.Context headers if stack protection is enabled.
[endsect]
[section:shadow_stack Support for shadow stack protection]
Shadow stack is part of Intel's Control-Flow Enforcement Technology. Users must
check if syscall 'map_shadow_stack' exists, which is no.451 and then define
`SHADOW_STACK_SYSCALL` before including any Boost.Context headers
if shadow stack protection is enabled.
[endsect]
[endsect]