Sorry about the above reply, I'm a little irritated, but the point remains. Neither of you seem to truly understand the issue.
Typically, buffer overflows don't overwrite code, they overwrite some important data. Program flow control is managed on the stack, in form of return addresses to points where functions were called. Local variables are also stored on stack, and a buffer that gets overflown on stack gets to overwrite the return addresses on stack. There you go, execution jumps to address of your choice. The traditional attack is to send code in the buffer, and then set execution to jump on stack one way or another. Usually there are bytes somewhere in the application that form a jump to stack, so such a position makes great return address. After that, the execution practically goes to the buffer you just sent, and data gets executed. HOWEVER, it's not necessary to jump there! Buffer overflow can still be extremely dangerous even without data execution. Imagine your application has a code that kills some files, databases, or any cleanup that prompts user if he's really sure. Place the return address right after the prompt has been checked, to where actual work gets done, and the buffer overflow will result is serious damage that NX-flags and such can't prevent. Yay!
The above mentioned stack buffer overflow isn't the only form of buffer overflow, a lot of variations exist. Basically, when attacker gets to overwrite some critical data in unexpected way, there is risk of program flow getting fucked up. Now, memory corruption bugs aren't limited to mere buffer overflows, and C has plenty of them. The very way pointers are defined in C invites compiler writers to implement them in a rather vulnerable fashion. Together with the CPUs being such wimps and raw devices, there's a serious problem. The problem can be solved on language level, but it could also be solved on compiler level, and definitely on CPU level. Designing better languages is the most practical way to approach the problem since you can't really change the CPU.
Microsoft's .NET takes approach at this by defining a virtual execution platform, in a form of bytecode, that things get compiled to. On this level, you can have a VM (kinda virtual cpu) level protection, compiler level protection, and language level protection. All of them, and they're all fscking implemented as well. How's that for security?