mirror of
https://github.com/boostorg/context.git
synced 2026-01-19 04:02:17 +00:00
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>
385 lines
13 KiB
Plaintext
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]
|