This document describes GC protection for on-stack objects safe against an
irregular stack layout such as stack-smashing protection performs.
Working on the previous stack protection strategy, following problem
occurs.(the explanation is based on
Anthy-dev 2273
by Jun Inoue, and assumes that the stack grows downward).
Take a look in following code fragment.
{
...
exp = "(number->string (* 32 1024))";
{
ScmObj stack_start;
ScmObj str_port;
ScmObj obj;
scm_gc_protect_stack(&stack_start);
str_port = scm_make_string_port(exp);
obj = scm_read(str_port);
obj = SCM_EVAL(obj, SCM_INTERACTION_ENV);
scm_gc_unprotect_stack(stack_start);
}
return SCM_STRING_STR(obj);
}
The previous strategy assumes that stack_start is certainly located at lower
than str_port and obj to cover them on the mark-and-sweep stage. But the
actual layout breaks the assumption as follows.
At the breakpoint immediately before scm_gc_protect_stack() of the code
compiled with gcc4 ((GCC) 4.0.1 20050617 (prerelease) (Debian 4.0.0-10)
(IA32)), actual stack layout had been reported as follows.
(gdb) p &stack_start
$6 = (ScmObj *) 0xbfffe7d8
(gdb) p &str_port
$7 = (ScmObj *) 0xbfffe7dc
(gdb) p $sp
$12 = (void *) 0xbfffe7d0
This result indicates the storage location order inversion:
expected: %esp < &str_port < &stack_start
actual: %esp < &stack_start < &str_port
So str_port is not protected in the case.
To accomplish it, a series of macros which turns a function into
stack-protected. See following steps to know how to use it.
1) Split the code fragment off into a function. No stack protection handling is
needed here.
static const char *
eval_str(const char *exp)
{
ScmObj str_port;
ScmObj obj;
str_port = scm_make_string_port(exp);
obj = scm_read(str_port);
obj = SCM_EVAL(obj, SCM_INTERACTION_ENV);
return SCM_STRING_STR(obj);
}
2) Call the function prepared in 1) with the macro SCM_GC_PROTECTED_CALL() as
follows. The macro handles all of the GC protection issues. Once the function
returned, the stack protection is disabled again. To protect another function
call, apply the macro to the call.
Use SCM_GC_PROTECTED_CALL_VOID() instead if the calee function returns
void.
{
const char *exp, *ret;
...
exp = "(number->string (* 32 1024))";
SCM_GC_PROTECTED_CALL(ret, const char *, eval_str, (exp));
return ret;
}