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.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
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". |
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. |
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.
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.
I guess a more correct description is "that are accessed in ways that we can't prove to be safe".