Canaries and other volatiles do not prevent the overflow; they just try to cope with the consequences of an overflow which has happened. The canary tries to detect the case of an overflow which overwrote the return address in a stack frame. DEP is one step further, it assumes that the return address has been overwritten and followed, and it restricts the areas where execution could jump. ASLR is yet one step further: it "shuffles around" the areas where execution is allowed.
Historically, buffer overflows where exploited to overwrite the return address in the stack, so as to make execution jump into the very data which has been used to overflow the buffer. The canary tries to detect that before jumping, and DEP is used to make the stack space non-executable. DEP also works when overflowing buffers in the heap (the canary is of any use only for stack buffer overflows, but heap can contain buffers as well, and also sensitive data to overwrite, such as pointers to functions -- especially in the context of OOP languages such as C++). To work around DEP and the canary, attackers have begun to look for overflows which allow to overwrite pointers to function, so as to make execution jump into standard library code which is necessarily "there" and also necessarily executable. That's why ASLR was invented: to make such games harder. ASLR can still be defeated by being lucky: since ASLR must maintain page alignment (4 kB on x86), within a not-too-large address space (typically less than 2 GB on 32-bit x86), there are not so many places where the target code may be (at most half a million). Depending on the attack context and how often the attacker's script can try, this can be too low for comfort.
The important theme here is that canaries, DEP and ASLR do not defeat overflows themselves, but target the generic overflow exploit methods which have traditionally been employed. In any application, an overflow which overwrites non-pointer data can be as deadly as a remote shell exploit (e.g., imagine an overflow which modifies a string field called "
authenticated_user_name"). The weapon race between attackers and defenders is becoming too specialized and, in my opinion, increasingly misses the point. On a general basis, it is much better to never allow the overflow to take place, i.e. block/kill the offending process/thread before writing bytes outside of the target buffer. That's what happens with almost any decent programming language (Java, C#, VB.NET, Python, Ruby, Node.js, OCaml, PHP... the choice is large).