Skip to content

GC analyzer does not spot safepoint issue #33501

Closed

Description

The following piece of C code, reduced from gc.c (ab was gc_cblist_notify_external_free, b was gc_cblist_notify_external_free), confuses the GC analyzer based on the definition of foobar. If it's a function declaration, void foobar() { }, the analyzer doesn't spot the potential safepoint by calling the ab function pointer from JL_NOTSAFEPOINT-annotated f:

#include <stdint.h>
#include <string.h>
#include "analyzer_annotations.h"
typedef struct {
    size_t ae
} af;
typedef struct ag *ah;
typedef struct {
    af ai
} aj;
struct ag {
    aj ak
};
ah *z;
ad, a, e;
typedef struct {
    uint64_t t
} u;
typedef struct {
    uint32_t v[8];
    int aa
} w;
u x;
w y;
typedef (*ab)();
#define b(c, d, ac)                                                            \
    for (; a;)                                                                 \
    ((c)a)()
f() JL_NOTSAFEPOINT
{
    b(ab, , );
}
h()
{
    f();
}
j()
{
    for (unsigned k = 0; k <= y.aa; k++) {
        uint32_t l = y.v[k];
        for (; l; l >>= 1)
            if (m())
                y.v[k] = 0;
        if (y.v[k])
            ;
    }
}
n()
{
    h();
}
o()
{
    size_t p = ad;
    for (int q = 0; q < p; q++)
        j();
}
r()
{
    int g = n();
    foobar();
    o();
    for (int q = 0; q < ad; q++) {
        ah s = z[q];
        if (!g)
            for (int i = 0; i < s; i++)
                for (; i < s->ak.ai.ae; i++)
                    ;
    }
    if (e)
        ;
    e = x.t;
}
$ cat reduce_decl.c
void foobar() { }

$ clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin.so -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -include reduce_decl.c reduce.c

If you instead define only a prototype, void foobar();, the analyzer does spot the issue:

$ cat reduce_proto.c
void foobar();

$ clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin.so -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -include reduce_proto.c reduce.c

reduce.c:31:5: warning: Calling potential safepoint from function annotated JL_NOTSAFEPOINT
    b(ab, , );
    ^
reduce.c:28:5: note: expanded from macro 'b'
    ((c)a)()
    ^
reduce.c:60:13: note: Calling 'n'
    int g = n();
            ^~~
reduce.c:50:5: note: Calling 'h'
    h();
    ^~~
reduce.c:35:5: note: Calling 'f'
    f();
    ^~~
reduce.c:31:5: note: Loop condition is true.  Entering loop body
    b(ab, , );
    ^
reduce.c:27:5: note: expanded from macro 'b'
    for (; a;)                                                                 \
    ^
reduce.c:31:5: note: Calling potential safepoint from function annotated JL_NOTSAFEPOINT
    b(ab, , );
    ^~~~~~~~~
reduce.c:28:5: note: expanded from macro 'b'
    ((c)a)()
    ^~~~~~~~
1 warning generated.

Removing seemingly trivial stuff that would never affect the analysis makes both cases detect the issue:

$ diff -u /tmp/reduce.c reduce.c                                                                                                                                                                                                     *[master] 
--- /tmp/reduce.c       2019-10-08 15:10:38.394677577 +0200
+++ reduce.c    2019-10-08 15:10:39.581379150 +0200
@@ -64,7 +64,7 @@
         ah s = z[q];
         if (!g)
             for (int i = 0; i < s; i++)
-                for (; i < s->ak.ai.ae; i++)
+                for (; i < s->ak.ai.ae; i--)
                     ;
     }
     if (e)

$ clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin.so -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -include reduce_decl.c reduce.c

reduce.c:31:5: warning: Calling potential safepoint from function annotated JL_NOTSAFEPOINT
    b(ab, , );
    ^

Furthermore, defining foobar through -include reduce_proto.c or -include reduce_decl.c seems necessary to reproduce the issue. This wasn't the case before reduction, so it doesn't look like a simple logic issue.

Tested with LLVM 6.0.1 from BinaryBuilder on Julia master. This issue is the reason that the llvmpasses buildbot fails over at #33467. Thoughts, @Keno?

The real fix would be to (document and) annotate the callback function pointer, i.e. typedef (*ab)() JL_NOTSAFEPOINT, which silences the error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions