The Following Code Causes Segfault in Clang
llvm.org
llvm.org
:quest $ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
:quest $ while true; do
> ./main.native -test gcc -n 1 > foo.c
> gcc -O2 -o foo foo.c
> ./foo || break
> echo -n .
> done
................................................................
................................................................
.................................................
Assertion failed: (b32 == b43), function callee_b0f, file foo.c, line 128.
Abort trap: 6
This means the tool found C code where parameter passing is not compiled properly. It took about 10 seconds to find this. The test case is pretty small: :quest $ wc foo.c
140 444 3485 foo.c
The generated code that where the assertion checks that parameters are received correctly looks like this: static
union bt8 *
callee_b0f(struct bt4 *bp7,
double *bp8,
struct bt6 bp9,
float bp10,
struct bt7 bp11,
double bp12,
short int bp13,
...)
{
va_list ap;
typedef int bd0;
typedef struct bt0 bd1;
typedef int bd2;
typedef union bt3 bd3;
bd0 b41;
bd1 b42;
bd2 b43;
bd3 b44;
/* seed: 2040 */
va_start(ap, bp13);
QUEST_ASSERT(b34 == bp7);
QUEST_ASSERT(b35 == bp8);
QUEST_ASSERT(b36.b24.b18 == bp9.b24.b18);
QUEST_ASSERT(b36.b24.b19 == bp9.b24.b19);
QUEST_ASSERT(b36.b24.b20 == bp9.b24.b20);
QUEST_ASSERT(b36.b24.b21 == bp9.b24.b21);
QUEST_ASSERT(b36.b24.b22 == bp9.b24.b22);
QUEST_ASSERT(b36.b24.b23 == bp9.b24.b23);
QUEST_ASSERT(b36.b25 == bp9.b25);
QUEST_ASSERT(b36.b26 == bp9.b26);
QUEST_ASSERT(b37 == bp10);
QUEST_ASSERT(b38.b27 == bp11.b27);
QUEST_ASSERT(b39 == bp12);
QUEST_ASSERT(b40 == bp13);
b41 = va_arg(ap, bd0);
b42 = va_arg(ap, bd1);
b43 = va_arg(ap, bd2);
b44 = va_arg(ap, bd3);
QUEST_ASSERT(b30 == b41);
QUEST_ASSERT(b31.b0 == b42.b0);
QUEST_ASSERT(b32 == b43);
QUEST_ASSERT(b33.b10.b1 == b44.b10.b1);
va_end(ap);
return b29;
} python -S -c 'print("void f(){} int main(){return (" + "*"*10**7 + "f)();}")' | gcc -xc -
(This is legal C -- look it up. Don't argue with me over the practical relevance of this please) ulimit -s unlimited> Both the translation and execution environments constrain the implementation of language translators and libraries. The following summarizes the language-related environmental limits on a conforming implementation; the library-related limits are discussed in clause 7.
> The implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following limits:
> 4095 characters in a logical source line
Of course, it notes:
> Implementations should avoid imposing fixed translation limits whenever possible.
Note that these aren't strict limits, and don't really have an effect on the legality of your program, I feel it's more of a discussion of the limits imposed by reality, and what compilers must handle at a bare minimum.
And honestly, I would hope most modern compilers would do better than the noted limits and I'd also hope for a decent error message, not "gcc: internal compiler error: Segmentation fault (program cc1)" (which is what the program generates).
Last,
> This is legal C
Is it? You're returning the result of a function that returns void in a function that returns int (and even if main were void, I still don't think that's legal). Were gcc able to handle the abusive number of stars, it would say,
<stdin>: In function ‘main’:
<stdin>:1:23: error: void value not ignored as it ought to be
(which is what it says if you remove some of the stars.) Granted, this can be corrected, and your example will still cause the same output. (Which doesn't seem nearly as interesting as the linked C++ code. I'd like to know why that causes a segfault. With yours, I'd like to know why you were doing that.)The reason I was testing this was a discussion on IRC about functions decaying to pointer to functions, such that they are endlessly dereferenceable. The snippet above crashes GCC -- hard.
So, while the implementation is free not to handle 10^7 dereferencing operations, I'm not sure a hard crash is the right answer.
Here's a version without the return and using a lambda to shorten it further:
python -S -c 'print("int main(){(" + "*"*10**7 + "+[]{})();}")' | g++ -std=c++11 -xc++ - python -c 'print ("int main(){" + "*" * (10**6) + "}")' | gcc -x c -I don't expect a compiler to be able to compile programs that exceed its internal limits, but a better error message would be much appreciated, instead of a hard crash. For comparison, with MSVC the parent's code produces
fatal error C1026: parser stack overflow, program too complex
and the nested parentheses results in fatal error C1013: compiler limit : too many open parentheses Unable to find instantiation of declaration!
UNREACHABLE executed at SemaTemplateInstantiateDecl.cpp:4384!
Not quite so unreachable...https://gist.github.com/cwgreene/d689f010619310dbbc77
https://github.com/llvm-mirror/clang/blob/b310439121c875937d...
template<class T> class foo
{
public:
~ foo()
{
}
foo &operator = (const foo &rhs)
{
foo::~foo();
new (this) foo (rhs);
return *this;
}
};
int main(int argc, char * argv[])
{
foo<int> a, b;
b = a;
}"Correctness" itself isn't an absolute. While something either does or does not conform to whatever has been defined as "correct", it's perfectly fine for the defined "correct" behavior in a given situation to be a crash.
In some cases a crash is the best that can be hoped for. Continuing on, even in an attempt to handle the failure more gracefully, can potentially be more harmful than just crashing.
You do understand what an ever increasing attack surface does?
There are better languages to write compilers in. OCaml is one.
> In general, these terms do not have a precise definition. Rather, they tend to be used by advocates or critics of a given programming language, as a means of explaining why a given language is better or worse than alternatives.
In practice it is a non issue, because build scripts can run arbitrary code long before a memory corruption in the system compiler. And if you plan on running the code you just compiled, you must already trust the code.
Unless of course its a segfault in LLVM and not just clang. LLVM is used for JIT in web browsers now, not that there are really any suitable replacements for C++ right now.