|
Message-ID: <64befac3-af89-611a-70d1-9838c91097b3@intel.com> Date: Mon, 26 Sep 2016 23:05:06 -0700 From: "LeMay, Michael" <michael.lemay@...el.com> To: Rich Felker <dalias@...c.org> Cc: "musl@...ts.openwall.com" <musl@...ts.openwall.com> Subject: Re: [RFC] Support for segmentation-hardened SafeStack 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 would help. A more fundamental challenge is that not adhering to the longstanding assumption that the stack resides in SS would probably necessitate major compiler revisions. Another challenge is that standard functions would expect parameters to be passed on the stack pointed to by ESP, whereas some other register would be used as the safe stack pointer in this case. ... >> 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. >> 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. Additional exceptions exist for certain function calls when segmentation-based hardening is disabled, but that's out of scope for this discussion. So, I agree that the verification that I described above is unnecessary. However, I still need to track pointers to the safe stack in registers when they get spilled and filled so that I can emit the proper segment override prefixes. > >> Note that manipulating safe stack addresses in registers within a >> single function is no problem. For example, traverses_stack_p in >> expand_heap.c compares a safe stack address to other addresses. >> Actually, traverses_stack_p is interesting in other ways, since it >> assumes that libc.auxv is on the main-thread stack. My revised >> patches move auxv to the main-thread unsafe stack. What checks >> should be performed in traverses_stack_p when multiple types of >> stacks are defined? > It can be omitted completely for safestack. It's just a fail-safe > workaround for buggy kernels that let you grow the brk across the > stack which presumably you don't have on x86 systems. I'll revise my patches to reflect this. Thanks, Michael
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.