Hacker News new | past | comments | ask | show | jobs | submit
The original technical debt, which cannot be paid off, was the decision to use a Von Neumann architectures instead of a Harvard architectures for CPUs.
Explain? (Genuine question because I wanna know)
Basically, have an explicit distinction between data and code at the architectural level, to enforce "Write xor Execute" in hardware. In this way, you would prevent e.g. a buffer overflow letting an attacker injecting shellcode that gets executed later by somehow pointing at it. AFAIK the issue would still remain with some programming languages though.
don't we already have the NX bit?

and it's not sufficient anyways. even with NX you could have the attacker control a function pointer you call. in any sufficiently complex program you'll have enough widgets to build anything you want just out of data.

code and data are the same thing. whatever inputs your system receives is "code" that your application interprets, just like your application is the code that the CPU interprets. turtles all the way down.

loading story #50366042
But.. alter data that gets executed (interpeter?), I mean, how does this work even in theory? All data is potentially code?
loading story #50371218
loading story #50366051
and we don't believe a sterilized virtual machine creates a similar situation?

any respective machine doesn't require full Turing completeness?

any ad-hoc machine allows for mutations and flexibility, which I'm sure is an added strength.

2nd edit,

Also, didn't we have a weaker model of this with segmented non-flat memory models on i386-

How do you turn text into code in this architecture? Through the kernel?
loading story #50366774
That won’t help with Lisp programs. ;)
Good point, but not much infrastructure runs on Lisp
This argument is much worse than "we should have made functional programming the standard back in 196X" (which might some merit but overdone imo). The Harvard architecture is just a hardware separation of instructions and data - the argument is virtually an urban legend. Hardwave code/data separations are relatively easy, have existed at time but change nothing since dynamic language just makes its code into data and the self-modification is unhindered.

see: https://www.computer.org/csdl/magazine/an/2022/03/09779481/1...

Separating the call stack (and probably inventing a very specific "the only valid reads/writes in this area are the ones made by the CALL/RET instructions" permission bit for it) from the data stack would make almost all buffer overflows useless for taking over the control flow: the normal STORE instruction simply can't overwrite the return address, period.

Of course, the interpreters would still be vulnerable, but even for them having separate (and distant) stacks make this particular vulnerability much harder to exploit. And again, a generalized technique having different processor instructions for manipulating differently "painted" memory regions would help there too — at least, it would be there so the implementers of the interpreters could reach for it if they decided to.

In the end, of course, nothing can save you from having a program that insists on literally doing eval(fetch(url=input())), or curl "$(read ; printf "%s" "$REPLY")" | "$SHELL", or something else to this effect.

PHP would still be a C interpreter even if you disabled JITs.

Really, we never should have left the Garden of Eden. Things were great there, other than the maternal mortality rate.

Given that there were no mothers in the Garden (other than arguably Adam who gave birth from his rib), the maternal mortality rate was undefined behavior (0/0). Which checks out -- what did they do during all their time in the Garden, anyway? I guess now we know that we cannot know, and that Lilith must have been of the nasal variety.
Modern CPUs already split code and data between two types of L1 caches. So they are technically both "Von Neumann" and "Harvard" simultaneously.

It's also missing the point because modern buffer overflow exploits no longer write code into the stack, they overwrite the return address on the stack and assemble jumps to the end of functions known as "return oriented programming", but the underlying issue still applies to function pointers on the heap.