Skip to content
Merged
Show file tree
Hide file tree
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
30 changes: 27 additions & 3 deletions src/sentry_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,40 @@ sentry_set_thread_stack_guarantee(uint32_t stack_guarantee_in_bytes)
return 1;
}

// Resolves the handler stack size (in KiB) from the `SENTRY_HANDLER_STACK_SIZE`
// environment variable, falling back to the compile-time default. This is
// called from `DllMain` via `sentry__set_default_thread_stack_guarantee`, so
// it must avoid CRT (`getenv`, `strtod`, ...) and only use kernel32 exports.
static size_t
sentry__handler_stack_size_kib(void)
{
char buf[12];
DWORD n = GetEnvironmentVariableA(
"SENTRY_HANDLER_STACK_SIZE", buf, sizeof(buf));
if (n == 0 || n >= sizeof(buf)) {
return SENTRY_HANDLER_STACK_SIZE;
}
size_t val = 0;
for (DWORD i = 0; i < n; i++) {
if (buf[i] < '0' || buf[i] > '9') {
return SENTRY_HANDLER_STACK_SIZE;
}
val = val * 10 + (size_t)(buf[i] - '0');
}
return (val > 0 && val <= 0xFFFFFFFFu / 1024) ? val
: SENTRY_HANDLER_STACK_SIZE;
}
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

void
sentry__set_default_thread_stack_guarantee(void)
{
if (!g_kernel32_GetCurrentThreadStackLimits) {
return;
}

const size_t stack_size_kib = sentry__handler_stack_size_kib();
const unsigned long expected_stack_guarantee
= SENTRY_HANDLER_STACK_SIZE * 1024;
= (unsigned long)stack_size_kib * 1024;
DWORD thread_id = GetThreadId(GetCurrentThread());
ULONG_PTR high = 0;
ULONG_PTR low = 0;
Expand All @@ -284,8 +309,7 @@ sentry__set_default_thread_stack_guarantee(void)
SENTRY_WARNF(
"Cannot set handler stack guarantee of %zuKiB for thread %lu "
"(stack reserve: %zuKiB, expected factor: %zux, actual: %.2fx)",
(size_t)SENTRY_HANDLER_STACK_SIZE, thread_id,
thread_stack_reserve / 1024,
stack_size_kib, thread_id, thread_stack_reserve / 1024,
(size_t)SENTRY_THREAD_STACK_GUARANTEE_FACTOR,
expected_stack_reserve / (double)expected_stack_guarantee);
return;
Expand Down
15 changes: 8 additions & 7 deletions tests/test_integration_crashpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,30 +440,31 @@ def test_crashpad_dumping_crash(cmake, httpserver, run_args, build_args):


@pytest.mark.parametrize(
"build_args",
"stack_size",
[
({}), # uses default of 64KiB
None, # uses default of 64KiB
pytest.param(
{"SENTRY_HANDLER_STACK_SIZE": "16"},
"16",
marks=pytest.mark.skipif(
sys.platform != "win32",
reason="handler stack size parameterization tests stack guarantee on windows only",
),
),
pytest.param(
{"SENTRY_HANDLER_STACK_SIZE": "32"},
"32",
marks=pytest.mark.skipif(
sys.platform != "win32",
reason="handler stack size parameterization tests stack guarantee on windows only",
),
),
],
)
def test_crashpad_dumping_stack_overflow(cmake, httpserver, build_args):
build_args.update({"SENTRY_BACKEND": "crashpad"})
tmp_path = cmake(["sentry_example"], build_args)
def test_crashpad_dumping_stack_overflow(cmake, httpserver, stack_size):
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})

env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver))
if stack_size:
env["SENTRY_HANDLER_STACK_SIZE"] = stack_size
httpserver.expect_oneshot_request("/api/123456/minidump/").respond_with_data("OK")
httpserver.expect_request("/api/123456/envelope/").respond_with_data("OK")

Expand Down
32 changes: 19 additions & 13 deletions tests/test_integration_stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,13 @@ def test_multi_process(cmake):
assert len(runs) == 0


def run_stdout_for(backend, cmake, example_args, build_args=None):
def run_stdout_for(backend, cmake, example_args, build_args=None, env=None):
build_args = dict(build_args or {})
build_args.update({"SENTRY_BACKEND": backend, "SENTRY_TRANSPORT": "none"})

tmp_path = cmake(["sentry_example"], build_args)

run(tmp_path, "sentry_example", example_args, expect_failure=True)
run(tmp_path, "sentry_example", example_args, expect_failure=True, env=env)

return tmp_path, check_output(tmp_path, "sentry_example", ["stdout", "no-setup"])

Expand Down Expand Up @@ -243,22 +243,25 @@ def test_inproc_crash_stdout_before_send_and_on_crash(cmake):


@pytest.mark.parametrize(
"build_args",
"stack_size",
[
({}), # uses default of 64KiB
None, # uses default of 64KiB
# no test with 16KiB since `inproc` fails with that handler stack size
pytest.param(
{"SENTRY_HANDLER_STACK_SIZE": "32"},
"32",
marks=pytest.mark.skipif(
sys.platform != "win32",
reason="handler stack size parameterization tests stack guarantee on windows only",
),
),
],
)
def test_inproc_stack_overflow_stdout(cmake, build_args):
def test_inproc_stack_overflow_stdout(cmake, stack_size):
env = dict(os.environ)
if stack_size:
env["SENTRY_HANDLER_STACK_SIZE"] = stack_size
tmp_path, output = run_stdout_for(
"inproc", cmake, ["log", "attachment", "stack-overflow"], build_args
"inproc", cmake, ["log", "attachment", "stack-overflow"], env=env
)

envelope = Envelope.deserialize(output)
Expand Down Expand Up @@ -328,18 +331,18 @@ def test_breakpad_crash_stdout_before_send_and_on_crash(cmake):


@pytest.mark.parametrize(
"build_args",
"stack_size",
[
({}), # uses default of 64KiB
None, # uses default of 64KiB
pytest.param(
{"SENTRY_HANDLER_STACK_SIZE": "16"},
"16",
marks=pytest.mark.skipif(
sys.platform != "win32",
reason="handler stack size parameterization tests stack guarantee on windows only",
),
),
pytest.param(
{"SENTRY_HANDLER_STACK_SIZE": "32"},
"32",
marks=pytest.mark.skipif(
sys.platform != "win32",
reason="handler stack size parameterization tests stack guarantee on windows only",
Expand All @@ -348,9 +351,12 @@ def test_breakpad_crash_stdout_before_send_and_on_crash(cmake):
],
)
@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend")
def test_breakpad_stack_overflow_stdout(cmake, build_args):
def test_breakpad_stack_overflow_stdout(cmake, stack_size):
env = dict(os.environ)
if stack_size:
env["SENTRY_HANDLER_STACK_SIZE"] = stack_size
tmp_path, output = run_stdout_for(
"breakpad", cmake, ["attachment", "stack-overflow"], build_args
"breakpad", cmake, ["attachment", "stack-overflow"], env=env
)

envelope = Envelope.deserialize(output)
Expand Down
Loading