I've been learning about buffer overflow attacks, where code is written into a buffer on the stack to be subsequently executed by the CPU.
I can't see how this would be possible on the Hack machine. It seems to me that the stack can be used only for manipulating data and that the code fetch-decode-execute cycle is completely isolated from it.
Am I missing something? Or is there some crucial difference between Hack and x86 that allows for code to be run from the stack on the latter but not on the former? If so... what is the difference?
I've actually been looking into this for a little while, but as soon as I posted this question here, I found something that might help explain it...
x86 has the assembly instructions CALL and RET, where Hack only has JMP, JGT, etc. I don't know anything about x86 assembly language, but I'm thinking now that perhaps RET (which I assume to stand for RETURN) fetches a return address FOR THE INSTRUCTION COUNTER from the stack, whereas Hack only ever fetches instruction counter values from inside the instruction memory itself.
It's true that HACK doesn't have call and return, but they can be simulated, as we do when translating the VM code to HACK machine code. Specifically, the jump to address in HACK is always in register A, so one can influence what happens next by somehow inserting a different value there.
But it's true, that buffer overflow attacks don't work well on HACK. Firstly, all the code is in ROM, so one can't change it from within the program. Also, buffer overflow is often used to obtain root (or admin) access to the machine. HACK has only one run level, no storage and no networking, so such attack would be pointless in that platform.
What you are missing is that the Hack is what is known as a Harvard architecture as opposed to the more traditional (for larger systems, anyway) von Neumann architecture.
In a von Neumann architecture, code and data share the same memory and so, at least at some level, there is no distinction between them and you can fill a block of memory with data and then trick the processor into executing it as code (unless the processor has mechanisms to prevent this based on where the block of memory is located). A von Neumann architecture also opens up the world of self-modifying code where programs can alter themselves. This is an amazingly powerful -- and dangerous -- world and there be demons in the shadows. Most people that engage in self-modifying code do so entirely by accident and the results are seldom pretty.
In a Harvard architecture, code and data are in different memories. This has some speed and security benefits, but a lot of power and flexibility is lost. You still see a lot of Harvard architecture parts out there, usually ROM-programmed embedded microcontrollers where they ran a dedicated program that never changed and where efficiency of execution was at a premium because they were clocked at such low rates (often measured in a few kHz). Security was a nice side benefit that is increasingly recognized as being of real value today.