Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

As we know, the C standard does not specify a lot of details of the implementation, for example value of NULL pointer, order of bits and bytes (endiannes), alignment in structs and of stack parameters, actual organization of memory (I don't think stack layout is necessary at all, just "auto"). Additionally, some other parts are instead defined (or undefined) to produce undefined behavior, such as dereferencing null (commonly segfault but not necessarily)

Since modern platforms (or is it up to the compiler?) (intentionally?) behave similarly, some consistent behavior of programs is actually dangerous to rely on.

So, is there a unix variant or unix inspired platform, (or a compiler if it is mostly up to the compiler?) which is standard but intentionally weird to produce bugs and point out undefined behavior and unportability? Does a similar thing exist but for POSIX instead of C? Is it even a reasonable thing to try and implement?

share|improve this question
    
posh on Debian does that to some extent for sh (though for the debian policy, not POSIX, though the debian policy is mostly based on POSIX). –  Stéphane Chazelas Mar 12 at 12:29
1  
You can rely on the standard-mandated behaviour (modulo compiler omissions or bugs). On what the standard says you can't rely, well... I.e., you have to program carefully. –  vonbrand Mar 12 at 14:17

3 Answers 3

Hewlett Packard's HP-UX is the closest I can think of.

The C compiler will allow NULL-pointer dereferencing, although there was a flag to change this. The stack grew "up" (towards larger addresses) and the heap grew down (towards smaller addresses) on PA-RISC hardware.

The PA-RISC hardware had some oddities beyond stack/heap reversal, too. It had a segment register (worked differently than x86 segments) so that various libraries actually lived in different segments, and pointers-to-functions were not a single, 64-bit pointer. I don't recall if PA-RISC was as strict about pointer alignment as SPARC was, however.

On a somewhat more available level, you can use C compilers like Clang, Pcc or even Tcc, although Tcc has some problems with GNU linkers and loaders because of "weak symbols" as I understand it. At the very least, you will get different warning messages from these alternate compilers, which is always worthwhile.

You could also try alternate C libraries, like Diet Libc or Musl. I've had code do different things when compiled against Musl as opposed to GNU Libc. Both of these libraries support static linking, which GNU Libc makes difficult or impossible. Musl even has a very different dynamic linking system, which might expose latent bugs in your code.

share|improve this answer

So, is there a unix variant or unix inspired platform, (or a compiler if it is mostly up to the compiler?) which is standard but intentionally weird to produce bugs and point out undefined behavior and unportability?

No because...

Is it even a reasonable thing to try and implement?

...not if reasonable means "a tool anybody would reasonably want to use". But yes in the sense that this is what a normal C compiler is already. They inevitably do allow you to indulge in undefined behavior in a defined (predicatable) way. For example:

i = i++ + ++i;

Is undefined because you've modified i multiple times between sequence points. However, almost certainly a given compiler will produce the exact same result every time. Of course, this won't be documented because you should not do it in the first place, but the compiler may know and can tell you if you ask it.

Put that in a file and compile it, gcc undefined.c. No problems. Now try it with some warnings enabled, gcc -Wall -pendantic undefined.c:

undefined.c: In function ‘main’:
undefined.c:8:4: warning: operation on ‘i’ may be undefined [-Wsequence-point]
  i = i++ + ++i;
    ^

There you go. Now, keeping in mind that the compiler will produce the same result here everytime, you could decide to exploit this for whatever purpose. This means it is impossible for a compiler to "intentionally...produce bugs" when you do something wrong, if we define a "bug" as something you didn't want to happen. It doesn't know what you want to happen and it cannot. Therefore, it can't intentionally produce a result you can't predict.

It obviously can, however, "point out undefined behaviour".

If you want something that can go further than the compiler in spotting mistakes, look into valgrind. There are other (possibly very very very expensive) things along the same line, but this is the only one I've used.

Taking something you've developed exclusively on a 64-bit machine and compiling and running it 32-bit can also expose a lot of facepalm -- but obviously that is not the ideal way to correct mistakes.

Finally: the defacto way to test software is to test it. You write specific tests as you go, and you run them over and over again: at the end of the day, whenever you've added a new piece, etc. There is no substitute for this.

share|improve this answer

The IBM Z Series runs Linux, and that is a quite strange beast. I don't know if they still use EBCDIC.

The Crays had all data types 32 bits (including ´char´).

There are C compilers for some 16 bit CPUs around (I fondly remember Turbo C on the PC, for instance). The word size is one of the differences waiting to byte you...

In his blog, John Regehr discusses tools to check that compilers don't do funky stuff. He has several posts on undefined behaviour, and how it allows (sometime surprising) code optimizations. Also discussions of undefined behaviour "in the wild"...

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.