c++assemblybooststackboost-context

Boost Context implementation


I'm reading the implementation of boost fcontext.

The function prototype of make_fcontext is typedef void* fcontext_t; fcontext_t BOOST_CONTEXT_CALLDECL make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) );

The first argument is top of context-stack, an example from boost document is as follows:

// context-function
void f(intptr);


// creates a new stack
std::size_t size = 8192;
void* sp(std::malloc(size));

// context fc uses f() as context function
// fcontext_t is placed on top of context stack
// a pointer to fcontext_t is returned
fcontext_t fc(make_fcontext(sp,size,f));

When I'm reading the implementation of make_context in i386_elf, the implementation always decrease the sp, it will make the context store in the memory before sp, which is out of memory from malloc. Could it overwrite the memroy not belong to the coroutine?

/* first arg of make_fcontext() == top of context-stack */
movl  0x4(%esp), %eax

/*decrease the adress of sp here*/
/* reserve space for first argument of context-function
   rax might already point to a 16byte border */
leal  -0x8(%eax), %eax

/* shift address in EAX to lower 16 byte boundary */
andl  $-16, %eax

/* reserve space for context-data on context-stack */
/* size for fc_mxcsr .. EIP + return-address for context-function */
/* on context-function entry: (ESP -0x4) % 8 == 0 */
leal  -0x20(%eax), %eax

/* third arg of make_fcontext() == address of context-function */
movl  0xc(%esp), %edx
movl  %edx, 0x18(%eax)

/* save MMX control- and status-word */
stmxcsr  (%eax)
/* save x87 control-word */
fnstcw  0x4(%eax)

Solution

  • Depending on your CPU architecture, the stack might grow upwards (towards higher addresses) or downwards (towards lower addresses, as is the case on x86). This is generally hard-coded in the instruction set by the way the push and pop instructions modify the stack pointer. For example, the x86 push instruction subtracts from [er]?sp.

    make_fcontext expects the stack pointer to have sufficient space in the architecture-specific direction needed by the platform. On x86, this means there must be available space before the pointer, not after. By passing the pointer you received from malloc directly, you are violating this contract.

    This is why the stack_allocator abstractions exists. They return pointers that point to the right end of the stack, depending on the architecture.

    (As a side note, I believe all architectures currently supported by Boost.Context have downwards-growing stacks.)