Are Buffer Overflow Sploits Intel's Fault?
Bruce Perens submitted a story he wrote for his website on overflows and who's fault they are. I'm pretty skeptical of almost every point raised in this story, but it's an interesting read. [Updated 21:13 by t] As Sea Monkey points out, Bruce has now taken down the article, with a brief note: "I've withdrawn this article after enough people convinced me that I didn't know what I was talking about. It happens sometimes. Thanks." What if everyone displayed such grace?
I disagree with this, making it the developers responsibility to write bounds checking code every time they deal with input is why we are in this mess today. Not a week goes by without annother buffer overflow sob story on Bugtraq. Asking software developers never to make any mistakes, ever, is not a realistic solution and assigning blame isn't going to make the problem go away.
There are a few possible solutions, none of them really easy.
Buffer overflows and other common security problems have been with us for over thirty years and still aren't in the "solved problems" bin. This is inexcusable. If people are going to rely on computers in their daily lives, the computer have to be reliable and having the possibility of security comprimise using 30 year old techniques does not a reliable computer make.
-- Remember: Wherever you go, there you are!
Hrm, interesting. Do you have a *real* Deja link?
At least gcc's features are open --- If people are worried about gcc accepting non-standards-conforming code, why not hack it into -pedantic or -ansi themselves and release a patch?
And to help half-wits like us still do something useful with software, it might be nice to give us tools we don't hurt ourselves with too much. You know, scissors instead of a Samurai sword. A Toyota Camry instead of a Formula 1 race car. 110V household current instead of 50KV "professional" power.
The fact is, C/C++ is too powerful for me. Oh, I understand it just fine, and with enough time and effort, I can make something work semi-reliably in it. But what's the benefit I get for that self-flagellation? Should I spend all that extra time because finding the bugs "hurts so good"?
The fact is: writing code in C/C++ is a lot of unnecessary work. The only reason why people put up with it is because everybody else does it, so it's the path of least resistance if you want to use the "standard" compiler on your platform, use a language other people are likely to understand, and use other people's C/C++ libraries. But make no mistake about it: C/C++ is successful these days in spite of its cumbersome design, not because of it.
And in such systems, you pay a lot of overhead if you try to generate code at runtime, because it always involves a system call. That may be fine for 1960's style COBOL and C/C++ code, but it is not acceptable for 2000's style languages and programming.
Buffer exploits can be be solved by more liberal use of segments. You can have one segment for VM from 0-2G, which is Ex/Re, and another for VM from 2-4G, which is Re/Wr. The problem is that OS'es like Windows and Linux are very primitive, and just use four segments, code and data flat selectors for each privilege level. If this scheme was used, buffer overflow exploits would be literally impossible, but it requires advanced OS'es which have not been developed yet.
It is also the fault of the language. For its first 10 years (when it did not have C), there was literally never a buffer overflow bug on any program in VMS. For example, the internet worm exploited only Unix hosts, and couldn't do anything to VMS hosts. However, in the last 10 years, VMS has adopted more C programs, and the number of buffer overflow exploits has risen from 0 to a siliar amount on Unix. The string and array methodologies under C are incredibly fragile and primitive (as well as very low performance - you need to access byte at a time, which is VERY SLOW on modern architectures), and more advanced languages have much more high performance and more secure methodologies of dealing with data.
C++ is type safe unless you use constructs which make it non-typesafe. As long as you don't use those constructs, like casting, it's a typesafe language.
'' Oh? I'm fairly sure machine code is /also/ "unsafe", and that's what your pretty source code ends up as. How do you prove that your oh-so-wonderful language is still safe when rendered into raw machine code? ''
I'm sure you're thinking that you've got me beat, but in fact this is a great question!
The old, less-satisfying answer is that compilers are less likely to have bugs than the programs they're given. This is probably true (I don't recall any exploits due to compiler bugs, though to be fair I do recall some Java VM exploits).
The new exciting answer is: We use a type-safe subset of the target machine's assembly code.
In our TILT compiler for instance, we take ML code and put it through a series of transformations. At each transformation or optimization we also translate the types (a static proof that the program can't crash) until we get to machine language. This catches a lot of compiler bugs, and helps propagate safety properties to the raw code.
The result is that we have machine language which can pretty easily be checked for type-safety. This allows us to do some other cool things, like ship the proof along with the raw machine code, to be executed on someone else's machine. They don't have to trust us (just the proof), and it doesn't suffer sandboxing costs like java. Wow! Read about Proof Carrying Code .
The second answer isn't really usable today, except in theory. The first is absolutely practical, though. (even if it fails due to compiler bugs, we'd still cut down on a high percentage of errors -- and we'd only need to fix bugs in one place).
The same is true for Modula-3, Oberon, Sather, Eiffel, and other languages. Yes, they don't have the support or user community that C has, but on technical grounds alone, they are fast, small, have no overhead, etc.
Very interesting post this one of yours, I could agree with you 100% in the first line, and gradually go down to 0% on the last line.
If I read you correctly, the burden to avoid buffer overflows should be put on the programmer, not on the hardware or on the machine. Then you start advocating that the compiler/library should take care of this! Isn't that like just laying the safety net a couple feet higher?
As a general rule, I think dumb tasks should be left for the machine, the noble ones for the programmer. Checking whether a given input validation is a potential door for an exploit is the programmer's responsibility.
Another thing that you seem to suggest is that quick and dirty test code should be the basis for production code, thus holes could migrate to the final product. I think the ideal solution is to discard test code altogether and start from scratch, but who does that, right? One other approach, which I actually use and believe many do, is to check for errors (at least return values) from the very beginning, so as those parts which will inevitably be cut and pasted to the production source are structured in a way that adding extra checks would be much easier (favouring using heap memory, encasing calls in try/catch blocks in C++, for example)
Just my $0,02
You can write object oriented code in any language you like. You can write procedural code in any language you like. Object oriented langauges simply facilitate the use of object orientation by providing you with tools that take advantage of it. You can write oo basic, and you can write procedural java (just make everything static and put it in your main class). The paradigm is not being forced on you. You simply ignore some of the features. But don't take my word for it, look at the tour if C++ in Starstroup's The C++ Programming Language.
Polymorphism, encapsulation, and inheritance are simply properties that fall out of the OO paradigm and are implemented to take advantage of it of its implications. Ultimately the right tool for the job in any given case may or may not implement these.
--locust
Buffer overflows are the fault of the LANGUAGE. Important system utilities need to be written in bounds-checked languages. Some compilers, no matter the architecture, will write executable code on the stack: "trampolines". Unfortunatly, this is common enough that the OS can't blindly turn off the executable bit on the stack pages. And non-executable stack pages don't stop all buffer overflow attacks, they just require a 2 part attack: A heap buffer to write the code, and a stack buffer to overwrite the return address. The heap buffer doesn't necessarily even need to be overflown, the attacker just needs to be able to deduce the address. And one can't set heap-addresses to be nonexecutable, simply because there are MANY language environments which do create code at runtime, such as interpreters, JITs, etc etc etc.
Nicholas C Weaver
nweaver@cs.berkeley.edu
Test your net with Netalyzr
Not if you want to parse command lines, for example, or use many of the C library functions (e.g., printf). Of course, in your own, 100% new code, you can use STL and stuff like that, but when you're interfacing with existing code or trying to use (a few of) the language's features, you have to use the unsafe constructs at least a little. That's particularly true when it comes to user input, which is (naturally) where buffer overflows come from.
--
-jacob
-jacob
Everything that you said about C applies to C++ (except for being forced to use it under UNIX!)
A lot of (bad?) programmers do a lot of unnecessary work with their strings because they don't know whether they need to be copied or not, so they always do it. C++ offers string classes that make this irrelevant. Reference counting leading to copy-on-write ensures much greater simplicity and often better performance. A decent string class implemented using a bridge pattern will only be 4 bytes (same as a char*) - it will just consist of a pointer to its implementation and so it can passed around on the stack very quickly. They also have the benefit of being guaranteed '\0' termination. Much easier to handle embedded NULs too.
First, it's not because of the CPU. Hell, the first well-known stack 'sploit was in the RTM worm, which worked for two flavors of Unix on a VAX cpu.
It's because MacOS uses one big-ass shared memory space to run everything in that it's safe from being taken over by buffer overflows. Well, gee, if it's all unprotected, why is it so safe? Because while you can still crash a program with a buffer overflow, you can't predict the stack address. And the critical part of a stack overflow exploit is to get the program counter pointing to the exploit code on the stack.
And even if you could, what would you do with it? There's no shell (at least not until OS X, but that's a completely different OS) to give commands, and no root privs to exploit (actually you are "root" at all times!)
Intel is relatively low on the fault scale here. A bigger problem is the number of people running Linux distros with the same binaries in them. If you compile your own code, the stack addresses will be less predictable (though not completely unpredicatable), and you'll be in the same boat as MacOS: without a predictable stack address, there's no way to run the 'sploit code!
If we simply had more people compile code their own binaries, the problem would be reduced.
But at heart, the fault is one of languages that let you stick things into memory without any sort of range checking. Get too much data or lose the null terminator from a C string and your stack is toast.
And most of these problems happen inside of a library routine. But you can't blame the library routine when it has no way to know the size of the destination buffer. The best it can do is know where the frame pointer is and to not write past it.
If C strings were more than just bare buffers with only a lone null to save you from oblivion, the library routines could be smart enough to save your ass. So I blame C and its strings as the primary problem causing buffer overflow exploits.
Use a language with internally checked datatypes and no bare pointers like Java or Perl, and this type of exploit will go away.
--
"Open source is good." - Steve Jobs
"Open source is evil." - Microsoft
If C strings were more than just bare buffers with only a lone null to save you from oblivion, the library routines could be smart enough to save your ass. So I blame C and its strings as the primary problem causing buffer overflow exploits.
Use a language with internally checked datatypes and no bare pointers like Java or Perl, and this type of exploit will go away.
Programmers writing SUID programs should also be capable of using pointers without creating buffer overflow exploits.
Or do you think the problems with buffer overflows outweigh the potential gain from using pointers in the first place ??
I strongly prefer the additional power of constructs in C that are provided by pointers. I do not think that a higher level language is likely to be any safer. Sure, the language may conceptually be without overflows, but the increased size of the compilers/interpreters makes those much more difficult to check, and still prone to overflow.
An AC says:
"duh, this one is really easy to solve. wrap delete with an inline function that checks if the pointer is NULL, deletes, then sets it to null.
no more multi delete problems. (of course, you really should fix the bug that causes you to delete the pointer twice, but you can't really blame the language for your own ineptitude...) "
I boggle, and respond:
Wow, now THERE is a severely misinformed post.
#1. I'm contending that we use more advanced languages because they make life easier for both good and bad programmers, not merely compensate for "ineptitude".
#2. Your solution is 100% wrong.
C * a = new C();
C * b = a;
delete a;
delete b;
'b' is a different memory location, which isn't set 0 when 'a' is deleted. Unless you're implicitly suggesting that we move to memory handles (which is going to give you MUCH worse performance than modern languages which just don't let you write this kind of program), your solution doesn't solve anything at all.
So having separate address spaces for code and data might cause problems. self-modifying code still exists, etc.
But the cool thing is -- you can just make the page tables regarding the code address spaces point at the same pages as the data (and vice versa), and do this at page granularity. So you can make an 8k buffer that is read/write/execute (but accessed with different linear addresses when used as code or data) for your genetic programming, but keep the rest of your program safe.
VM tricks are so much fun. ^_^
The enemies of Democracy are
What if everyone displayed such grace?
Or what if everyone bothered to do some research before writing and self-promoting some inane rant.
If you used the segment registers, the result was basically a highly non-linear address space. In a lot of ways, it was an 8 bit processor with 16 bit registers and hardware bank switching (for those of you that remember bank switching).
as a result, there were a few 'standard' memory models that programmers used:
- Small address space: All segment registers the same. don't touch them. This gave you a flat 16bit (64k)address space, turning the machine into a glorified 8085/Z80 -- almost completely source code (assembler!) compatible. It also gave a slight speed advantage, since all pointers and integers were 16 bits wide.
- Intermediate address space: segment registers point to disjoint spaces. not too much difference but you get some breathing space since the code and data don't share the same (tiny!) 64K address space.. pointers are still 16 bits, but you now have to remember which segment you're talking to.
- 'large' address space: all pointers are 32 bits wide. (include both segment registers and then pointers within the segments). This gives you access to the full 1M address space. (the 640K limit was because 380K was reserved for I/O space).
The 80286 allowed people to break the 1M barrier without doing bank switching (EMS?), but it turned the segment register/pointer problem into a serious horror story. Unless you were seriously masochistic (or just plain desperate) you just made it look like an 8086 that ran a bit faster.SERIOUS performance hit. If you allow arrays >64K then just about every array access requires you to calculate and load the segment register. address math sucks because if you have two 32 bit addresses A and B, A != B does not necessarily mean that they don't point to the same memory, and *X++ can require some serious work to do the exepected thing.
When they came out with the '386 you now had segments of 4GB each. This was at a time when a 2GB ram module could have been camouflaged as a desk and would have required a 15KW watt power supply.
Most programmers and OS designers just set all the segment registers the same (the '386 equivalent of the 'small memory model', and forget about them (I called this traumatic amnesia).
So, yes: Intel has a Segment model that could be used to provide security, but few people are brave/stupid enough to risk the horror stories/ flashbacks that enabling it might entail.
Intel: Just short of intelligent.
Free Software: Like love, it grows best when given away.
Yes, C++ is somewhat better than C because it does allow you to build abstractions that perform more checking and automatic resource management. But even if you do that C++ is fundamentally unsafe. Why?
C++ still uses the C pointer model and adds a similar reference model. And C++ still uses manual memory management for dynamic allocation. You cannot, in general, address those problems by creating safe abstractions. If you try, you end up severely limiting language semantics, and as soon as you face any outside library, you have to convert to raw pointers anyway.
And C++ still does not guarantee fault isolation among modules or any way of determining from the source code of a module whether that module is safe or not. That is, any piece of code you link with can cause arbitrary problems in any other piece of code, and you have no way of telling. Perhaps you think that's inevitable, but it is not. None of the other languages that I mentioned have that misfeature.
Arguing that one should not bother fixing those problems because there are lots of other ways in which people can make mistakes is wrong. The problems C/C++ creates for programmers are easily avoided, without performance penalty or other drawbacks. A day lost trying to chase some avoidable pointer bug in a C/C++ program is a day that could have been spent on testing and fixing some conceptual security bug.
I have been using C for 20 years and C++ since before its first public release (nearly 15 years?). I still use them a lot because that's what interfaces best with the software that's out there. At the time, they were reasonably good tradeoffs. But this is the year 2000, and tradeoffs that were good then are not good anymore.
Hey dudes! I'm kinda new to this slashdot thingy here, but could some sweet, loving guy please explain the buffer overflow to me. It seems like a pretty cool idea, is it like sorta like going out with too many guys or something? help me please!
Oops...I did it again tour info, bio, and more!
So, if you don't use pointers, address-of, array subscripting, call-by-reference, or any library functions that do, yes, then you can write type-safe C++ programs. Too bad that you also can't do much in that subset of C++.
There is no problem with providing those unsafe constructs in a systems programming language. In fact, lots of languages do, just like C/C++. The problem with C/C++ is that the safe constructs and the unsafe constructs are indistinguishable, and that means that even the 99.9% of a program that can be written nicely with the safe constructs use the unsafe ones, and as a result are much more likely to crash.
The underlying problem is that C/C++/Objective-C do not have mechanisms to protect against these kinds of problems. In fact, it's impossible to write substantial programs in those languages that use only "safe" constructs. This is a peculiar and fundamental bug in the C-family language design.
There are excellent alternatives around. Modula-3, Oberon, Ada, Sather, and Eiffel all have efficient, free, open source implementations around, they all provide access to unsafe features when needed, and one of them should satisfy anybody's programming needs. Java is an excellent applications and server programming language, although it has a bit more overhead and no access to low-level features.
So, folks, get with the program and stop writing servers and other applications in C/C++.
This is a very old debate, and it's been raised on the kernel list several times. The problem is that it seems pretty clear that given a buffer overrun attack which can be exploitable without the stack-exec patch, it's possible to transform that attack into an exploit which will work with the stack-exec patch present.
It may require more work to create the exploit, but it's the sort of thing which only one person needs to do and then share with 100,000 of his best friends on some cracker web site. Hence, such a patch only provides the illusion of security, and it adds crap to the kernel. (There's all sorts of kludges you have to put in there to make sure that trampoline code doesn't break, etc., etc.)
The problem isn't Intels fault because the arch has an execute bit in the segments. The original idea was you put your code in a separate code segment from your stack and data segments. The real problem is OS designers who for various reasons decide that the x86 arch's segmentation should be ignored and set the code segments equal in size to the data segments and stack segments. It then becomes a simple matter to just jump into the data or stack segment and begin executing code.
Of course since most of the OS's don't properly use the protection mechanisms Intel has provided, I guess it becomes Intels fault if they don't extend the arch to support a feature and potentially break downward compatibility with other OS's using the current paging system.
In C++, this might look like:
char *p = unsafe::allocate(char,100);
char c = unsafe::ref(p,10);
float f = unsafe::castref(float,p,0);
Of course, C++ would also need to eliminate the unsafe constructs it has outside namespace "unsafe", and in some cases add safe constructs to replace them. Then, you could limit the use of unsafe constructs to only the few places where you actually need them. That greatly reduces the probability of making errors. Languages that do this exist: Modula-3, Oberon, and others. There are no such languages in widespread use yet that look like C or C++, unfortunately.
Blame the language! C and C++ continue to be inappropriate for security-critical work.
Aside from speed-critical stuff like kernels and Quake 3, I don't see the need to write programs in C and C++ any more.
Let's start using modern languages with type safety. They're easier to write programs in (because debugging is easier) and not that slow.
I know that I'd gladly take the 2x speed hit on my security-critical apps (mail daemon, web server, ssh, etc.) to know that they cannot have this kind of bug in them, because they were written in a language like ML, Eiffel, Haskell, or even Java.
struct Cast { int x[1]; float y; };
int float_bits_as_int(float f) {
Cast c;
c.y = f;
return x[1];
}
Here is another example:
int float_bits_as_int(float f) {
float *p = new float;
*p = f;
delete p;
int *ip = new int;
int v = *ip;
delete ip;
return v;
}
These "logic errors" are related to type errors: they allow the bits of an object of one type to be interpreted as the bits of an object of another type. A system that's type safe guarantees that that doesn't happen.
In any case "type safety" doesn't just mean compile time type safety. Java has a lot of runtime type safety, where type errors are caught at runtime, not by the compiler. That's still fine for many purposes. C++ has neither.
I wouldn't blame the intel architecture, but..
There are architectures (Gould/SEL-32/xx is one) that allow for and, in some cases, insist on strict divisions of code and data pages. The code sections are read only, and will generate a fault if an attempt to write to it occurs from a non-system level.
The data sections are read/write, but you cannot branch there.
It makes it a bit difficult to write self-modifying code, but not impossible if you really need to.
---
Interested in the Colorado Lottery?
Interested in the Colorado Lottery or Powerball games?
check out http://colotto.com
The article putes quotes around the words "code" and "data" and that is the problem. In i386, CODE segments can either by read/execute or execute only. DATA segments can either by read/write or read only - not execute.
So what is an Intel-baesd, flat-mode program to do? It sets up two segments - one data, one code - pointing to the same memory. Goodbye hardware security.
Of course the VM doesn't protect against execution - that is the segmentation system's job. Linux (and anything else that assumes a 68k or VAX flat address space) just blows it off.
Simple solution - bring back seperate code and data. Excuse me, I and D. Just like the PDP-11 UNIX grew up on.
Thanks for that.
They need to be deprecated more forcefully. All the unsafe functions should be pulled from the standard C library and moved to something like "deprecated_unsafe_library.h". All set-UID programs need to be purged of those functions. Now. Any manufacturer shipping a system with those functions in a security-critical program should be sued for gross negligence.
Here's a pretty good description of how to write a buffer overflow: ftp://ftp.technotronic.com/rfc/phrack49-14.txt Jim
Intel's 386 protected mode has "code" segments and "data" segments. Writes to code cause a segfault. Branches into data cause a segfault. But if the OS points the code and data segments at the same area of RAM, the 386 doesn't care.
<O
( \
XGNOME vs. KDE: the game!
Will I retire or break 10K?
The Tao of Buffers
Escapes most but plagues many.
Has Intel caused this?
If you don't like C, fine. But I do like C and I use C. Again, fine. Different code for different folks. Where you're wrong is making a blanket statement as to why people use C. I won't deny that you could find people who actually believe that reason. I use C for entirely different reasons. But I won't tell you what the reasons are, because I'm not flaming you about specific reasons; I'm flaming you for generalizing totally inappropriately.
now we need to go OSS in diesel cars
Meanwhile, does anyone remember the IAPX 432? It was a flop for several reasons: ADA flopped, its performance was bad, it was a real departure from the architectures of the day. But it had some real innovations - every function ran in its own protected space, using message passing for communication, and your program could protect itself from itself. Is it time to revisit that sort of architecture?
Thanks
Bruce
Bruce Perens.
Crispin
-----
Immunix: Free Hardened Linux
Chief Scientist, WireX
As I said myself, those languages have smaller user communities, and that means they have fewer libraries and tools for them. But you can call C and C++ code from them and they are quiet usable, in particular on Linux.
I don't recommend doing everything in them, but give them a try for some projects, in particular open source projects for Linux. That's the only way this chicken-and-egg problem of moving beyond C++ will get addressed.
As someone who sees attemplts against his own system on IRC every day and sees newbies announce to the world at general "Hi I'm running wu-ftpd on Redhat 6.0 and ARRGHH what the hell just happened?" who's this guy on my box!?" I decided to investigate the feasability of providing safe versions of commonly run services.
Libsafe is quite good but cant catch everything and breaks quite a few programs if you set it up in ld.so.preload.
The Stackguard compiler is definitely more robust and seems to work well during the course of my tests.
I've prepared RPMS of BIND8.2.2pl5 and Wu-FTPD 2.6.1 with Stackgaurd 2.0 Stout for RH Linux 6.2.
As I've just prepared the rpms this week on my Slackware 7.1 system I dont know how well they perform as they haven't recieved a great deal of testing (No bug reports so far tho)
The RPMS are available at http://indigo.ie/~fowler/ELSL/ with more daemons to come soon and hopefully DEBS. Try them out and mail me with any problems (My email address can be read from the reuslts of rpm -qpi file.rpm)
Good luck and keep safe on the net
Gnubie_ Efnet #Linux
What about that huge chunk of interpreter, written in C or C++? Have you audited that, too?
In a higher-level language, the simplest code can have side effects that might provide a security hole, so to reassure yourself you're going to effectively have to audit the behaviour of the interpretation of your program, not just the program itself. In C, at least, you know when you're making a function call, and you can be reasonably confident everything your program does is done explicitly rather than being hidden.
I went to a rather informative lecture by a person whose business is selling security services and also works on OpenBSD. His view was that C programs, calling a minimal set of libraries (excluding GUI libraries, amongst others), are the only things that should ever be suid root.
Any sufficiently advanced technology is indistinguishable from a rigged demo
--Andy Finkel (J. Klass?)
Don't blame the hardware, don't blame the language, blame the programmer. Relying on the hardware to fix bad programming style is like a parachutist relying on a safety net.
Any input operation that overwrites memory it is not supposed to, is bad programming style. Ideally the programmer does not know what hardware their code will run on, maybe it will be a flat memory machine with 0 memory managment hardware.
In C scanf("%s",foo) is nice and handy for little programs. But it is not production level code. Production level code should instead always use limited length routines. So it is a little harder, maybe the first implementation has to be audited to remove the screw ups. This is like checking the return values on printf and scanf nobody does in the test code, but damn well better be done in the final code.
Programmers need to limit themselves to limited input routines or at the start of the project development build a little library of limited input routines.
What someone really needs to do is come up with a "no-overrun libc" that does not included any unlimited input functions and spits horrible messages whenever a standard input function is called with arguments allowing unlimited input.
You link and run development code against this library and fix any place where it screams.
I really am suprised big development houses don't do anything like this. But of course no one has time to do it right.
Very possibly... before the iAPX432 came out (I still own the original architecture manual), there was the Burrough x700 mainframe architecture. It had 48-bit words, each with a 3-bit tag... one of the tag values was reserved for executable code. It also used an indirect base+displacement pointer architecture which allowed hardware bounds checking.
I did quite a lot of kernel hacking on that as we had the full source code. Unless you did something stupid with the process dispatcher, it was impossible to overwrite memory or hang the system.
Elliott Organick wrote an excellent book on the architecture. I think with some modifications this would make an excellent (and fast!) Java machine...
Lets say you have three variables that define the properties of some object and you have two operations (methods) on those three variables that are then done to/performed on that object. You then stipulate that no access will be made to those properties, except through your two methods. Now when you write the rest of the code you think of these three properties and two methods as one thing.
This is a not as precise an answer as I would prefer to give. The problem being that it all has to do with what's going on in your head as the programmer.
--locust
Ick. That's just the sort of mundane task I want a compiler for. As a programmer, I already have too much to worry about -- bounds checking is one simple task that I'd just as soon have the compiler do.
In most cases, the bounds check can be hoisted out of loops, so there's almost no overhead. In a perfect world, I'd like to see a compiler that, when given a high enough warning level, warns that it can't hoist bounds checks.
Blame the developer!
Sure, some operating systems or languages or chips hold the coder's hand and make some dangerous things impossible or difficult to do.
It's still the programmer's fault for not knowing what the (void*) they're doing.
This is the same argument as "C++ is slow!" It's only slow if you don't bother to learn what code a C++ compiler generates, using lots of mechanisms without realizing it. C++ implements its mechanisms as tightly as it can, but every mechanism you use takes some time to operate.
Back to buffer overrun security: If you are gonna accept data from an untrusted source, why are you (1) putting it on the must-be-kept-inviolate stack, (2) not doing everything in your power to accept no more than n bytes that have been allocated?
If the compiler docs specifically say "data in auto variables will never be put into an executable address space," and it does, then it's time to fix the compiler or docs. Likewise if the docs belie the behavior of a chip, time to fix the chip or docs.
Don't blame a microprocessor for your mess. Don't blame a language for your mess.
You have only yourself to blame.
[
Try this hypothetical: what if, instead of doing public speeches, polticians took to publishing their opinions in articles on the web? That way, if anything they say produces a bad reaction, they can just edit it away, and no one will be able to figure out what the complaints were about. Very convienient, eh?
My take: If you publish an article, and then later recant, the thing to do is to add a link at the top pointing to your later thoughts on the subject.
the Harvard architecture.
There's a separate data bus, and a separate instruction bus. I don't think it is strictly required to have a separate data memory area and a separate instruction memory area, but I think it's usually implemented that way. There are a number of microcontrollers that use this architecture, storing the program in a ROM and accessing a RAM chip for scratchpad area.
If tits were wings it'd be flying around.
Why?
Because a lot of people are forced to program in C...
I'm not a fan of C, the library has some horrid things (like routines without buffer-overrun checking), and the language is very low level. But when working with other people, sometimes C is a necessary evil
What to do? You can get some higher level programming using BetterC, a C library that gives you Eiffel-like exception checking with a minimum efficience penalty, and without leaving your favorite C compiler.
It's my Nirvana, I don't use debuggers anymore...
Lest you be confused by the +1 funny on my post, let me say that I am not joking.
2x slower is the most conservative estimate for the speed of modern safe languages against C code. (In practice I've seen much better. Does anyone trust benchmarks?) My point is, even if it is 2X slower, I'll gladly take it and sleep a little more soundly at night knowing that my linux box isn't being hacked due to 20 year-old issues. 99% of my box's CPU time is spent at Nice -19 trying to find big primes for the GIMPS project.
Modern languages (take java if OO is your thing, but there are more intersting languages around) have SOLVED this problem with buffer checking (or static proofs that checking isn't needed). Without having to worry about this type of common security hole, programmers can spend more time on things we REALLY need: documentation, maintainable code, asymptotic speed increases, and the other possible security holes (ie, not escaping shell metacharacters in user input).
See my thread on Functional Languages for what I think is a convincing argument about modern typed languages in general. I know my position is extreme, but that doesn't make it a joke.
http://slashdot.org/comments.pl?sid=00/07/01/23
a non-executable stack does nothing, you just return either into your data segment or into libc. this has all been hashed out before on various mailing lists. all of these patches only disable a particular method of exploitation, but the overflow still exists to be exploited in some other way!
this is security through obscurity, plain and simple.
Like a system, and langauge can be as secure or insecure as you can make it. One can write an extremely tight program in C++ while writing one in Perl or Java that leaves gaping security holes open.
This statement troubles me. C/C++ addict who have little exposure to other languages have little knowledge of what they're missing.
_Many_ (if not most?) security attacks involve buffer overflows. You have to _work_ and _think_ to free yourself of buffer overflows in C/C++. In other languages, this protection comes for free.
Yes, it's possible to make a secure program in C/C++. But it's just a hell of a lot easier in bounds-checking languages.
So there.
Why wouldn't the stack-exec patch do what it was supposed to do and prevent any buffer overrun attack?
The only thing changed is the strategy of the overrun. The attack on a non-exec is accomplished by overruning the buffer so that you set up the parameters for a desirable syscall (exec /bin/sh comes to mind) and adjust the return address on the stack to point to a syscall in the daemon's code (best place is in libc somewhere nearly everything will have that linked in). Now, the function returns and your syscall executes just fine in the code segmnent.
It's not so much like putting a stronger door in as it is putting in a lock that turns the other way. A moment of confusion is all you will create. Soon, lock pickers will be wise to the trick and wont even be inconvienianced.