You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/11/19 16:17:39 UTC

[plc4x] branch feature/plc4go updated: - Secured the access to the valueCache in KNX against simultaneous access

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/plc4go by this push:
     new 11b542e  - Secured the access to the valueCache in KNX against simultaneous access
11b542e is described below

commit 11b542eda23b6aab981bac353d60ec9b3e1f6426
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Nov 19 17:17:26 2020 +0100

    - Secured the access to the valueCache in KNX against simultaneous access
---
 .../internal/plc4go/knxnetip/KncNetIpConnection.go |  10 +-
 plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go  | 240 +++++++++++----------
 2 files changed, 131 insertions(+), 119 deletions(-)

diff --git a/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go b/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
index 2eebff1..fcdd4d0 100644
--- a/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
+++ b/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
@@ -33,6 +33,7 @@ import (
 	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
 	"net"
 	"strconv"
+	"sync"
 	"time"
 )
 
@@ -62,7 +63,9 @@ type KnxNetIpConnection struct {
 	leve3AddressCache        map[uint16]*driverModel.KnxGroupAddress3Level
 	leve2AddressCache        map[uint16]*driverModel.KnxGroupAddress2Level
 	leve1AddressCache        map[uint16]*driverModel.KnxGroupAddressFreeLevel
-	valueCache               map[uint16][]int8
+
+	valueCache      map[uint16][]int8
+	valueCacheMutex sync.RWMutex
 
 	GatewayKnxAddress      *driverModel.KnxAddress
 	GatewayName            string
@@ -85,6 +88,7 @@ func NewKnxNetIpConnection(messageCodec spi.MessageCodec, options map[string][]s
 		leve2AddressCache:  map[uint16]*driverModel.KnxGroupAddress2Level{},
 		leve1AddressCache:  map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
 		valueCache:         map[uint16][]int8{},
+		valueCacheMutex:    sync.RWMutex{},
 	}
 }
 
