|
Message-ID: <20160927144303.GG19318@brightrain.aerifal.cx> Date: Tue, 27 Sep 2016 10:43:03 -0400 From: Rich Felker <dalias@...c.org> To: "LeMay, Michael" <michael.lemay@...el.com> Cc: "musl@...ts.openwall.com" <musl@...ts.openwall.com> Subject: Re: [RFC] Support for segmentation-hardened SafeStack On Mon, Sep 26, 2016 at 11:05:06PM -0700, LeMay, Michael wrote: > > > On 9/26/2016 11:08, Rich Felker wrote: > >On Mon, Sep 26, 2016 at 10:28:40AM -0700, LeMay, Michael wrote: > .... > >>A salient requirement is that all code that runs while restricted > >>segment limits are in effect for DS and ES must use appropriate > >>segment override prefixes. This is to direct safe stack accesses to > >>SS and thus avoid violating the segment limits on DS and ES. It is > >>still possible to call code that does not satisfy that requirement > >>(I will refer to such functions as "standard functions", in contrast > >>to "segmentation-aware functions") in the same program, but the > >>segment registers would need to be reverted to contain flat segment > >>descriptors before calling such code. Otherwise, a segment limit > >>violation would occur if a standard function attempted to access > >>data on the stack using DS or ES. Of course, reverting to flat > >>segment descriptors would leave the safe stacks unprotected. > >In short, non-safestack code called from safestack code would try to > >store data on the safe stack, which doesn't/can't work. > > > >This could probably be avoided by a different approach that avoids > >call/ret and instead uses manual pushes/pops to the safe stack and > >jumps in place of call/ret. Then %ss could point to the non-safe stack > >so that calling non-safestack code would work fine (but would be > >unprotected as usual). > > > >I suspect this is sufficiently costly to implement (either in > >performance or implementation complexity or both) that you're not > >interested in doing it that way, but I mention it for completeness. > > That's an interesting idea that could offer some benefits as you > described, but it would indeed also have drawbacks. One example > that comes to mind is that to protect the safe stacks, only > instructions that require access to the safe stacks should be able > to use the segment that grants such access. Thus, many instructions > that access data sections and the heap would need to have segment > override prefixes to direct their memory accesses to SS. Using EBP > as the base address for some of those accesses could reduce the need > for segment override prefixes, but I don't know to what extent that If you actually load %ss with the safestack segment, you would not be able to call non-safestack code since it might use addressing forms that immplicitly use %ss. Instead, %fs would have to be loaded with the safestack segment and all jumps for calls and returns would have to use a %fs prefix to load the address. Of course that makes it all the more work to implement (and more costly at runtime). > .... > >>However, there are instances where such writes are > >>necessary. For example, the va_list object used to support variadic > >>arguments stores a pointer to the safe stack. > >Are your "safe stack pointers" just implementation-details like > >va_list state? In that case I think they're valid since they can only > >be accessed via va_arg. But I'm wondering why the argument list is on > >the unsafe stack at all. This mandates that you copy incoming > >(non-variadic) arguments rather than using them in-place, which is > >very expensive for (moderately-)large aggregate-type arguments. > > Arguments, whether variadic or not, are still passed on the main > (safe) stack like usual, and they can be used in-place. Here I think we're just differing on what "used in-place" means. For me that would include the ability to take their addresses. I assume you're just talking about using the values. > >>I implemented other > >>compiler patches to emit the SS segment override prefix when > >>accessing variadic arguments, so storing the safe stack pointer into > >>the va_list object should be allowed. The intraprocedural analysis > >>attempts to detect this type of write, but it currently has > >>limitations on the complexity of pointer computations that it can > >>handle. Thus, I added compiler command line options to selectively > >>override this analysis for certain files and allow safe stack > >>pointers to be written to memory even when the compiler cannot > >>verify that they are being written to va_list objects. > >I don't think they should even be able to _arise_ except as > >variadic-argument pointers. If they can't arise you don't need to > >analyze where they're written. > > I refreshed my memory on what allocations the SafeStack pass moves > to the unsafe stack, and I think you're right. If a pointer to the > safe stack would be written to memory (e.g. into a structure passed > to another function or into a global variable), then the SafeStack > pass moves the allocation to the unsafe stack. Sorry to have > forgotten that when I wrote my previous message. I found just a > couple of exceptions to that rule. Variadic argument handling is > one and register spills are another. This is another place where I think we're just using terms differently. From my perspective (the formal C language) variadic argument handling does not involve taking or dereferencing addresses on the stack; those are just va_list/va_arg implementation details. At the level of the formal language I think there are no exceptions; in all cases where the address on "the stack" leaks outside the scope of what the compiler can see/control, "the stack" it's on has to be the unsafe stack. Rich
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.