Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
With alloca() and the parser taint code in place, we can handle storing and loading alloca()ed values from variables. We detect these easily enough: stores from nodes with DT_NF_ALLOCA turned on, to identifiers with DT_IDFLG_ALLOCA turned on are derived from alloca() and need reduction to scalar offsets via subtraction of the DCTX_SCRATCHMEM value; loads from identifiers with DT_IDFLG_ALLOCA turned on need addition again. The store case is fairly easy (a straight subtraction, hanging on to the un-subtracted value for use as the return value of the allocation itself), with one wrinkle: we detect stores from nodes with DT_NF_NONALLOCA turned on, and complain if both DT_NF_ALLOCA and DT_NF_NONALLOCA are simultaneously active, because this indicates that someone is reusing a variable used to store alloca()ed pointers for non-alloca purposes too. We can't have that, since we are modifying the variable at both load and store time! The load case is trickier, and is pushed into a new dt_cg_load_alloca function to keep it out of the way. Converting the offset back into a map_value pointer requires quite a few contortions: we have to bounds-check everything because most of the values we need start out as scalars, but we're trying to add them to a map_value... but the verifier only uses conditionals to adjust its idea of bounds if the conditional is == or != (not useful for us), or if one side of the conditional is a constant. So we have to come up with constant upper bounds for things to satisfy the verifier, before checking the *real* bounds in conditionals the verifier will always accept. In particular, the scratchlen turns up as an unbounded scalar: we bound it by ANDing it with one less than the next highest power of two above the scratchlen option's value (which is guaranteed not to affect the actual value, which must be less than that, but bounds the scratchlen for the verifier). The actual bounds checking is shuffled off into a new function, dt_cg_check_bounds: among other things this is passed the reg to bounds-check, the actual scratchlen to check against, and a constant maximum scratch upper bound for the verifier (twice the actual scratchlen, so that when we are chcking real accesses with nonzero lengths, rather than simple pointers, we don't need to worry about accesses right at the end of the allocated space: we can just let the verifier assume that all accesses might be max-length, and they will still fit). The reg comes back suitably bounded to do arithmetic on it (to convert it back from an offset to a pointer), and suitably bounds-checked to dereference. dt_cg_check_bounds is significantly more flexible than we need here: it will check pointers, not just offsets (subtracting them from a base register, i.e. the scratch base), and it will check ranges, not just a simple pointer, so we can use it to make sure that accesses to scratch space are in range. There is no support for TLS storage yet, nor even diagnosis of attempts to do so. I don't know what it would involve. Help solicited: it's probably either next to impossible or dead easy :) Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
- Loading branch information