• Nenhum resultado encontrado

3.4 The Týr typing and instrumentation rules

3.4.4 Local pointers

Instruction:id=getelementptrptr,isz 0, idx,

whentypeΓ(ptr) =P tr(SArray(n, T Y), lo, hi) Action:

Emit checkptr6=null Emit checklo≤0∧hi≥1 Emit the instruction

Γ←+ id:SP tr+(T Y,0−idx, n−1−idx)

It should be noted that local variables are notrequired to have aLocaltype, only variables involved in type dependencies are. Pointers to local variables not involved in type dependencies can be given a more general pointer type, which don’t have the usage restrictions ofLocalpointers, but cannot be dereferenced in type expressions.

The specific pointer type assigned to the result of an alloca instruction depends on whether the programmer has provided an annotation for the pointer, the allocation quantity, and the allocation base type. If an annotation is given, it must provide aLocal, P tr or SP tr type consistent with the LLVM IR type and quantity used by the alloca instruction; otherwise, the annotation is rejected.

ALocaltype annotation can only be given to allocations with a quantity of 1, and the base type must be compatible with the initial (zero2) value of the object. This means, for instance, that the base type of aLocalpointer cannot be aP tr+type, since the stored value is initially null.

Instruction:id=allocaty,isz 1, whenA(id) = Local(T Y) Action:

Emit checkcompatΓ(tyzeroinitializer, T Y) Emit the instruction

EmitzeroF ill(id,0,1) Γ←+ id:Local(T Y)

Here, zeroF ill(id, lo, hi) means to emit code to store zeros in the positions in range[lo, hi)relative to the pointerid.

If aP trtype annotation is provided by the programmer, then the bounds must be compatible with the allocation quantity, and the base type must be similarly compatible with the initial value of the region.

Instruction:id=allocaty, qtd, whenA(id) =P tr±(T Y, lo, hi) Action:

Emit checklo≥0∧hi≤qtd

Emit checkcompatΓ(tyzeroinitializer, T Y) Emit the instruction

EmitzeroF ill(id, lo, hi) Γ←+ id:P tr±(T Y, lo, hi)

2We assume here that the memory allocated byallocais zero-initialized. This is enforced by emitting code to zero-fill the region immediately after theallocainstruction.

If an SP tr type is provided, then the declared upper bound must be strictly less the quantity of the allocation. This ensures that the last element of the allocated region (which is initialized with zeros) will remain outside the declared bounds, and therefore that a null terminator will be present past the declared upper bound, as required by the rules ofSP trpointers (as seen in Section 3.4.3).

Instruction:id=allocaty, qtd, whenA(id) =SP tr±(T Y, lo, hi) Action:

Emit checklo≥0∧hi < qtd

Emit checkcompatΓ(tyzeroinitializer, T Y) Emit the instruction

EmitzeroF ill(id, lo, hi) Γ←+ id:SP tr±(T Y, lo, hi)

When no annotation is provided, in general the allocation is given aP trtype with appropriate bounds based on the allocation quantity, and the base type is inferred from the LLVM IR one given in the instruction.

Instruction:id=allocaty, qtd,

whenA(id) = ∅∧(qtd6= 1∨tyis not a pointer type) Action:

Emit the instruction EmitzeroF ill(id,0, qtd) Γ←+ id:P tr+(dtye,0, qtd)

The exception is allocations with a quantity of 1 and a ponter base type, i.e., those corresponding to local pointer variables in C. To reduce the annotation burden on the programmer, and to allow the bounds of a pointer to vary during execution (for example, when incrementing the pointer in C), we implicitly allocate two integer local variables to hold the bounds of the pointer, which will be automatically updated whenever the pointer is modified. We initialize all variables with zeros, set the bounds of the created pointer to be the newly created local integers, and add a mapping in the automatic bounds environment∆to record that this pointer has automatically managed bounds.

Instruction:id=allocaty∗, qtd, whenA(id) = ∅∧qtd= 1 Action:

Emitlo=allocaiWORD,iWORD1, for fresh identifierlo Emithi=allocaiWORD,iWORD1, for fresh identifierhi Emit the instruction

EmitzeroF ill(id,0,1), zeroF ill(lo,0,1), zeroF ill(hi,0,1) Γ←+ id:Local(P tr(dtye,∗lo,∗hi))

Γ←+ lo:Local(iWORD), hi:Local(iWORD)

∆←+id: (lo, hi)

