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.
There is no fundamental distinction between code and data. This separation does not exist as category in actual reality (and I vividly remember that being one of the bigger points GEB made, early on). They're fundamentally one and the same, and classifying some bits as data and some as code is a point of view that really depends on what you're doing and why.
As for Harvard architecture - every time you branch on your "data", you're effectively executing it, and your "code" can be seen as emulating a system where your "data" is the actual code.
Says you. I wrote a program for a Hardvard architecture chip (PIC) that read bytecode from the data memory and executed it. Updates were made by replacing the data block with a new data block.
In practice, there is no way to separate data and instructions. After all, JS downloaded inside a webpage is literally data, and it gets executed.
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-
That doesn't even make sense, to be honest. Interpreted languages like Python aren't running machine code. They are taking data and executing machine code based on the data. Is Python safe?
It's not that hard to imagine that in a specific embedded device the kernel / compiler have access to write to the instruction memory space while user space programs don't have any API / permissions to do that.
It also doesn't solve one important attack vector: the existing executable has logic that can be used maliciously (e.g. a function that sends a file to the internet), and this logic is executed based on data on memory. In this case you only need to manipulate the data, without manipulating the "code", in order to exploit it.
I'm not sure I understand why would you need special hardware for that.
I think that the principle of "code is data" is that data influences the algorithms either way, so there is not much of a point in separating the two.
In practice, it's usually possible to write to the embedded flash memory via software, but it's much more difficult and disruptive than a simple CPU memory access.
I don't know any details about Python's architecture, but it's totally possible to write an interpreter for machine code; that's basically what an emulator is. The trade off is that pure interpreters tend to be very slow.
Uh? It would work via a file - as AOT.
see: https://www.computer.org/csdl/magazine/an/2022/03/09779481/1...
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.
Really, we never should have left the Garden of Eden. Things were great there, other than the maternal mortality rate.
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.