Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

A contemporary x86, RISC, mixed (ISA + accelerators), or what other CPU? I think CPU is a broad term. :) Anyway, Wikipedia has a detailed write-up that assembly experts can base a comparison on:

https://en.wikipedia.org/wiki/PDP-11_architecture

It wasn't that PDP-11 made C implementations more efficient. It's that C was a BCPL specifically designed to compile easily and run fast on their PDP-11. That's why I can't overemphasize C's actual history vs the lore that people repeat. It's literally an ALGOL language with every feature that couldn't compile on 60's and 70's era hardware chopped off with some extensions added latter.

http://pastebin.com/UAQaWuWG

Worked fine for a PDP-11. Yet, forcing its memory model or tradeoffs into a language used on different hardware can cause unnecessary problems. In contrast, Hansen's Edison language deployed on PDP-11 had only five statements (extreme simplicity haha) but would map efficiently to most architectures. As would Pascal and Modula-2 that inspired it & were safer.

http://brinch-hansen.net/papers/1981b.pdf

https://en.wikipedia.org/wiki/Modula-2



> A contemporary x86, RISC, mixed (ISA + accelerators), or what other CPU? I think CPU is a broad term. :) Anyway, Wikipedia has a detailed write-up that assembly experts can base a comparison on:

Contemporary x86 and RISC CPUs are what I was comparing the PDP-11 instruction set to. I don't see any fundamental differences. Painting with very broad strokes, the PDP11 ISA looks reasonably close to x86. And those minor differences in more modern RISC actually map better to C than the PDP 11 does -- for example, status flags being replaced with jumping based on register contents. Implicit widening to words is a weak mismatch for x86, but it's a pretty good match for modern risc (no need to mask out top bits in registers), etc.

I looked at your links, and I'm still not seeing how other C maps better to a PDP-11 than it does to modern CPUs. The only thing I'm seeing in the pastebin rant is that CPUs are fast enough and memories are big enough today to support more expensive features, which I can agree with.

Again:

> Worked fine for a PDP-11. Yet, forcing its memory model or tradeoffs into a language used on different hardware can cause unnecessary problems.

What parts of its memory model or tradeoffs made it into C? I can't find any specifics that you're basing these claims on, only assertions that it's true.

In fact, the usual complaint associated with C is that it left the memory model so loosely specified -- initially to allow it to match any hardware -- that optimizing compilers can use the looseness to do really strange things to your code.


"The only thing I'm seeing in the pastebin rant is that CPUs are fast enough and memories are big enough today to support more expensive features, which I can agree with."

Fair enough haha. Ok, my memory loss is hurting me on examples. I might have just been the little things adding up. I do recall two from security work: reverse-stack and prefix strings. MULTICS, UNIX's predecessor, had both with significant reliability and security benefits. Reason C had null-terminated strings was PDP-11's hardware and one personal preference/opinion:

"C designer Dennis Ritchie chose to follow the convention of NUL-termination, already established in BCPL, to avoid the limitation on the length of a string caused by holding the count in an 8- or 9-bit slot, and partly because maintaining the count seemed, in his experience, less convenient than using a terminator."

Now, on reverse stack, my memory is cloudy. Common stacks have incoming data flow toward the stack pointer in a way that can clobber it, even leading to hacks. MULTICS had data flow away from the stack pointer with an overflow dropping into newly allocated memory or raising an error. C language (and most) implementations use regular stack. I think it was because PDP hardware expected that with a reverse stack requiring high-penalty indirection. I could be wrong, though. I know a reverse stack on x86 gets a performance penalty and key traits of x86 come from PDP-11. A CISC with reverse stack would have problems with C.

The pointer stuff. Lots of the pointer stuff, esp arrays, comes from efficiency needs for running on a PDP-11. This by itself is why we can't map C easily to safer or high-level hardware. The CPU at crash-safe.org, jop-design.com, and Ten15 VM come to mind. PDP-11 model doesn't support safety/security so neither does C.

These are a few that come to mind that carry over into modern work trying to go against C's momentum. Hardware, software, and compiler work.


> Now, on reverse stack, my memory is cloudy. Common stacks have incoming data flow toward the stack pointer in a way that can clobber it, even leading to hacks.

That's not a restriction of C, but a way to get more out of your memory on a restricted system; If your heap grows up and your stack grows down (or vice versa), then you can keep using growing both until the two meet, at which point you've used all the available memory. However, if they both grow in the same direction, you need to statically decide how much to give each one, which will lead to waste if you're not using much stack or heap:

    [heap-->|           |<--stack]
vs:

    [heap-->|    |stack-->|      ]
But, again, not something that C cares about; you have a number of architectures like Alpha (IIRC) where the program break and the top of stack move in the same direction.


Gotcha. Appreciate the tip.


Can you explain what you mean by a reverse stack? Is that a stack that grows upwards like the heap? Why does this incur a penalty?


I originally learned about it and other issues in a paper by the people (Schell & Karger) that invented INFOSEC:

https://www.acsac.org/2002/papers/classic-multics.pdf

Really old stuff. Relevant quote: "Third, stacks on the Multics processors grew in the positive direction... if you actually accomplished a buffer overflow, you would be overwriting unused stack frames rather than your own return pointer, making exploitation much more difficult."

I can't find the original paper showing the penalty on x86/Linux. However, this one does the same thing for different reasons with many details:

http://babaks.com/files/TechReport07-07.pdf

Key point: "The direction of stack growth is not flexible in hardware and almost all processors only support one direction. For example, in Intel x86 processors, the stack always grows downward and all the stack manipulation instructions such as push and pop are designed for this natural growth."

So, on such an architecture, you can't directly use the stack operations to do the job: must implement extra instructions without hardware acceleration. The stack on x86 is effed-up and insecure by design. If C's stack is fixed, there's still a mismatch between it and x86 ASM. Itanium at least provided stack protection among other security benefits.


Interesting. Thanks for the links. Isn't C's stack pretty much tied to the hardware? Curious what you mean by "If C's stack is fixed"? How could that be implemented, changing the run time?


You change the compiler to emit different things like a reverse stack or whatever your protection model is. Far as implementation, they describe it in p5 (PDF p7) of paper above (not MULTICS paper). It's actually brilliant now that I read it as the naive thing they avoid is, IIRC, what the other academics did on Linux/GCC. The performance overhead hit 10% easily due to x86's stack approach. I think worst-case was even higher. This team effectively tricks the CPU with simple instructions (eg addition/subtraction) without invoking memory to get it to worst-case of 2%. Clever.

Note: I'm not saying this is sufficient to stop stack smashing. Just that reverse stacks are a better idea than the ludicrous concept of making unknown amount and quality of data flow toward the stack pointer. Definitely reduced risk a bit but how much takes more assessment.


Interesting stuff, thanks.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: