ooc-lang / rock

:ocean: self-hosted ooc compiler that generates c99
http://ooc-lang.org/
MIT License
401 stars 40 forks source link

Unexpectedly "good" behaviour from ooc arrays #1005

Closed marcusnaslund closed 7 years ago

marcusnaslund commented 7 years ago

(This may be the product of something I've misunderstood, or just pure random chance, if so I will close)

Consider the very simple program:

Foo: class {
    init: func
    test: func {
        arr: Int[]
        arr free()
    }
}

Foo new() test()

(I know, with GC you wouldn't have to manually free, but bear with me)

The generated C code for test is

void wtf__Foo_test(wtf__Foo* this) {
    _lang_array__Array arr; // uninitialized arr.data and arr.length!
    types__Array_free(arr); // calls free(arr.data);
}

This puts an uninitialized array on the stack, where length and data have garbage values. But the free call never seems to crash, as opposed to

f: Foo
f free()

which always crashes. With good reason, of course. But why does freeing an uninitialized ooc array never seem to crash?

(Running gcc 4.9 on ubuntu 14.04.)

marcusnaslund commented 7 years ago

In contrast,

#include <stdlib.h>
#include <time.h>

int main()
{
    srand(time(0));
    int* p = (int*)(rand() % 0xFFFFFFFF);
    free(p);
}

segfaults every time.

vendethiel commented 7 years ago

This puts an uninitialized array on the stack

No, it doesn't. It puts an initialized struct on the stack, which contains a pointer to void.

It's undefined behavior, so really, nothing is "expected". If you compile with optimizations, the free() will be elided. with gcc -O2, this is the x86 generated for your program:

main:                                   # @main
        xorl    %eax, %eax
        retq

Without optimizations, we get:

main:                                   # @main
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movq    -8(%rbp), %rdi # quadword
        callq   free
        xorl    %eax, %eax
        addq    $16, %rsp
        popq    %rbp
        retq

Since everything is uninitialized, what you have on the stack is what was there before.

If we go out of our way to "pollute" the stack, it's easy to observe a crash:

http://coliru.stacked-crooked.com/a/22ab5eb5b913af2e

marcusnaslund commented 7 years ago

It puts an initialized struct on the stack, which contains a pointer to void.

Sorry, this is what I meant. The data pointer is what is uninitialized.

I understand now, I just did not pollute the stack enough to see the problem.

Many thanks.

vendethiel commented 7 years ago

No problem. The Wonderful World Of UB is always difficult to navigate in...