Skip to content

Commit

Permalink
Avoid segfault when ptls->safepoint is NULL
Browse files Browse the repository at this point in the history
There are cases (eg: when __gmpz_init is called from python via PyCall in a
secondary thread) where ptls->safepoint is NULL when maybe_collect is called,
and *ptls->safepoint causes a NULL pointer dereference and a segfault.  This
change fixes that case.

Included test/gmp-thread.jl that will reproduce this issue.

See JuliaLang#2720 for backtrace and more details
  • Loading branch information
bluesmoon committed May 8, 2018
1 parent 989de79 commit dc582d1
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
21 changes: 15 additions & 6 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2744,23 +2744,29 @@ void jl_gc_init(void)
JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz)
{
jl_ptls_t ptls = jl_get_ptls_states();
maybe_collect(ptls);
if (ptls && ptls->safepoint) {
maybe_collect(ptls);
}
gc_num.allocd += sz;
gc_num.malloc++;
void *b = malloc(sz);
if (b == NULL)
// If ptls->safepoint is NULL, then let the caller deal with the failed malloc
if (b == NULL && ptls && ptls->safepoint)
jl_throw(jl_memory_exception);
return b;
}

JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz)
{
jl_ptls_t ptls = jl_get_ptls_states();
maybe_collect(ptls);
if (ptls && ptls->safepoint) {
maybe_collect(ptls);
}
gc_num.allocd += nm*sz;
gc_num.malloc++;
void *b = calloc(nm, sz);
if (b == NULL)
// If ptls->safepoint is NULL, then let the caller deal with the failed calloc
if (b == NULL && ptls && ptls->safepoint)
jl_throw(jl_memory_exception);
return b;
}
Expand All @@ -2781,14 +2787,17 @@ JL_DLLEXPORT void jl_gc_counted_free(void *p, size_t sz)
JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size_t sz)
{
jl_ptls_t ptls = jl_get_ptls_states();
maybe_collect(ptls);
if (ptls && ptls->safepoint) {
maybe_collect(ptls);
}
if (sz < old)
gc_num.freed += (old - sz);
else
gc_num.allocd += (sz - old);
gc_num.realloc++;
void *b = realloc(p, sz);
if (b == NULL)
// If ptls->safepoint is NULL, then let the caller deal with the failed realloc
if (b == NULL && ptls && ptls->safepoint)
jl_throw(jl_memory_exception);
return b;
}
Expand Down
2 changes: 1 addition & 1 deletion src/julia_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ JL_DLLEXPORT void (jl_cpu_wake)(void);

// gc safepoint and gc states
// This triggers a SegFault when we are in GC
// Assign it to a variable to make sure the compiler emit the load
// Assign it to a variable to make sure the compiler emits the load
// and to avoid Clang warning for -Wunused-volatile-lvalue
#define jl_gc_safepoint_(ptls) do { \
jl_signal_fence(); \
Expand Down
9 changes: 9 additions & 0 deletions test/gmp-thread.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

x = BigInt()

# This will cause a SIGSEGV prior to https://github.com/JuliaLang/julia/pull/27020
ret = @threadcall((:__gmpz_init, :libgmp), Void, (Ptr{BigInt}, ), Ref(x))

# Not really needed, as long as it doesn't segfault, we're ok
@test typeof(ret) == Void

0 comments on commit dc582d1

Please sign in to comment.