Live data from Hacker News

Everything in C is undefined behavior

blog.habets.se

691–700 of 748 posts

Re: Everything in C is undefined behavior

#691

Earlier quoted context omitted.

> because Assembly doesn't have UB To be pedantic, old hardware like 6502 family chips (Commodore 64, Apple II, etc) had illegal instructions which were often used by programmers, but it was completely up to the chip to do whatever it wanted with those like with UB.

> illegal instructions... were often used by programmers Intentionally, with an expected effect? I'd need a citation for that.

Some instructions were very useful and they were simply discovered by programmers who tried out what each instruction did. People did not necessarily have access to documentation those days!

So any instruction or hardware feature would get used, whether it's "officially" documented or not.

Re: Everything in C is undefined behavior

#692

Earlier quoted context omitted.

> I’ve always assumed access past the end of an array can’t always be detected in C, so I don’t see how those instructions could be eliminated. "Can't always be detected" is jut a different way of saying "Can sometimes be detected". Upon detection, I'd rather that the compiler still emit the instructions, not elide the code altogether.

Now the behavior of your compiler/runtime stack is dependent on the sophistication of your compiler or runtime relative to the particular code at issue + the specific information available statically or dynamically in the instance. That does not seem like an improvement if your goal is predictable, consistent behavior.

> Now the behavior of your compiler/runtime stack is dependent on the sophistication of your compiler or runtime relative to the particular code at issue + the specific information available statically or dynamically in the instance.

> That does not seem like an improvement if your goal is predictable, consistent behavior.

Getting unpredictable outcomes only on some items is better than getting unpredictable outcomes on every item. This is what I meant when I said moving UB to IB might not fix everything, but it is certainly a solid start!

Re: Everything in C is undefined behavior

#693
post #577

Earlier quoted context omitted.

> Now, is it exploitable that `find` also reads the uninitialized auto variable `status` (UB) from a `waitpid(&status)` before checking if `waitpid()` returned error? (not reported) I can't imagine an architecture or compiler where it would be, no. I presume you're referring to this code: pid = waitpid(pid, &status, 0); if (WIFEXITED(status)) rval = WEXITSTATUS(status); else rval = -1; The only signal handler find in…

Couldn’t waitpid return EINTR if the (parent) process were stopped and then continued? EINTR scares the crap out of me because nobody expects it!

No. You only get EINTR when a signal handler fires and you didn't use the SA_RESTART flag with sigaction. If you don't install any signal handlers, or you use SA_RESTART on all handlers, or you've blocked/masked all signals (or at least the ones with handlers), you won't get EINTR.

When writing library code, it's important to consider EINTR because you can't know about signal dispositions. Though, the common practice of looping on EINTR kind of defeats the purpose.

Re: Everything in C is undefined behavior

#694
post #641

Earlier quoted context omitted.

> The language should not let you create an an invalid pointer, or at least warn you when you are doing so completely agree!

That's a nonsensical statement, a language cannot warn you, only a compiler can (-Wcast-align). The compiler can also decide what is and isn't an invalid pointer, this way the language avoids leaky abstractions.

Thanks for the clarification. Yes, that's right – that's the job of the compiler/parser/etc.

I like the C programming language. To be honest, I regret not having the knowledge (low skills) to write my own compiler, or at least a standard library. I have some rough sketches on paper of what it should look like, but I can't implement it.

Re: Everything in C is undefined behavior

#695

Earlier quoted context omitted.

I get that it's defined that way, but I'd really like to know why. I can see the value in saying that struct x* isn't compatible with struct y*, because they could have different alignment or packing rules. But struct x* and void*, which is already special-cased to allow assignment without a cast? Why aren't these considered compatible in function pointer parameter definitions? Is there any work involved in casting v…

Yes, some restrictions seem arbitrary. Just like why these two types are not compatible: struct a {int data;}; struct b {int data;}; I know, I know, changing it would break existing code, etc.

You jest, but a central feature of C is that void* is a universal pointer, and can be used as the basis of polymorphism and context passing. That's why you can do things like:

    void qsort(void *base, size_t nmemb, size_t size,
               int (*compar)(const void *, const void *));
It would be nice to write:

    int compare_foo(const struct foo *a, const struct foo *b) {
        ...
    }
But instead we have to write this to avoid being declared "undefined behaviour":

    int compare_foo(const void *a, const void *b) {
        const struct foo *real_a = (struct foo *)a;
        const struct foo *real_b = (struct foo *)b;
        ...
    }
So what is the upside of having this rule, given the same thing is going to happen anyway, just more verbose?

Re: Everything in C is undefined behavior

#696
post #511

Earlier quoted context omitted.

This is one of those "everyone doing this kind of work knows" that's rather hard to source, but: this is basically the point of volatile. Especially for reads rather than writes, where you may want to read some location that is being written into by a different piece of hardware. People used to use it for thread synchronization before proper memory barrier primitives (see https://mariadb.org/wp-content/uploads/2017/1…

Yeah. I could have sworn that I've read somewhere an anecdote from the Bell Labs era in which this comes up, but I can't find it and might be misremembering. The whole volatile keyword doesn't exist in K&R C as released, there are no "type qualifiers" at all in that language, both volatile and const are introduced in C89. Duff's famous Device, often misunderstood as some insight about memory copying or something sill…

No idea about volatile, but I do remember function prototypes and const came as influence from C++, well CFront.

Re: Everything in C is undefined behavior

#698

Earlier quoted context omitted.

A lot of the Central UB can not be defined, because they rely on detection. In order to have a well defined behaviour (by the standard or the compiler) the implementation needs to first detect that the behaviour is triggered, this is often very tricky or expensive. Its easy to define that a program should halt, if it writes outside an array, but detecting if it does can be both slow and hard to implement. There are i…

> How would you optimize: (x * 2) / 2 I'd do the math myself and just write x. I don't even use * for multiplication anymore, I use __builtin_mul_overflow and then check the result. Anyone who doesn't is gonna hit the overflow case one day, and they'll be lucky if their program isn't exploited because of it. I've been making an effort to use all the overflow checking builtins by default in most if not all cases. I've…

The thing with (x * 2) / 2 is that for all practical purposes you might even have written something else, so the expression cannot be replaced by x directly.

What happens is that after a few common expression eliminations, peephole optmisations, code inlining, and possibly other optimisation passes, the remaining AST will be (x * 2) / 2, and then the magic happens.

Re: Everything in C is undefined behavior

#699
post #268

Earlier quoted context omitted.

I was about to call out that the code is supposed to be C and not C++, but I double checked and I realised it actually says std::atomic , not atomic_int!

Exactly, this is very old C++ on display in this article. It’s certainly not as safe as a language like Rust, but quite a lot of undefended behavior and things that will shoot yourself in the foot have been changed over the last 10 years. Most C++ today will be immediately obvious and not accidentally mixed up with C.

Unfortunely most C++ today keeps making use of C idioms, including at companies with seat at WG21 table.

Re: Everything in C is undefined behavior

#700

Earlier quoted context omitted.

What should the behavior above be defined to do?

Print x twice. Not all “side effects” care about order. Better yet, define an order for parameter evaluation.

There is an easy way to take control: read the volatile variable only once.

  volatile int x = 5;
  ...
  int y=x;
  printf("%d in hex is 0x%x.\n", y, y);
Post reply on HN