Buffer overruns occur when a program allows writing more data to a buffer than it was allocated to hold. This can lead to code injection attacks by overwriting memory addresses like return addresses on the stack. Common types of buffer overruns include stack overruns, heap overruns, array indexing errors, and format string bugs. Developers can prevent buffer overruns by carefully handling untrusted user input, using bounds-checked functions, and compiling with protections like GS flags.