News for hackers. Stuff that matters.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @04:57PM (#32348215)
How does a huge VLA corrupt the stack? If there's not enough space but code keeps going then isn't that a massive bug with your compiler or runtime?
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by anyfoo on Thursday August 04, 2022 @06:35PM (#32349178)
Okay. How do you tell the kernel that? Sure, the kernel will have put a guard page or more at the end of the stack, so that if you regularly push onto the stack, you will eventually hit a guard page and things will blow up appropriately.

But what if the length of your variable length array is, say, gigabytes, you've blown way past the guard pages, and your pointer is now in non-stack kernel land.

You'd have to check the stack pointer all the time to be sure, that's prohibitive performance-wise. Ironically, x86 kind of had that in hardware back when segmentation was still used.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @06:53PM (#32349340)
I think the normal pattern is a stack probe every page or so when there's a sufficiently large allocation. There's no need to check the stack pointer all the time.

But that's not my point. If the compiler/runtime knows it will blow up if you have an allocation over 4KB or so, then it needs to do something to mitigate or reject allocations like that.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by anyfoo on Thursday August 04, 2022 @06:58PM (#32349371)
> I think the normal pattern is a stack probe every page or so when there's a sufficiently large allocation.

What exactly are you doing there, in kernel code?

> But that's not my point. If the compiler/runtime knows it will blow up if you have an allocation over 4KB or so, then it needs to do something to mitigate or reject allocations like that.

Do what exactly? Just reject stack allocations that are larger than the cluster of guard pages? And keep book of past allocations? A lot of that needs to happen at runtime, since the compiler doesn't know the size with VLAs.

It's not impossible and mitigations exist, but it is pretty "extra". gcc has -fstack-check that (I think) does something there.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @07:12PM (#32349484)
> What exactly are you doing there, in kernel code?

In kernel code?

What you're doing is triggering the guard page over and over if the stack is pushing into new territory.

> Do what exactly? Just reject stack allocations that are larger than the cluster of guard pages? And keep book of past allocations? A lot of that needs to happen at runtime, since the compiler doesn't know the size with VLAs.

Just hit the guard pages. You don't need to know the stack size or have any bookkeeping to do that, you just prod a byte every page_size. And you only need to do that for allocations that are very big. In normal code it's just a single not-taken branch for each VLA.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by anyfoo on Thursday August 04, 2022 @07:18PM (#32349528)
That seems to be what -fstack-check for gcc is doing:

"If neither of the above are true, GCC will generate code to periodically “probe” the stack pointer using the values of the macros defined below."[1]

I guess I'm wondering why this isn't always on if it solves the problem with negligible cost? Genuine question, not trying to make a point.

[1] https://gcc.gnu.org/onlinedocs/gccint/Stack-Checking.html

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @07:32PM (#32349635)
What I'm finding in a quick search is:

* It should be fast, but I haven't found a benchmark.

* There appear to be some issues of signals hitting at the wrong time vs. angering valgrind, depending on probe timing.

* Probes like this are mandatory on windows to make sure the stack is allocated, so it can't be that bad.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by anyfoo on Thursday August 04, 2022 @07:36PM (#32349670)
I'm mostly interested in it for kernel code though, so the second point at least does not apply, at least not directly. Maybe there is something analogous when preempting kernel threads, I haven't thought it through at all. But interesting.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Joker_vD on Friday August 05, 2022 @03:26AM (#32352842)
Because it's on by default in MSVC [0], and we all know that whatever technical decisions MS makes, they're superior to whatever technical decision the GNU people make. /s

Speaking seriously, I too would like an answer.

[0] https://docs.microsoft.com/en-us/windows/win32/devnotes/-win...

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by pjmlp on Friday August 05, 2022 @04:48AM (#32353400)
One decision Microsoft made was not to support VLAs at all, even after their new found C love.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by bjourne on Thursday August 04, 2022 @09:30PM (#32350406)
An attacker would first trigger a large VLA-allocation that puts the stack pointer within a few bytes of the guard page. Then they would just have the kernel put a return address or two on the stack and that would be enough to cause a page fault. The only way to guard against that would be to check that every CALL instruction has enough stack space which is infeasible.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @10:15PM (#32350726)
But that's the entire point of the guard page, it causes a page fault. That's not corruption.

Denial of service by trying to allocate something too big for the stack is obvious. I'm asking about how corruption is supposed to happen on a reasonable platform.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by naasking on Friday August 05, 2022 @03:29AM (#32352860)
Perhaps they're trying to guard against introducing easy vulnerabilities on unreasonable platforms. With VLAs unskilled developers can perhaps more easily introduce this problem. It would be a case of bad platforms and bad developers ruining it for the rest.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Someone on Friday August 05, 2022 @03:22AM (#32352813)
An attacker could trigger a large VLA allocation that jumps over the guard page, and a write to that allocation. That write would start _below_ the guard page, so damage would be done before the page fault occurs (ideally, that write wouldn’t touch the guard page and there wouldn’t be a page fault but that typically is harder to do; the VLA memory allocation typically is done to be fully used)

Triggering use of the injected code may require another call timed precisely to hit the changed code before the page fault occurs.

Of course, the compiler could and should check for stack allocations that may jump over guard pages and abort the program (or, if in a syscall, the OS) or grow the stack when needed. Also, VLAs aren’t needed for this. If the programmer creates a multi-megabyte local array, this happens, too (and that can happen accidentally, for example when increasing a #define and recompiling)

The lesson is, though, that guard pages alone don’t fully protect against such attacks. The compiler must check total stack space allocated by a function, and, if it can’t determine that that’s under the size of your guard page, insert code to do additional runtime checks.

I don’t see that as a reason to outright ban VLAs, though.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by bjourne on Friday August 05, 2022 @03:41PM (#32360973)
VLAs give the attacker an extra attack vector. The size of the VLA is runtime-determined and potentially controlled by user input. Thus, the only safe way to handle VLAs is to check that there is enough stack space for every VLA allocation. Which may be prohibitively expensive and even impossible on some embedded platforms. Stack overflows may happen for other reasons too, but letting programmers put dynamic allocations on the stack is just asking for trouble.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Someone on Saturday August 06, 2022 @01:49AM (#32365257)
I don’t think “may be prohibitively expensive and even impossible on some embedded platforms” is a strong argument for not including it in C. There are many other features in C for which that holds, such as recursion, dynamic memory allocation, or even floating point.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by petters on Thursday August 04, 2022 @05:08PM (#32348304)
Welcome to the world of undefined behavior. Anything can happen....
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by mtlmtlmtlmtl on Thursday August 04, 2022 @05:48PM (#32348733)
I think this is a common misunderstanding about UB. It's not that anything can happen, just that the standard doesn't specify what happens, meaning whatever happens is compiler/architecture/OS dependent. So you can't depend on UB in portable code. But something definite will happen, given the current state of the system. After all, if it didn't, these things wouldn't be exploitable either.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by tialaramex on Thursday August 04, 2022 @07:31PM (#32349632)
> But something definite will happen, given the current state of the system.

This is only true in the very loose and more or less useless sense that the compiler is definitely going to emit some machine code. What does that machine code do in the UB case? It might be absolutely anything.

One direction you could go here is you insist that surely the machine code has a defined meaning for all possible machine states, but that's involving a lot of state you aren't aware of as the programmer, and it's certainly nothing you can plan for or anticipate so it's essentially the same thing as "anything can happen".

Another is you could say, no, I'm sure the compiler is obliged to put out specific machine code, and you'd just be wrong about that, Undefined Behaviour is distinct from Unspecified Behaviour or merely Platform Dependant behaviour.

Many C and C++ programmers have the mistaken expectation that if their program is incorrect it can't do anything really crazy, like if I never launch_missiles() surely the program can't just launch_missiles() because I made a tiny mistake that created Undefined Behaviour? Yes, it can, and in some cases it absolutely will do that.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by mtlmtlmtlmtl on Friday August 05, 2022 @02:11AM (#32352383)
I'm aware you can get some pretty crazy behaviours, say if you end up overwriting a return address and your code begins to jump around like crazy. Even that could reproduce the same behaviour consistently though.

I once had a bug like that in a piece of AVR C code where the stack corruption would happen in the same place every time and the code would pathologically jump to the same places in the same order every time. It's worth noting though that when there's an OS, usually what will happen is just a SIGABRT. See the OpenBSD libc allocator for a masterclass in making misbehaving programs crash.

I was never advocating to rely on UB, btw. But yes, UB can be understood in many cases.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by legalcorrection on Thursday August 04, 2022 @10:09PM (#32350681)
You are confusing the C standard and actual platforms/C implementations. A lot of things are UB in the standard but perfectly well defined on your platform. Standards don’t compile code, real compilers do. The standard doesn’t provide standard library implementations, the actual platform does.

Targeting the standard is nice, but if all of your target platforms guarantee certain behaviors, you might consider using those. A lot of UB in the C standard is perfectly defined and consistent across MSVC, GCC, Clang, and ICC.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by tialaramex on Friday August 05, 2022 @05:23AM (#32353625)
> A lot of UB in the C standard is perfectly defined and consistent across MSVC, GCC, Clang, and ICC.

Do you have examples of this "a lot of UB in the C standard" which is in fact guaranteed to be "perfectly defined and consistent" across all the platforms you listed ? You may need to link the guarantees you're relying on.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by legalcorrection on Friday August 05, 2022 @09:01AM (#32355470)
Okay so take the two most complained about UBs, improper aliasing and signed integer overflow. Every compiler I’ve ever used lets you turn both into defined behavior.
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by tialaramex on Friday August 05, 2022 @08:58PM (#32363752)
These things aren't the default, the compiler may "let you" but it doesn't do it until you already know you've got a problem and you explicitly tell it you don't want standard behaviour. However lets ignore that for a moment:

My GCC offers to turn signed integer overflow into either an abort or wrapping, either of which is defined behaviour, but how do I get MSVC to do precisely the same thing?

Likewise for aliasing rules. GCC has a switch to have the optimiser not assume the language's aliasing rules are actually obeyed, but what switch in MSVC does exactly the same thing?

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by legalcorrection on Saturday August 06, 2022 @02:11AM (#32365403)
MSVC doesn’t enforce strict aliasing to begin with. And passing /d2UndefIntOverflow makes signed integer overflow well-defined. Even if you don’t pass it, MSVC is very conservative in exploiting that UB, precisely to avoid breaking code that was valid before they started doing this optimization (2015 or so).
Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by protomolecule on Friday August 05, 2022 @02:01AM (#32352314)
What you are describing is unspecified and implementation-defined behavior [0].

Avoiding UB (edit: in general) doesn't have anything to do with the code being portable and everything with the code not being buggy [1][2].

[0] https://en.cppreference.com/w/c/language/behavior

[1] https://blog.regehr.org/archives/213

[2] http://blog.llvm.org/2011/05/what-every-c-programmer-should-...

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by legalcorrection on Friday August 05, 2022 @02:16AM (#32352419)
Oh really? Then why does every compiler I use have a parameter to turn off strict aliasing?

You cite to a source that contradicts you. In the llvm blog post: "It is also worth pointing out that both Clang and GCC nail down a few behaviors that the C standard leaves undefined."

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by protomolecule on Friday August 05, 2022 @02:42AM (#32352565)
Sometimes a compiler gives a guarantee that a particular UB is always handled in a specific way, but you cannot generalize this to all UB.

Added 'in general' to my comment to make this explicit.

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by Dylan16807 on Thursday August 04, 2022 @06:49PM (#32349302)
What is undefined about a large VLA? It shouldn't be undefined.

According to wikipedia "C11 does not explicitly name a size-limit for VLAs"

Re:C99 doesn't need function bodies, or 'VLAs are Turing com... by saagarjha on Thursday August 04, 2022 @07:56PM (#32349833)
The C standard has no mentions of a program stack. This isn’t undefined behavior.