Live data from Hacker News

Why do we need for an Undefined Behavior Annex to C++

community.intel.com

41–50 of 76 posts

Re: Why do we need for an Undefined Behavior Annex to C++

#41
post #26

Notably, the C standard does have an undefined behavior annex, Annex J.2, since C99. It's not mentioned in the post, and this omission in tandem with repeated usage of "C and C++" at various points might suggest otherwise to some people.

And C has a UB study group and we are working on a technical rapport that in great detail describes how UB works in C, and a comprehensive list of sample code for all known UB.

Is there hope that we could some day achieve an exhaustive lists of all undefined behavior that C programmers need to be aware of?

I realize no one really knows today, but is there a path?

Re: Why do we need for an Undefined Behavior Annex to C++

#42
post #41

Earlier quoted context omitted.

And C has a UB study group and we are working on a technical rapport that in great detail describes how UB works in C, and a comprehensive list of sample code for all known UB.

Is there hope that we could some day achieve an exhaustive lists of all undefined behavior that C programmers need to be aware of? I realize no one really knows today, but is there a path?

No. It would require listing not just every place the standard states undefined behavior is the result, but also every corner case not covered by the spec, or that is under-specified. It’s possible to document known instances, but never to document all possible sources of undefined or unspecified behavior.

Re: Why do we need for an Undefined Behavior Annex to C++

#43
post #36
post #18

Earlier quoted context omitted.

Safe Rust has no undefined behavior. Undefined behavior does not mean no crashing, it means that the semantics of the program are undefined. Rust's semantics are to abort on a stack overflow. A language like C or C++ have no such semantics, they may abort or they may continue running and producing jibberish.

The fact that this program results in reading/writing an unmapped memory address means it’s doing an out-of-bounds access. It segfaults on macOS because the runtime/OS has allocated the stack such that the overflow results in a bad memory access, but that is a behavior of the runtime/OS/hardware, not the language. I guarantee I could exploit this on a system that does not have virtual memory, or a runtime that does n…

> It segfaults on macOS because the runtime/OS has allocated the stack such that the overflow results in a bad memory access, but that is a behavior of the runtime/OS/hardware, not the language.

Stack overflows are checked in C on macOS not because of guard pages but because the compiler emits stack checks (with cookies). Probably the same is true here.

> I guarantee I could exploit this on a system that does not have virtual memory, or a runtime that does not have unmapped addresses at the end of the stack, to, say, manipulate the contents of another thread’s stack. Therefore, this behavior is undefined.

That's implementation-defined, not undefined.

Re: Why do we need for an Undefined Behavior Annex to C++

#44
I love how in C++ there seems to be this security issue, and now hundreds of people try to come up with different ways on how to tackle the problem.

Everybody wants to help and spend their free time on it.

You don't see this in other languages so much. People just use those languages and "they" have to fix any flaws.

Also keep your bingo cards ready for people saying Rust does not have undefined behaviour.

I'm also not so sure Rust is even that safe. Yes it's memory safe but the languages with the most vulnerabilities is not C++. It's PHP, .NET, C, Java, JavaScript.

C++ is not an unsafe language. You can't make a language safe by adding an "unsafe" keyword end expect everything outside it to be safe. Else .NET and Java would be lower on the list than C++ because they do have bounds checks and they don't have raw pointers.

It's a bit of a false sense of security and a play of words this "unsafe" keyword in Rust.

Re: Why do we need for an Undefined Behavior Annex to C++

#45
post #20
post #12

Earlier quoted context omitted.

This is my point precisely. Having proven to itself that 0 <= x < 8, the compiler could go on to remove any test for x < 8, right?

Yes, and the compiler does that. That's one of the reasons that iterators are preferred over for-loops with manual access in Rust. While compilers can for very simple cases determine that you will always stay within the bounds (as in your example, where it can find at compile time that the length will always be 8 and no access outside happens) with iterators the compiler doesn't need to know the actual length and can…

The Rust compiler is smarter than that. Even for classic subscript-type loops, it can often elide the bounds checks. There was a discussion on this in HN a few months back, with machine code excerpts. An iterator and an explicit loop with subscripts both generated the same machine instructions.

If the compiler can elide bounds checks in inner loops, that's usually enough. Bounds checks elsewhere rarely affect performance.

Re: Why do we need for an Undefined Behavior Annex to C++

#46

I love how in C++ there seems to be this security issue, and now hundreds of people try to come up with different ways on how to tackle the problem. Everybody wants to help and spend their free time on it. You don't see this in other languages so much. People just use those languages and "they" have to fix any flaws. Also keep your bingo cards ready for people saying Rust does not have undefined behaviour. I'm also n…

You have data to confirm your claims? Moreover, vulnerabilities in languages themselves are not that important in comparison to vulnerabilities in software written in the specific language, at least how I understand this.

