Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!watmath!clyde!rutgers!sri-unix!hplabs!hp-pcd!uoregon!omepd!perry From: perry@omepd.UUCP Newsgroups: comp.os.minix Subject: Re: MINIX memory management (sort of) Message-ID: <369@omepd> Date: Sat, 7-Feb-87 02:47:28 EST Article-I.D.: omepd.369 Posted: Sat Feb 7 02:47:28 1987 Date-Received: Mon, 9-Feb-87 03:43:21 EST References: <252@hqda-ai.UUCP> <1169@steinmetz.steinmetz.UUCP> <511@bobkat.UUCP> Sender: news@omepd Reply-To: perry@inteloa.intel.com (Perry The Cynic) Followup-To: comp.os.minix Organization: Intel Corp., Hillsboro Lines: 70 Keywords: 8086 segments hardware protection Summary: 8086 segments are no good hardware protection In article <1169@steinmetz.steinmetz.UUCP> davidsen@kbsvax.UUCP (william E Davidsen) writes about the protection of parallel MINIX processes by using the 8086 segment registers: > >If you allocate a full 64k to data, there is hardware protection: you >can't address more than that. This assures that any program which >doesn't deliberately set out to cause problems will not modify the >code. ... All right. Let's assume that all our little MINIX processes are well-behaved, i.e. they don't touch their segment registers that have been set up by the OS. Let's further assume that we have split I/D space, i.e. that code and data/stack segments do NOT overlap. (If they do, any stray pointer can CHANGE my code into a FAR jump or call... and here goes my protection.) Now the idea of our poor man's hardware protection is that we never write to the code segment, only to the DS/SS/ES segments that are identical. So we can indeed be sure that our process never modifies its code (which is fine because it allows text sharing and simplifies demand paging of code) and likewise never modifies memory in other processes, including the OS. Good. Unfortunately, the stack is part of the data segment. Remember CALLs? The return address INTO THE CODE SEGMENT is written onto the stack and used upon return. If, by accident (read: bug), you smear garbage over your stack, you are liable to clobber some return address. When the return occurs, you will happily jump to a random location in the code segment. This is likely to be the middle of some instruction, or a constant. [You CAN put constants in the code segment and access them with segment overrides as long as you don't need a pointer to them!] Again, this random instruction may accidentally modify my segment registers and clobber whoever happens to be out there. Don't say that's unlikely. Besides the MOVs to segment registers, there are the FAR jump, call and return instructions that all change the code segment, the LDS and LES instructions that change DS/ES (clobber, clobber!) and some others. Consider that at that stage, your program is executing random code, i.e. running around in circles and screaming. When it executes some millions of instructions per second, it has quite a chance to hit one of those critical instructions eventually. (Of course, it can also just hang in a closed loop.) By the way, accidentally hitting a CLI (disable interrupts) instruction kills you too (it disables the clock interrupt that activates the MINIX kernel!). Some other ways how random instructions can disrupt the machine are left as an exercise to the reader. Incidentally, there IS a way out! All we have to do is to separate the stack and data segments. If these two segments do not overlap, bad pointers and our other favorite bugs cannot disrupt the stack. Unfortunately, this means that the stack is outside our 'data address space' and can no longer be used to hold automatic variables. We would have to split the stack into a *control stack* in the stack segment, holding subroutine return information, and a *data stack* that is allocated in the data segment and holds automatic variables. We'd have to use SI or DI as a 'data stack pointer' (BP implies the stack segment, and always using segment overrides is no fun), interfering with their use in string instructions. Processes would become significantly larger (we'd have to allocate 64K for the stack segment, or else a stack overflow would overwrite something) and slower (stack management gets more cumbersome). Note that even with split stacks, we are STILL vulnerable to undisciplined PUSH and POP operations - a PUSH AX followed by a RETN (near return) is the perfect recipe for a runaway program (see above). But that's nothing new - any assembler programmer can screw up MINIX with ease, anyway, and our C compiler just has to keep its PUSHs and POPs straight. Oops! That's gotten longer than I intended. My apologies to those who aren't so interested in the lower pits of the hardware. If you can see where I went wrong, tell me. Flames per mail, please. -- perry ------------------------------------------------------------------------ << Perry The Cynic >> ...!tektronix!ogcvax!omepd!inteloa!perry ...!verdix!omepd!inteloa!perry (Peter Kiehtreiber) -or try- perry@inteloa.intel.com