Skip to content

Commit f165b8f

Browse files
committed
add logic to perform authorization check for contract code access during simulation/eth_call and block processing
1 parent 5014b2b commit f165b8f

File tree

4 files changed

+105
-34
lines changed

4 files changed

+105
-34
lines changed

core/state_transition.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,13 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
322322
if err != nil {
323323
return nil, 0, true, err
324324
}
325-
// managed parties for public contracts is empty so nothing to check there
325+
// managed parties for public transactions is empty so nothing to check there
326326
if len(managedPartiesInContract) > 0 {
327327
if common.NotContainsAll(managedPartiesInContract, managedPartiesInTx) {
328328
log.Debug("Managed parties check has failed for contract", "addr", address, "EPH",
329329
pmh.eph.TerminalString(), "contractMP", managedPartiesInContract, "txMP", managedPartiesInTx)
330330
st.evm.RevertToSnapshot(snapshot)
331-
// TODO - see whether we can find a way to store this error and make it available via customizations to getTransactionReceipt or an alternate API
331+
// TODO - see whether we can find a way to store this error and make it available via customizations to getTransactionReceipt
332332
return nil, 0, true, nil
333333
}
334334
}

core/vm/evm.go

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,30 +63,14 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
6363
// Using CodeAddr is favour over contract.Address()
6464
// During DelegateCall() CodeAddr is the address of the delegated account
6565
address := *contract.CodeAddr
66-
if _, ok := evm.affectedContracts[address]; !ok {
67-
evm.affectedContracts[address] = newAffectedType(MessageCall, ModeUnknown)
68-
// for multitenancy, we cache the expectation of AffectedMode to be used later
69-
// when an opcode is executed.
70-
if evm.SupportsMultitenancy && evm.AuthorizeMessageCallFunc != nil {
71-
// when contract code is empty, there's no execution hence the
72-
// multitenancy check will not happen in captureOperationMode().
73-
// This additional check to ensure we capture this case
74-
if len(contract.Code) == 0 {
75-
return nil, multitenancy.ErrNotAuthorized
76-
}
77-
authorizedRead, authorizedWrite, err := evm.AuthorizeMessageCallFunc(address)
78-
if err != nil {
79-
return nil, err
80-
}
81-
expectedMode := evm.affectedContracts[address].mode
82-
if authorizedRead {
83-
expectedMode = expectedMode | ModeRead
84-
}
85-
if authorizedWrite {
86-
expectedMode = expectedMode | ModeWrite
87-
}
88-
evm.affectedContracts[address].mode = expectedMode
89-
}
66+
// during simulation/eth_call, when contract code is empty, there's no execution hence the
67+
// multitenancy check will not happen in captureOperationMode().
68+
// This additional check to ensure we capture this case
69+
if evm.SupportsMultitenancy && evm.AuthorizeMessageCallFunc != nil && len(contract.Code) == 0 {
70+
return nil, multitenancy.ErrNotAuthorized
71+
}
72+
if err := evm.captureAffectedContract(address, ModeUnknown); err != nil {
73+
return nil, err
9074
}
9175
// When delegatecall, need to capture the operation mode in the context of contract.Address()
9276
// the affected contract is required read only mode
@@ -152,7 +136,7 @@ type Context struct {
152136
// It's only injected during simulation
153137
AuthorizeCreateFunc multitenancy.AuthorizeCreateFunc
154138
// AuthorizeMessageCallFunc performs tenancy authorization check for message call to a contract.
155-
// It's only injected during simulation
139+
// It's only injected during simulation/eth_call
156140
AuthorizeMessageCallFunc multitenancy.AuthorizeMessageCallFunc
157141
}
158142

@@ -229,6 +213,10 @@ type AffectedType struct {
229213
mode AffectedMode
230214
}
231215

216+
func (t AffectedType) String() string {
217+
return fmt.Sprintf("reason=%d,mode=%d", t.reason, t.mode)
218+
}
219+
232220
// AffectedReason defines a type of operation that was applied to a contract.
233221
type AffectedReason byte
234222

@@ -248,6 +236,9 @@ const (
248236
ModeRead AffectedMode = iota
249237
// ModeWrite indicates that state has been modified as the result of contract code execution
250238
ModeWrite = ModeRead << 1
239+
// ModeUpdated indicates that the affected mode has been setup for multitenancy check.
240+
// This is mainly used during simulation and eth_call
241+
ModeUpdated = ModeRead << 7
251242
)
252243

253244
func ModeOf(isWrite bool) AffectedMode {
@@ -257,8 +248,27 @@ func ModeOf(isWrite bool) AffectedMode {
257248
return ModeRead
258249
}
259250

260-
func (mode AffectedMode) IsAuthorized(actualMode AffectedMode) bool {
261-
return mode&actualMode != actualMode
251+
func (mode AffectedMode) IsNotAuthorized(actualMode AffectedMode) bool {
252+
return mode.Has(ModeUpdated) && !mode.Has(actualMode)
253+
}
254+
255+
func (mode AffectedMode) Update(authorizedRead bool, authorizedWrite bool) AffectedMode {
256+
newMode := mode | ModeUpdated
257+
if authorizedRead {
258+
newMode = newMode | ModeRead
259+
}
260+
if authorizedWrite {
261+
newMode = newMode | ModeWrite
262+
}
263+
return newMode
264+
}
265+
266+
func (mode AffectedMode) Has(modes ...AffectedMode) bool {
267+
expectedMode := ModeUnknown
268+
for _, m := range modes {
269+
expectedMode = expectedMode | m
270+
}
271+
return mode&expectedMode == expectedMode
262272
}
263273

264274
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
@@ -705,6 +715,7 @@ func getDualState(evm *EVM, addr common.Address) StateDB {
705715

706716
if evm.PrivateState().Exist(addr) {
707717
state = evm.PrivateState()
718+
evm.captureAffectedContract(addr, ModeUnknown)
708719
} else if evm.PublicState().Exist(addr) {
709720
state = evm.PublicState()
710721
}
@@ -818,13 +829,13 @@ func (evm *EVM) peekAddress() common.Address {
818829
func (evm *EVM) captureOperationMode(isWriteOperation bool) error {
819830
currentAddress := evm.peekAddress()
820831
if (currentAddress == common.Address{}) {
821-
return nil
832+
return evm.lastError
822833
}
823834
actualMode := ModeOf(isWriteOperation)
824835
if t, ok := evm.affectedContracts[currentAddress]; ok {
825836
// perform multitenancy check
826837
if evm.enforceMultitenancyCheck() {
827-
if t.mode.IsAuthorized(actualMode) {
838+
if t.mode.IsNotAuthorized(actualMode) {
828839
log.Trace("Multitenancy check for captureOperationMode()", "address", currentAddress.Hex(), "actual", actualMode, "expect", t.mode)
829840
evm.lastError = multitenancy.ErrNotAuthorized
830841
}
@@ -838,7 +849,43 @@ func (evm *EVM) captureOperationMode(isWriteOperation bool) error {
838849
return nil
839850
}
840851

841-
// enforceMultitenancyCheck returns true if EVM is enforced to do multitenancy check, false otherwise
852+
// Quorum
853+
//
854+
// captureAffectedContract stores the contract address to the affectedContract list if not yet there.
855+
// The affected mode is also updated if required.
856+
// Default affected reason is MessageCall.
857+
// In simulation/eth_call for multitenancy, it sets the expectation of AffectedMode
858+
// to be verified later when an opcode is executed.
859+
func (evm *EVM) captureAffectedContract(address common.Address, mode AffectedMode) error {
860+
affectedType, found := evm.affectedContracts[address]
861+
if !found {
862+
affectedType = newAffectedType(MessageCall, mode)
863+
evm.affectedContracts[address] = affectedType
864+
}
865+
if affectedType.mode != ModeUnknown {
866+
return nil
867+
}
868+
if evm.SupportsMultitenancy && evm.AuthorizeMessageCallFunc != nil {
869+
authorizedRead, authorizedWrite, err := evm.AuthorizeMessageCallFunc(address)
870+
if err != nil {
871+
return err
872+
}
873+
// if we don't authorize either read/write, it's unauthorized access
874+
// and we need to inform EVM
875+
if !authorizedRead && !authorizedWrite {
876+
evm.lastError = multitenancy.ErrNotAuthorized
877+
log.Debug("Affected contract not authorized", "address", address.Hex(), "read", authorizedRead, "write", authorizedWrite)
878+
return multitenancy.ErrNotAuthorized
879+
}
880+
oldMode := affectedType.mode
881+
affectedType.mode = affectedType.mode.Update(authorizedRead, authorizedWrite)
882+
log.Debug("AffectedMode changed", "address", address.Hex(), "old", oldMode, "new", affectedType.mode)
883+
}
884+
return nil
885+
}
886+
887+
// enforceMultitenancyCheck returns true if EVM is enforced to do multitenancy check
888+
// during simulation/eth_call, false otherwise
842889
func (evm *EVM) enforceMultitenancyCheck() bool {
843890
return evm.AuthorizeCreateFunc != nil || evm.AuthorizeMessageCallFunc != nil
844891
}

core/vm/evm_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package vm
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestAffectedMode_Update_whenTypical(t *testing.T) {
10+
testObject := ModeUnknown
11+
authorizedReads := []bool{true, false}
12+
authorizedWrites := []bool{true, false}
13+
for _, authorizedRead := range authorizedReads {
14+
for _, authorizedWrite := range authorizedWrites {
15+
actual := testObject.Update(authorizedRead, authorizedWrite)
16+
17+
assert.True(t, actual.Has(ModeUpdated))
18+
assert.Equal(t, authorizedRead, actual.Has(ModeRead))
19+
assert.Equal(t, authorizedWrite, actual.Has(ModeWrite))
20+
assert.False(t, testObject.Has(ModeUpdated))
21+
}
22+
}
23+
}

core/vm/instructions.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,10 +551,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
551551
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
552552
slot := stack.peek()
553553
address := common.BigToAddress(slot)
554-
if interpreter.evm.StateDB.Empty(address) {
554+
stateDB := getDualState(interpreter.evm, address)
555+
if stateDB.Empty(address) {
555556
slot.SetUint64(0)
556557
} else {
557-
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
558+
slot.SetBytes(stateDB.GetCodeHash(address).Bytes())
558559
}
559560
return nil, nil
560561
}

0 commit comments

Comments
 (0)