Loading from a Local pointer requires no checks: since the Local type is only given to pointers returned by alloca with a quantity of 1, we know that the bounds of the referenced region are [0,1), and therefore safe to load. However, if the base type contains pointer dereferences (e.g.,Local(P tr(i8,0,∗len))), these must be replaced with the actual values of the pointers at the time of the load. The intuition behind this is that after you load the content of a local variablex into a register id, id is fixed, i.e., a snapshot of the value of x at that moment. Therefore, the bounds of id must also be a snapshot of the values of the bounds at the time of the load, and it makes no sense for further modifications to the values of the bounds ofxto affect the snapshot stored inid.

To achieve this, each pointer dereferenced in the base type is loaded into a register, and the dereference is replaced by the register in the resulting type.

Instruction:id=loadptr, whentypeΓ(ptr) = Local(T Y) Action:

Emitidi ← ∗xi for each∗xiappearing inT Y and freshidi Emit the instruction

Γ←+ id:T Y[∗xi 7→idi]for eachi

Storing into a Local pointer is trickier. First, we need to check that the type of the value being stored is the same as the base type of the local variable. But since the type of the variable may depend on the variable itself, the type may change upon the assignment. Therefore, this compatibility check must happen against the new type, i.e., replacing all dereferences of the variable with the would-be new value. Second, because the types of other Local pointers (as well as the type of the pointer itself) may depend

on the variable being modified, their types may change. We need to check that the new value that they would acquire if the assignment happens is compatible with their current type. For instance, ifpis aLocal(P tr(i8,0,∗len)), changing the value stored atlenwill change the bounds ofp. If the new bounds are compatible with the old ones (for example, if we are decrementinglen, thus narrowing the bounds ofp), then the assignment tolenis okay. If not (for example, if we are incrementinglen, thus widening the bounds ofp, thus enabling access to a memory region which should not be accessible), then the assignment tolenmust not be allowed.

Instruction:storeval, ptr, whentypeΓ(ptr) =Local(T Y)∧∆(ptr) =∅ Action:

Emit checkcompatΓ(val, T Y[∗ptr 7→val])

For each(id:T Yid)inΓwhere∗ptrappears inT Yid Emit checkcompatΓ(id, T Yid[∗ptr 7→val]) Emit the instruction

ForLocal pointers whose base type is a pointer with automatic bounds, we must also update the bounds based on those of the new pointer being stored onto it.

Instruction:storeval, ptr,

whentypeΓ(ptr) =Local(P tr(T Yptr,∗lo,∗hi))∧∆(ptr) = (lo, hi) Action:

Let(T Yval, loval, hival) = DestructureP ointerT ype(typeΓ(val)) Emit checkcompatΓ(T Yval, T Yptr)

Emitstoreloval, lo, storehival, hi

For each(id:T Yid)inΓwhere∗ptrappears inT Yid

Emit checkcompatΓ(id, T Yid[∗ptr 7→val]) Emit the instruction

Where:

DestructureP ointerT ype(P tr±(T Y, lo, hi)) = (T Y, lo, hi) DestructureP ointerT ype(SP tr±(T Y, lo, hi)) = (T Y, lo, hi)

In principle, we would not want to allow pointer arithmetic on Local pointer:

Localpointers only ever point to a single object, so pointer arithmetic is not particularly useful on them. Moreover, if we allowed pointer arithmetic with a zero offset, the result would be a new pointer to the same element as the existingLocal pointer, which would

violate the non-aliasing invariant ofLocalpointers. However, as we will see below,Local pointers can be converted into general pointers with empty bounds, so it is possible to perform pointer arithmetic onLocalpointers, so long as the result is empty-bounded.

Instruction:id=getelementptrptr, idx, whentypeΓ(ptr) =Local(T Y) Action:

Emit the instruction Γ←+ id:P tr+(T Y,0,0)

Type compatibility rules. In general, Local pointers are not compatible with anything, not even with themselves: since they cannot be passed around or copied, it does not make sense to speak of type compatibility for local pointers. There is one ex-ception to this rule, to accomodate a common use case in LLVM IR: adding a call to a

“dummy” LLVM function passing a pointer to a local variable as a means of associating metadata with it. For example, Clang inserts calls to the [email protected] [email protected] to associate liveness information with variables. These are es-sentially dummy functions, present in the code only as a way to represent information which further LLVM passes may use, and not present in the resulting binary. Because these functions do not read or write to the pointer being passed, they do not pose a viola-tion of memory safety. To account for this case, Týr allows convertingLocalpointers into commonP trpointers, as long as the resulting bounds are empty. This allows the address of the local variable to be passed as an argument to those calls, but disallows using the contentof the variable, thus preserving safety.

compatΓ(Local(T Y), P tr±(T Y, lo, hi)) = (lo=hi)

Documentos relacionados