Re: Why do we need for an Undefined Behavior Annex to C++

#47
post #35
post #18

Earlier quoted context omitted.

Safe Rust has no undefined behavior. Undefined behavior does not mean no crashing, it means that the semantics of the program are undefined. Rust's semantics are to abort on a stack overflow. A language like C or C++ have no such semantics, they may abort or they may continue running and producing jibberish.

How does Rust implement this on targets where LLVM does not implement stack clash protection?

On Unix targets it installs a signal handler for SIGSEGV and checks if the faulting address falls within the range of the stack guards. See https://github.com/rust-lang/rust/blob/411f34b/library/std/s...

The stack guards would normally be setup by the system runtime (e.g. kernel in the case of the main thread stack, libc for thread stacks), not Rust's runtime. Likewise, stack probes that ensure stack operations don't skip guard pages are usually (always?) emitted by the compiler backend (e.g. GCC, LLVM), not Rust's instrumentation, per se.

In this sense Rust isn't doing anything different than any other typical C or C++ binary, except that automagically hijacking SIGSEGV (or any other signal) from non-application code as Rust does is normally frowned upon, especially when it's merely for aesthetics--i.e. printing a pretty message in-process before dying. Also, attempting to introspect current thread metadata from a signal handler gives me pause. I'm not familiar enough with Rust to track down the underlying implementation code. I presume it's using some POSIX threads interfaces, but POSIX threads interfaces aren't async-signal safe, and though SIGSEGV would normally be sent synchronously (sometimes permitting greater assumptions about the state of the thread), that doesn't mean the Rust runtime isn't technically relying on undefined behavior.

EDIT: To get the guard page range it's using pthread_self, pthread_getattr_np, pthread_attr_getstack, and friends, of which only pthread_self is async-signal safe. See https://github.com/rust-lang/rust/blob/411f34b/library/std/s... I have no concrete evidence to believe the reliance isn't safe in practice on the targeted platforms (OTOH, I could imagine the opposite), but it's a little ironic that it's depending on undefined behavior.

Re: Why do we need for an Undefined Behavior Annex to C++

#48

I love how in C++ there seems to be this security issue, and now hundreds of people try to come up with different ways on how to tackle the problem. Everybody wants to help and spend their free time on it. You don't see this in other languages so much. People just use those languages and "they" have to fix any flaws. Also keep your bingo cards ready for people saying Rust does not have undefined behaviour. I'm also n…

You have data to confirm your claims? Moreover, vulnerabilities in languages themselves are not that important in comparison to vulnerabilities in software written in the specific language, at least how I understand this.

I don't have solid data but for "core" mature software written in c/c++ like browsers and Linux, I feel like I see far more high profile security bugs from the lack of memory safety rather than something like "Linux failed to enforce the existing permissions".

Re: Why do we need for an Undefined Behavior Annex to C++

#49

Earlier quoted context omitted.

I don’t know. Safe rust has 0 UB although if I recall correctly things are still a bit messy with integer overflow. I could imagine a world where we define annotations around “signed integer will never overflow” that you can add to hit paths but otherwise disallow optimizing around that UB and for all other UB require an explicit annotation acknowledging it or just a warning and a missed optimization instead. The cur…

things are still a bit messy with integer overflow No, they aren't messy, they're fully defined. Integer overflow in safe Rust works just like Java: the numbers wrap around. It's totally defined. You can, optionally, also enable a runtime check for this wraparound. This is usually enabled for debug builds. There is of course a performance cost to doing this.

I didn't say they're undefined but I guess we can agree to disagree that integer addition behavior depending on debug vs release is messy.

Re: Why do we need for an Undefined Behavior Annex to C++

#50

Earlier quoted context omitted.

I don’t know. Safe rust has 0 UB although if I recall correctly things are still a bit messy with integer overflow. I could imagine a world where we define annotations around “signed integer will never overflow” that you can add to hit paths but otherwise disallow optimizing around that UB and for all other UB require an explicit annotation acknowledging it or just a warning and a missed optimization instead. The cur…

things are still a bit messy with integer overflow No, they aren't messy, they're fully defined. Integer overflow in safe Rust works just like Java: the numbers wrap around. It's totally defined. You can, optionally, also enable a runtime check for this wraparound. This is usually enabled for debug builds. There is of course a performance cost to doing this.

> they aren't messy, they're fully defined. Integer overflow in safe Rust works just like Java: the numbers wrap around.

That is both fully defined and messy. They are not mutually exclusive. It means there are integers where n+1 is less than n. That is messy. No integers that I learned about in math class work like that.

The only two non-messy well-defined behaviours are 1) bignums by default like in Python, but not suitable for a low level language like Rust; or, 2) trap on overflow, like Ada is supposed to do though it is usually shut off by a pragma. Both of those have significant runtime cost.

Post reply on HN