Skip to content

Commit b1f61bb

Browse files
authored
Use static analyzer to check pinning state (#49)
This PR introduces some preliminary work to use GCChecker to check pinning state. The clangsa tests pass. But the checker found issues with the current runtime code base. Future PRs will fix that.
1 parent 0ac54e6 commit b1f61bb

File tree

9 files changed

+656
-130
lines changed

9 files changed

+656
-130
lines changed

src/clangsa/GCChecker.cpp

Lines changed: 525 additions & 103 deletions
Large diffs are not rendered by default.

src/julia.h

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,49 @@
55

66
#ifdef LIBRARY_EXPORTS
77

8+
// Generated file, needs to be searched in include paths so that the builddir
9+
// retains priority
10+
#include <jl_internal_funcs.inc>
11+
#undef jl_setjmp
12+
#undef jl_longjmp
13+
#undef jl_egal
14+
#endif
15+
16+
#include "julia_fasttls.h"
17+
#include "libsupport.h"
18+
#include <stdint.h>
19+
#include <string.h>
20+
21+
#include "htable.h"
22+
#include "arraylist.h"
23+
#include "analyzer_annotations.h"
24+
825
#ifdef __cplusplus
926
extern "C" {
1027
#endif
1128

1229
extern int mmtk_object_is_managed_by_mmtk(void* addr);
1330
extern unsigned char mmtk_pin_object(void* obj);
31+
extern unsigned char mmtk_unpin_object(void* obj);
32+
#ifdef __clang_gcanalyzer__
33+
extern void PTRHASH_PIN(void* key) JL_NOTSAFEPOINT;
34+
extern void PTRHASH_UNPIN(void* key) JL_NOTSAFEPOINT;
35+
#else
1436
// FIXME: Pinning objects that get hashed in the ptrhash table
1537
// until we implement address space hashing.
1638
#ifdef MMTK_GC
1739
#define PTRHASH_PIN(key) mmtk_pin_object(key);
40+
#define PTRHASH_UNPIN(key) mmtk_unpin_object(key);
1841
#else
1942
#define PTRHASH_PIN(key)
43+
#define PTRHASH_UNPIN(key)
44+
#endif
2045
#endif
2146

2247
#ifdef __cplusplus
2348
}
2449
#endif
2550

26-
// Generated file, needs to be searched in include paths so that the builddir
27-
// retains priority
28-
#include <jl_internal_funcs.inc>
29-
#undef jl_setjmp
30-
#undef jl_longjmp
31-
#undef jl_egal
32-
#endif
33-
34-
#include "julia_fasttls.h"
35-
#include "libsupport.h"
36-
#include <stdint.h>
37-
#include <string.h>
38-
39-
#include "htable.h"
40-
#include "arraylist.h"
41-
#include "analyzer_annotations.h"
42-
4351
#include <setjmp.h>
4452
#ifndef _OS_WINDOWS_
4553
# define jl_jmp_buf sigjmp_buf
@@ -907,6 +915,24 @@ extern void _JL_GC_PUSHARGS(jl_value_t **, size_t) JL_NOTSAFEPOINT;
907915

908916
extern void JL_GC_POP() JL_NOTSAFEPOINT;
909917

918+
#ifdef MMTK_GC
919+
extern void JL_GC_PUSH1_NO_TPIN(void *) JL_NOTSAFEPOINT;
920+
extern void JL_GC_PUSH2_NO_TPIN(void *, void *) JL_NOTSAFEPOINT;
921+
extern void JL_GC_PUSH3_NO_TPIN(void *, void *, void *) JL_NOTSAFEPOINT;
922+
extern void JL_GC_PUSH4_NO_TPIN(void *, void *, void *, void *) JL_NOTSAFEPOINT;
923+
extern void JL_GC_PUSH5_NO_TPIN(void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
924+
extern void JL_GC_PUSH7_NO_TPIN(void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
925+
extern void JL_GC_PUSH8_NO_TPIN(void *, void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
926+
extern void _JL_GC_PUSHARGS_NO_TPIN(jl_value_t **, size_t) JL_NOTSAFEPOINT;
927+
// This is necessary, because otherwise the analyzer considers this undefined
928+
// behavior and terminates the exploration
929+
#define JL_GC_PUSHARGS_NO_TPIN(rts_var, n) \
930+
rts_var = (jl_value_t **)alloca(sizeof(void*) * (n)); \
931+
memset(rts_var, 0, sizeof(void*) * (n)); \
932+
_JL_GC_PUSHARGS_NO_TPIN(rts_var, (n));
933+
934+
#endif
935+
910936
#else
911937

912938
#define JL_GC_PUSH1(arg1) \

src/julia_internal.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,21 @@ extern "C" {
99

1010
extern int mmtk_object_is_managed_by_mmtk(void* addr);
1111
extern unsigned char mmtk_pin_object(void* obj);
12+
extern unsigned char mmtk_unpin_object(void* obj);
13+
14+
#ifdef __clang_gcanalyzer__
15+
extern void PTR_PIN(void* key) JL_NOTSAFEPOINT;
16+
extern void PTR_UNPIN(void* key) JL_NOTSAFEPOINT;
17+
#else
18+
1219
#ifdef MMTK_GC
1320
#define PTR_PIN(key) mmtk_pin_object(key);
21+
#define PTR_UNPIN(key) mmtk_unpin_object(key);
1422
#else
1523
#define PTR_PIN(key)
24+
#define PTR_UNPIN(key)
25+
#endif
26+
1627
#endif
1728

1829
#ifdef __cplusplus

src/support/analyzer_annotations.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
#define JL_PROPAGATES_ROOT __attribute__((annotate("julia_propagates_root")))
1414
#define JL_NOTSAFEPOINT __attribute__((annotate("julia_not_safepoint")))
1515
#define JL_MAYBE_UNROOTED __attribute__((annotate("julia_maybe_unrooted")))
16+
#define JL_MAYBE_UNPINNED __attribute__((annotate("julia_maybe_unpinned")))
1617
#define JL_GLOBALLY_ROOTED __attribute__((annotate("julia_globally_rooted")))
18+
#define JL_GLOBALLY_PINNED __attribute__((annotate("julia_globally_pinned")))
19+
#define JL_GLOBALLY_TPINNED __attribute__((annotate("julia_globally_tpinned")))
1720
#define JL_ROOTING_ARGUMENT __attribute__((annotate("julia_rooting_argument")))
1821
#define JL_ROOTED_ARGUMENT __attribute__((annotate("julia_rooted_argument")))
1922
#define JL_GC_DISABLED __attribute__((annotate("julia_gc_disabled")))
@@ -34,7 +37,15 @@ extern "C" {
3437
#define JL_PROPAGATES_ROOT
3538
#define JL_NOTSAFEPOINT
3639
#define JL_MAYBE_UNROOTED
40+
#define JL_MAYBE_UNPINNED
41+
// The runtime may mark any object that is reachable from a global root as globally rooted.
42+
// So JL_GLOBALLY_ROOTED does not need to an actual root. Thus we don't know anything
43+
// about pining state.
3744
#define JL_GLOBALLY_ROOTED
45+
// A root may be pinned. But any object reachable from the root is not pinned.
46+
#define JL_GLOBALLY_PINNED
47+
// A root may be transitively pinned, and any object reachable from the root is transitively pinned.
48+
#define JL_GLOBALLY_TPINNED
3849
#define JL_ROOTING_ARGUMENT
3950
#define JL_ROOTED_ARGUMENT
4051
#define JL_GC_DISABLED

test/clangsa/GCPushPop.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -Xclang -verify -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -x c++ %s
3+
// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -Xclang -verify -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -x c++ %s
44

55
#include "julia.h"
66
#include <string>

test/clangsa/ImplicitAtomicsTest.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
// RUN: clang-tidy %s --checks=-*,concurrency-implicit-atomics -load libImplicitAtomicsPlugin%shlibext -- -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -x c -std=c11 | FileCheck --check-prefixes=CHECK,CHECK-C %s
4-
// RUN: clang-tidy %s --checks=-*,concurrency-implicit-atomics -load libImplicitAtomicsPlugin%shlibext -- -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} ${CXXFLAGS} -x c++ -std=c++11 | FileCheck --check-prefixes=CHECK,CHECK-CXX %s
3+
// RUN: clang-tidy %s --checks=-*,concurrency-implicit-atomics -load libImplicitAtomicsPlugin%shlibext -- -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} -x c -std=c11 | FileCheck --check-prefixes=CHECK,CHECK-C %s
4+
// RUN: clang-tidy %s --checks=-*,concurrency-implicit-atomics -load libImplicitAtomicsPlugin%shlibext -- -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} ${CXXFLAGS} -x c++ -std=c++11 | FileCheck --check-prefixes=CHECK,CHECK-CXX %s
55

66
#include "julia_atomics.h"
77

test/clangsa/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ TESTS = $(patsubst $(SRCDIR)/%,%,$(wildcard $(SRCDIR)/*.c) $(wildcard $(SRCDIR)/
1313
PATH=$(build_bindir):$(build_depsbindir):$$PATH \
1414
LD_LIBRARY_PATH="${build_libdir}:$$LD_LIBRARY_PATH" \
1515
CLANGSA_FLAGS="${CLANGSA_FLAGS}" \
16-
CLANGSACXX_FLAGS="${CLANGSACXX_FLAGS}" \
16+
CLANGSA_CXXFLAGS="${CLANGSA_CXXFLAGS}" \
1717
CPPFLAGS_FLAGS="${CPPFLAGS_FLAGS}" \
1818
CFLAGS_FLAGS="${CFLAGS_FLAGS}" \
1919
CXXFLAGS_FLAGS="${CXXFLAGS_FLAGS}" \

test/clangsa/MissingPinning.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -v -x c %s
4+
5+
#include "julia.h"
6+
#include "julia_internal.h"
7+
8+
extern void look_at_value(jl_value_t *v);
9+
10+
void unpinned_argument() {
11+
jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}}
12+
JL_GC_PROMISE_ROOTED(val); // expected-note{{Value was rooted here}}
13+
look_at_value((jl_value_t*) val); // expected-warning{{Passing non-pinned value as argument to function that may GC}}
14+
// expected-note@-1{{Passing non-pinned value as argument to function that may GC}}
15+
}
16+
17+
void pinned_argument() {
18+
jl_svec_t *val = jl_svec1(NULL);
19+
JL_GC_PROMISE_ROOTED(val);
20+
PTR_PIN(val);
21+
look_at_value((jl_value_t*) val);
22+
PTR_UNPIN(val);
23+
}
24+
25+
void missing_pin_before_safepoint() {
26+
jl_svec_t *val = jl_svec1(NULL); // expected-note{{Started tracking value here}}
27+
JL_GC_PROMISE_ROOTED(val); // expected-note{{Value was rooted here}}
28+
jl_gc_safepoint();
29+
look_at_value((jl_value_t*) val); // expected-warning{{Argument value may have been moved}}
30+
// expected-note@-1{{Argument value may have been moved}}
31+
}
32+
33+
void proper_pin_before_safepoint() {
34+
jl_svec_t *val = jl_svec1(NULL);
35+
JL_GC_PROMISE_ROOTED(val);
36+
PTR_PIN(val);
37+
jl_gc_safepoint();
38+
look_at_value((jl_value_t*) val);
39+
PTR_UNPIN(val);
40+
}
41+
42+
void push_tpin_value() {
43+
jl_svec_t *val = jl_svec1(NULL);
44+
JL_GC_PUSH1(&val);
45+
jl_gc_safepoint();
46+
look_at_value((jl_value_t*) val);
47+
JL_GC_POP();
48+
}
49+
50+
void push_no_tpin_value() {
51+
jl_svec_t *val = jl_svec1(NULL);
52+
JL_GC_PUSH1_NO_TPIN(&val);
53+
jl_gc_safepoint();
54+
look_at_value((jl_value_t*) val);
55+
JL_GC_POP();
56+
}

test/clangsa/MissingRoots.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s
3+
// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CLANGSA_CXXFLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s
44

55
#include "julia.h"
66
#include "julia_internal.h"
77

88
extern void look_at_value(jl_value_t *v);
9-
extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED);
9+
extern void process_unrooted(jl_value_t *maybe_unrooted JL_MAYBE_UNROOTED JL_MAYBE_UNPINNED);
1010
extern void jl_gc_safepoint();
1111

1212
void unrooted_argument() {
@@ -167,7 +167,7 @@ int unrooted() {
167167
// expected-note@-1{{Trying to access value which may have been GCed}}
168168
}
169169

170-
extern jl_value_t *global_value JL_GLOBALLY_ROOTED;
170+
extern jl_value_t *global_value JL_GLOBALLY_ROOTED JL_GLOBALLY_PINNED;
171171
void globally_rooted() {
172172
jl_value_t *val = global_value;
173173
jl_gc_safepoint();
@@ -239,7 +239,7 @@ void pushargs_as_args()
239239
JL_GC_POP();
240240
}
241241

242-
static jl_typemap_entry_t *this_call_cache[10] JL_GLOBALLY_ROOTED;
242+
static jl_typemap_entry_t *this_call_cache[10] JL_GLOBALLY_ROOTED JL_GLOBALLY_TPINNED;
243243
void global_array2() {
244244
jl_value_t *val = NULL;
245245
JL_GC_PUSH1(&val);
@@ -296,12 +296,12 @@ void tparam0(jl_value_t *atype) {
296296
look_at_value(jl_tparam0(atype));
297297
}
298298

299-
extern jl_value_t *global_atype JL_GLOBALLY_ROOTED;
299+
extern jl_value_t *global_atype JL_GLOBALLY_ROOTED JL_GLOBALLY_TPINNED;
300300
void tparam0_global() {
301301
look_at_value(jl_tparam0(global_atype));
302302
}
303303

304-
static jl_value_t *some_global JL_GLOBALLY_ROOTED;
304+
static jl_value_t *some_global JL_GLOBALLY_ROOTED JL_GLOBALLY_PINNED;
305305
void global_copy() {
306306
jl_value_t *local = NULL;
307307
jl_gc_safepoint();

0 commit comments

Comments
 (0)