[cfe-dev] Buildling with/without AddressSanitizer causes divergent execution behaviour

Dan Liew via cfe-dev cfe-dev at lists.llvm.org
Thu Feb 11 05:53:03 PST 2016


Hi Kostya & Yury,

Thanks for the advice.

On 9 February 2016 at 22:48, Kostya Serebryany <kcc at google.com> wrote:
> Hi Dan,
>
> On Tue, Feb 9, 2016 at 10:57 AM, Dan Liew <dan at su-root.co.uk> wrote:
>>
>> Hi,
>>
>> # TL;DR
>>
>> I've been building an application with and without the address
>> sanitizer (with gcc 5.3 and clang 3.7.1) and I've observed that the
>> application's behaviour changes (assertion hit/ not hit). I'm
>> wondering if this could be a bug in address sanitizer or if the
>> application I'm running is just buggy (e.g. doing bad things like
>> relying on memory layout, etc.). I'm also observing ASan reporting a
>> heap-use-after-free which Valgrind is not reporting, which makes me
>> wonder if it is a false positive.
>
>
> Let us start from this heap-use-after-free report. The one in
> https://github.com/Z3Prover/z3/issues/436 looks legitimate.
> Unless the application does something extremely weird and tricky,
> heap-use-after-free reports are usually true positives.

The developers tell me they are doing some unusual things and they
believe that this might the cause of the report.

> Can you somehow verify that this heap-use-after-free is happening?
> E.g. print all the pointer values coming from memory::allocate, coming into
> memory::deallocate, and coming into sat::clause::operator[]
>
> If curious, check what size of quarantine is required to catch this bug
> (ASAN_OPTIONS=quarantine_size_mb=N, default=256)
> Valgrind may have smaller default quarantine and thus misses this bug.

I was lazy and just told valgrind to execute the program (built by gcc
without ASan) with the largest quarantine it supported.

```
LD_LIBRARY_PATH=`pwd` valgrind --freelist-vol=10000000000 ./c_example
```

It didn't report any problems. This fills me with some confidence that
when the application is compiled without ASan that it probably doesn't
have a heap-use-after-free.


> Does the application have threads? (If yes, did you run with TSan?)

Not yet but I've stumbled across an issue that looks interesting. See below

> Did you try msan?

I just have and it immediately reported a problem. I took a closer
look and it looks like a false positive to me.

Here are the steps to reproduce

```
git clone https://github.com/Z3Prover/z3.git
cd z3
git checkout 9ed7dadc0251db992b44984edfa6c586aab20ecb
CC=clang CXX=clang++ CXXFLAGS="-fsanitize=memory -fPIE -pie
-fno-omit-frame-pointer -fsanitize-memory-track-origins"
LDFLAGS="-fsanitize=memory" python scripts/mk_make.py --build
build_msan_clang --noomp --debug
cd build_msan_clang
make
make c_example
LD_LIBRARY_PATH=`pwd` ./c_example
==26936==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x7fa7d906f3b0 in Z3_open_log
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../src/api/api_log.cpp:33:13
    #1 0x55c0c03107f5 in main
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../examples/c/test_capi.c:2794:5
    #2 0x7fa7d78f960f in __libc_start_main (/usr/lib/libc.so.6+0x2060f)
    #3 0x55c0c024d838 in _start
(/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/c_example+0x1c838)

  Uninitialized value was created by a heap allocation
    #0 0x55c0c0253ea0 in __interceptor_malloc
(/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/c_example+0x22ea0)
    #1 0x7fa7e2a6c120 in memory::allocate(unsigned long)
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../src/util/memory_manager.cpp:276:16
    #2 0x7fa7d906f125 in Z3_open_log
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../src/api/api_log.cpp:31:20
    #3 0x55c0c03107f5 in main
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../examples/c/test_capi.c:2794:5
    #4 0x7fa7d78f960f in __libc_start_main (/usr/lib/libc.so.6+0x2060f)

SUMMARY: MemorySanitizer: use-of-uninitialized-value
/home/dsl11/dev/klee/z3/z3_upstream/build_msan_clang/../src/api/api_log.cpp:33:13
in Z3_open_log
Exiting
```

Side note using ``MSAN_OPTIONS="halt_on_error=0`` doesn't seem to do
anything, the application always exits when it hits the reported bug.
I'd like a way to catch this in gdb but I'm not sure how to do it.

I took a look. MSan is complaining about this code

```
std::ostream * g_z3_log = 0;

...

    Z3_bool Z3_API Z3_open_log(Z3_string filename) {
        if (g_z3_log != 0)
            Z3_close_log();
        g_z3_log = alloc(std::ofstream, filename);
        g_z3_log_enabled = true;
        if (g_z3_log->bad() || g_z3_log->fail()) {
            dealloc(g_z3_log);
            g_z3_log = 0;
            return Z3_FALSE;
        }
        return Z3_TRUE;
    }
```

It seems to be complaining that ``g_z3_log`` is uninitialised when
doing ``g_z3_log->bad() || g_z3_log->fail()``. This seems incorrect
because ``alloc()`` appears to be this macro (see
``src/util/memory_manager.h``).

```
#define alloc(T,...) new (memory::allocate(sizeof(T))) T(__VA_ARGS__)
```
With the macro expanded it looks like this

```
g_z3_log = new (memory::allocate(sizeof(std::ofstream)))
std::ofstream(filename);
```

I think this is calling the "placement" version of ``operator new``
and so constructs a new object (in this case ``std::ofstream``)  using
the constructor but uses the memory allocated by
``memory::allocate(sizeof(T))``.

Therefore the memory in the pointer ``g_z3_log`` is initialised and
the "use-of-uninitialized-value" report is a false positive. Would you
agree with this assessment?

Thanks,
Dan.



More information about the cfe-dev mailing list