@@ -378,13 +382,17 @@ func (m *KnxNetIpConnection) handleIncomingTunnelingRequest(tunnelingRequestChan
 			cemiDataInd := driverModel.CastCEMIDataInd(tunnelingRequest.Cemi.Child)
 			if cemiDataInd != nil {
 				addressData := uint16(cemiDataInd.CemiDataFrame.DestinationAddress[0])<<8 | (uint16(cemiDataInd.CemiDataFrame.DestinationAddress[1]) & 0xFF)
+				m.valueCacheMutex.RLock()
 				val, ok := m.valueCache[addressData]
+				m.valueCacheMutex.RUnlock()
 				changed := false
 				var payload []int8
 				payload = append(payload, cemiDataInd.CemiDataFrame.DataFirstByte)
 				payload = append(payload, cemiDataInd.CemiDataFrame.Data...)
 				if !ok || !m.sliceEqual(val, payload) {
+					m.valueCacheMutex.Lock()
 					m.valueCache[addressData] = payload
+					m.valueCacheMutex.Unlock()
 					// If this is a new value, we have to also provide the 3 different types of addresses.
 					if !ok {
 						destinationAddress := cemiDataInd.CemiDataFrame.DestinationAddress
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
index d6b43b6..79961fd 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
@@ -19,141 +19,145 @@
 package knxnetip
 
 import (
-    driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
-    internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/model"
-    internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/model/values"
-    "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
-    "github.com/apache/plc4x/plc4go/internal/plc4go/utils"
-    apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
-    apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+	driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+	internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/model"
+	internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/model/values"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+	"github.com/apache/plc4x/plc4go/internal/plc4go/utils"
+	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+	apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
 )
 
 type KnxNetIpReader struct {
-    connection *KnxNetIpConnection
-    spi.PlcReader
+	connection *KnxNetIpConnection
+	spi.PlcReader
 }
 
 func NewKnxNetIpReader(connection *KnxNetIpConnection) *KnxNetIpReader {
-    return &KnxNetIpReader{
-        connection: connection,
-    }
+	return &KnxNetIpReader{
+		connection: connection,
+	}
 }
 
 func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiModel.PlcReadRequestResult {
-    resultChan := make(chan apiModel.PlcReadRequestResult)
-    go func() {
-        responseCodes := map[string]apiModel.PlcResponseCode{}
-        plcValues := map[string]apiValues.PlcValue{}
-        for _, fieldName := range readRequest.GetFieldNames() {
-            // Get the knx field
-            field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
-            if err != nil {
-                responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
-                plcValues[fieldName] = nil
-                continue
-            }
+	resultChan := make(chan apiModel.PlcReadRequestResult)
+	go func() {
+		responseCodes := map[string]apiModel.PlcResponseCode{}
+		plcValues := map[string]apiValues.PlcValue{}
+		for _, fieldName := range readRequest.GetFieldNames() {
+			// Get the knx field
+			field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
+			if err != nil {
+				responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+				plcValues[fieldName] = nil
+				continue
+			}
 
-            // Pattern fields can match more than one value, therefore we have to handle things differently
-            if field.IsPatternField() {
-                // Depending on the type of field, get the uint16 ids of all values that match the current field
-                matchedAddresses := map[uint16]*driverModel.KnxGroupAddress{}
-                switch field.(type) {
-                case KnxNetIpGroupAddress3LevelPlcField:
-                    for key, value := range m.connection.leve3AddressCache {
-                        if field.matches(value.Parent) {
-                            matchedAddresses[key] = value.Parent
-                        }
-                    }
-                case KnxNetIpGroupAddress2LevelPlcField:
-                    for key, value := range m.connection.leve2AddressCache {
-                        if field.matches(value.Parent) {
-                            matchedAddresses[key] = value.Parent
-                        }
-                    }
-                case KnxNetIpGroupAddress1LevelPlcField:
-                    for key, value := range m.connection.leve1AddressCache {
-                        if field.matches(value.Parent) {
-                            matchedAddresses[key] = value.Parent
-                        }
-                    }
-                }
+			// Pattern fields can match more than one value, therefore we have to handle things differently
+			if field.IsPatternField() {
+				// Depending on the type of field, get the uint16 ids of all values that match the current field
+				matchedAddresses := map[uint16]*driverModel.KnxGroupAddress{}
+				switch field.(type) {
+				case KnxNetIpGroupAddress3LevelPlcField:
+					for key, value := range m.connection.leve3AddressCache {
+						if field.matches(value.Parent) {
+							matchedAddresses[key] = value.Parent
+						}
+					}
+				case KnxNetIpGroupAddress2LevelPlcField:
+					for key, value := range m.connection.leve2AddressCache {
+						if field.matches(value.Parent) {
+							matchedAddresses[key] = value.Parent
+						}
+					}
+				case KnxNetIpGroupAddress1LevelPlcField:
+					for key, value := range m.connection.leve1AddressCache {
+						if field.matches(value.Parent) {
+							matchedAddresses[key] = value.Parent
+						}
+					}
+				}
 
-                // If not a single match was found, we'll return a "not found" message
-                if len(matchedAddresses) == 0 {
-                    responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
-                    plcValues[fieldName] = nil
-                    continue
-                }
+				// If not a single match was found, we'll return a "not found" message
+				if len(matchedAddresses) == 0 {
+					responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
+					plcValues[fieldName] = nil
+					continue
+				}
 
-                // Go through all of the values and create a plc-struct from them
-                // where the string version of the address becomes the property name
-                // and the property value is the corresponding value (Other wise it
-                // would be impossible to know which of the fields the pattern matched
-                // a given value belongs to)
-                values := map[string]apiValues.PlcValue{}
-                for numericAddress, address := range matchedAddresses {
-                    // Get the raw data from the cache
-                    int8s := m.connection.valueCache[numericAddress]
+				// Go through all of the values and create a plc-struct from them
+				// where the string version of the address becomes the property name
+				// and the property value is the corresponding value (Other wise it
+				// would be impossible to know which of the fields the pattern matched
+				// a given value belongs to)
+				values := map[string]apiValues.PlcValue{}
+				for numericAddress, address := range matchedAddresses {
+					// Get the raw data from the cache
+					m.connection.valueCacheMutex.RLock()
+					int8s, _ := m.connection.valueCache[numericAddress]
+					m.connection.valueCacheMutex.RUnlock()
 
-                    // Decode the data according to the fields type
-                    rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
-                    plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
-                    // If any of the values doesn't decode correctly, we can't return any
-                    if err != nil {
-                        responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
-                        plcValues[fieldName] = nil
-                        continue
-                    }
+					// Decode the data according to the fields type
+					rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
+					plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
+					// If any of the values doesn't decode correctly, we can't return any
+					if err != nil {
+						responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
+						plcValues[fieldName] = nil
+						continue
+					}
 
-                    values[GroupAddressToString(address)] = plcValue
-                }
+					values[GroupAddressToString(address)] = plcValue
+				}
 
-                // Add it to the result
-                responseCodes[fieldName] = apiModel.PlcResponseCode_OK
-                plcValues[fieldName] = internalValues.NewPlcStruct(values)
-                continue
-            } else {
-                // If it's not a pattern field, we can access the cached value a lot simpler
+				// Add it to the result
+				responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+				plcValues[fieldName] = internalValues.NewPlcStruct(values)
+				continue
+			} else {
+				// If it's not a pattern field, we can access the cached value a lot simpler
 
-                // Serialize the field to an uint16
-                wb := utils.NewWriteBuffer()
-                err = field.toGroupAddress().Serialize(*wb)
-                if err != nil {
-                    responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
-                    plcValues[fieldName] = nil
-                    continue
-                }
-                rawAddress := wb.GetBytes()
-                address := (uint16(rawAddress[0]) << 8) | uint16(rawAddress[1] & 0xFF)
+				// Serialize the field to an uint16
+				wb := utils.NewWriteBuffer()
+				err = field.toGroupAddress().Serialize(*wb)
+				if err != nil {
+					responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+					plcValues[fieldName] = nil
+					continue
+				}
+				rawAddress := wb.GetBytes()
+				address := (uint16(rawAddress[0]) << 8) | uint16(rawAddress[1]&0xFF)
 
-                // Get the value form the cache
-                int8s, ok := m.connection.valueCache[address]
-                if !ok {
-                    responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
-                    plcValues[fieldName] = nil
-                    continue
-                }
+				// Get the value form the cache
+				m.connection.valueCacheMutex.RLock()
+				int8s, ok := m.connection.valueCache[address]
+				m.connection.valueCacheMutex.RUnlock()
+				if !ok {
+					responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
+					plcValues[fieldName] = nil
+					continue
+				}
 
-                // Decode the data according to the fields type
-                rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
-                plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
-                if err != nil {
-                    responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
-                    plcValues[fieldName] = nil
-                    continue
-                }
+				// Decode the data according to the fields type
+				rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
+				plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
+				if err != nil {
+					responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
+					plcValues[fieldName] = nil
+					continue
+				}
 
-                // Add it to the result
-                responseCodes[fieldName] = apiModel.PlcResponseCode_OK
-                plcValues[fieldName] = plcValue
-            }
-        }
-        result := internalModel.NewDefaultPlcReadResponse(readRequest, responseCodes, plcValues)
-        resultChan <- apiModel.PlcReadRequestResult{
-            Request:  readRequest,
-            Response: result,
-            Err:      nil,
-        }
-    }()
-    return resultChan
+				// Add it to the result
+				responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+				plcValues[fieldName] = plcValue
+			}
+		}
+		result := internalModel.NewDefaultPlcReadResponse(readRequest, responseCodes, plcValues)
+		resultChan <- apiModel.PlcReadRequestResult{
+			Request:  readRequest,
+			Response: result,
+			Err:      nil,
+		}
+	}()
+	return resultChan
 }