Skip to content

Commit

Permalink
Merge pull request #2877 from onflow/sainati/entitlement-mapping-escape
Browse files Browse the repository at this point in the history
Entitlement mapping escape fixes
  • Loading branch information
dsainati1 authored Oct 19, 2023
2 parents 6dc6b38 + 5defa03 commit 0bfd2c0
Show file tree
Hide file tree
Showing 7 changed files with 666 additions and 39 deletions.
17 changes: 17 additions & 0 deletions runtime/interpreter/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,23 @@ func (e ValueTransferTypeError) Error() string {
)
}

// UnexpectedMappedEntitlementError
type UnexpectedMappedEntitlementError struct {
Type sema.Type
LocationRange
}

var _ errors.InternalError = UnexpectedMappedEntitlementError{}

func (UnexpectedMappedEntitlementError) IsInternalError() {}

func (e UnexpectedMappedEntitlementError) Error() string {
return fmt.Sprintf(
"invalid transfer of value: found an unexpected runtime mapped entitlement `%s`",
e.Type.QualifiedString(),
)
}

// ResourceConstructionError
type ResourceConstructionError struct {
CompositeType *sema.CompositeType
Expand Down
8 changes: 8 additions & 0 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2115,6 +2115,14 @@ func (interpreter *Interpreter) convert(value Value, valueType, targetType sema.
// transferring a reference at runtime does not change its entitlements; this is so that an upcast reference
// can later be downcast back to its original entitlement set

// check defensively that we never create a runtime mapped entitlement value
if _, isMappedAuth := unwrappedTargetType.Authorization.(*sema.EntitlementMapAccess); isMappedAuth {
panic(UnexpectedMappedEntitlementError{
Type: unwrappedTargetType,
LocationRange: locationRange,
})
}

switch ref := value.(type) {
case *EphemeralReferenceValue:
return NewEphemeralReferenceValue(
Expand Down
30 changes: 17 additions & 13 deletions runtime/sema/check_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,23 @@ func (checker *Checker) checkFunction(
functionActivation.InitializationInfo = initializationInfo

if functionBlock != nil {
if mappedAccess, isMappedAccess := access.(*EntitlementMapAccess); isMappedAccess {
checker.entitlementMappingInScope = mappedAccess.Type
}

checker.InNewPurityScope(functionType.Purity == FunctionPurityView, func() {
checker.visitFunctionBlock(
functionBlock,
functionType.ReturnTypeAnnotation,
checkResourceLoss,
)
})

checker.entitlementMappingInScope = nil
func() {
oldMappedAccess := checker.entitlementMappingInScope
if mappedAccess, isMappedAccess := access.(*EntitlementMapAccess); isMappedAccess {
checker.entitlementMappingInScope = mappedAccess.Type
} else {
checker.entitlementMappingInScope = nil
}
defer func() { checker.entitlementMappingInScope = oldMappedAccess }()

checker.InNewPurityScope(functionType.Purity == FunctionPurityView, func() {
checker.visitFunctionBlock(
functionBlock,
functionType.ReturnTypeAnnotation,
checkResourceLoss,
)
})
}()

if mustExit {
returnType := functionType.ReturnTypeAnnotation.Type
Expand Down
17 changes: 1 addition & 16 deletions runtime/sema/check_member_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,29 +321,14 @@ func (checker *Checker) visitMember(expression *ast.MemberExpression, isAssignme
switch ty := resultingType.(type) {
case *ReferenceType:
return NewReferenceType(checker.memoryGauge, resultingAuthorization, ty.Type)
case *OptionalType:
switch innerTy := ty.Type.(type) {
case *ReferenceType:
return NewOptionalType(checker.memoryGauge,
NewReferenceType(checker.memoryGauge, resultingAuthorization, innerTy.Type))
}
}
return resultingType
}

shouldSubstituteAuthorization := !member.Access.Equal(resultingAuthorization)

if shouldSubstituteAuthorization {
switch ty := resultingType.(type) {
case *FunctionType:
resultingType = NewSimpleFunctionType(
ty.Purity,
ty.Parameters,
NewTypeAnnotation(substituteConcreteAuthorization(ty.ReturnTypeAnnotation.Type)),
)
default:
resultingType = substituteConcreteAuthorization(resultingType)
}
resultingType = resultingType.Map(checker.memoryGauge, make(map[*TypeParameter]*TypeParameter), substituteConcreteAuthorization)
}

// Check that the member access is not to a function of resource type
Expand Down
16 changes: 9 additions & 7 deletions runtime/sema/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1256,19 +1256,21 @@ func (checker *Checker) functionType(
parameterList *ast.ParameterList,
returnTypeAnnotation *ast.TypeAnnotation,
) *FunctionType {

oldMappedAccess := checker.entitlementMappingInScope
if mapAccess, isMapAccess := access.(*EntitlementMapAccess); isMapAccess {
checker.entitlementMappingInScope = mapAccess.Type
} else {
checker.entitlementMappingInScope = nil
}
defer func() { checker.entitlementMappingInScope = oldMappedAccess }()

convertedParameters := checker.parameters(parameterList)

convertedReturnTypeAnnotation := VoidTypeAnnotation
if returnTypeAnnotation != nil {
// to allow entitlement mapping types to be used in the return annotation only of
// a mapped accessor function, we introduce a "variable" into the typing scope while
// checking the return
if mapAccess, isMapAccess := access.(*EntitlementMapAccess); isMapAccess {
checker.entitlementMappingInScope = mapAccess.Type
}
convertedReturnTypeAnnotation =
checker.ConvertTypeAnnotation(returnTypeAnnotation)
checker.entitlementMappingInScope = nil
}

return &FunctionType{
Expand Down
Loading

0 comments on commit 0bfd2c0

Please sign in to comment.