This is an archive of the discontinued LLVM Phabricator instance.

Better handling of function calls in SafeStack
AbandonedPublic

Authored by eugenis on Oct 2 2015, 5:25 PM.

Details

Summary

For a call to be safe, require that it does not capture the alloca pointer and also does not write to it.
Special case for memory intrinsics when they are known to be inbounds.

Diff Detail

Repository
rL LLVM

Event Timeline

eugenis updated this revision to Diff 36419.Oct 2 2015, 5:25 PM
eugenis retitled this revision from to Better handling of function calls in SafeStack.
eugenis updated this object.
eugenis added reviewers: majnemer, pcc, dvyukov.
eugenis set the repository for this revision to rL LLVM.
eugenis added a subscriber: llvm-commits.
eugenis updated this revision to Diff 36421.Oct 2 2015, 5:37 PM
dvyukov added inline comments.Oct 4 2015, 5:18 AM
lib/Transforms/Instrumentation/SafeStack.cpp
73

I guess a more correct description is "that are accessed in ways that we can't prove to be safe".

eugenis updated this revision to Diff 36558.Oct 5 2015, 2:37 PM

Added the same logic for plain stores.

eugenis updated this revision to Diff 36559.Oct 5 2015, 2:38 PM
eugenis marked an inline comment as done.
eugenis updated this revision to Diff 36560.Oct 5 2015, 2:39 PM

+ missing test file (store.ll)

dvyukov edited edge metadata.Oct 14 2015, 11:33 AM

I am not qualified to review the gist of this patch.

pcc edited edge metadata.Oct 26 2015, 7:05 PM

I suppose a higher level point is whether it is sufficient to rely only on the readonly attribute. Consider this function:

void *f(void **a, int x) {
  return a[x];
}

The a argument would be readonly and nocapture, but it could leak the address of the safe stack given an appropriate x. We already protect against this kind of thing within a function (via the gep case in SafeStack::IsSafeStackAlloca), but not across function call boundaries.

Maybe we do in fact need something like a global analysis like FunctionAttrs that attaches safestack attributes to parameters.

lib/Transforms/Instrumentation/SafeStack.cpp
238

It occurs to me that this isn't necessarily a valid assumption (consider for example an access with out-of-bounds indices that were constant folded by the optimizer from something that the frontend wasn't smart enough to warn about).

272

This comment should be updated.

281

I would just check for the attribute here. The FunctionAttrs pass should have already set attributes according to AA results.

In D13403#275791, @pcc wrote:

I suppose a higher level point is whether it is sufficient to rely only on the readonly attribute. Consider this function:

It would not protect us from information leaks, but it makes write-overflow impossible at least.

void *f(void **a, int x) {
  return a[x];
}

The a argument would be readonly and nocapture, but it could leak the address of the safe stack given an appropriate x. We already protect against this kind of thing within a function (via the gep case in SafeStack::IsSafeStackAlloca), but not across function call boundaries.

In fact, this is not handled correctly within a function. For example, bitcast to larger type + load are not checked at all.

Maybe we do in fact need something like a global analysis like FunctionAttrs that attaches safestack attributes to parameters.

I think we will need to do something like this in the end, but let's start with something simple. I'll stop relying on "readonly" attribute, keeping just a special case for memory intrinsics.

Would be great if this "safestack" attribute specified the range of memory accesses based on the parameter, so that it could describe memset(), for example.

eugenis abandoned this revision.Nov 11 2015, 5:00 PM

I've rewritten the whole thing using ScalarEvolution. The result looks simpler and is more powerful.
The code is significantly different, so I've uploaded it as a new revision. See D14599.
Closing this one.