Date: Mon, 14 Jan 2002 10:07:55 -0700 From: Earl Boebert Subject: Buffer overflows and other stupidities "I used to be disgusted, now I try to be amused." -- Elvis Costello "What a stupid robot." -- Marvin the Paranoid Android In my view, attempts to close the buffer overflow vulnerability through software or compiler tricks are doomed to one degree of failure or another because you're trying to program around a stupid processor design. Certain contemporary processors actually host a Pantheon of stupidities, consisting of a Greater Stupidity and two handmaiden Lesser Stupidities. Greater Stupidity: Read access implying execute access. Any piece of data that the processor can be tricked into loading into the command register immediately becomes code. This is a stupidity of such breadth and depth that it comes with an event horizon. Lesser Stupidity I: Segmented addressing that isn't. Let's say you have an addressing scheme consisting of segment number plus offset. This raises the question of what to do when, in executing code, block moves, etc., the offset gets counted up to maximum length plus one. Smart answer: take a fault. Dumb answer: set offset to zero and count up one in segment number. Lesser Stupidity II: Brain-dead stack design. If you enumerate the design space of dynamic storage management, you may realize that one actually has to *work* to produce a stack design so dumb that overflow attacks are possible. Here are four classes of designs that are immune to the vulnerability: 1. Descriptor stacks. The only thing that goes in the stack are addresses, preferably with a bounds value attached. Overflow a buffer and at worst you clobber the heap. Penalty: one level of indirection, which (The Horror! The Horror!) may cause your dancing pigs to dance slower than the other guy's. Possibility: can be fitted transparently to existing processor designs, assuming anybody cared. 2. Stack per protection domain. This assumes you can find the perimeters of your protection domains. Also slows down dancing pig displays because of copying parameters from stack to stack. 3. Separate control and data stacks. CALL/RETURN works the control stack, PUSH/POP works the data stack. Doh. 4. Error-checking stacks. A whole raft of options, including "shadow stacks" with checksums, return addresses protected with trap bits, etc. etc. So, if it's all so straightforward and well known, why hasn't some vendor or other fixed it? Answer: the dancing pigs have won. [Ah, yes. Earl is tacitly recalling the good old days of Multics (beginning in 1965) and its progenies (SRI's object-oriented Provably Secure Operating System design 1973--1980, and the Honeywell/Secure Computing Corporation type-enforced systems), all of which took care of this problem and so many others so long ago. But with today's badly designed bloatware, the dancing pigs are increasingly becoming 700-pound porkers that can barely move around the pigsty without massive memory and processing power, and whose pigpen could not even contain them if they were in reality Trojan pigs. PGN]