diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index a270cca7a20..7fb89b42a70 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -733,6 +733,17 @@ func (h *Handler) HandleGetStateMetadata(msg *pb.ChaincodeMessage, txContext *Tr // Handles query to ledger to rage query state func (h *Handler) HandleGetStateByRange(msg *pb.ChaincodeMessage, txContext *TransactionContext) (*pb.ChaincodeMessage, error) { + // Defer panic recovery + defer func() { + if r := recover(); r != nil { + chaincodeLogger.Errorf("Panic in HandleGetStateByRange: %v", r) + // Clean up any query context that may have been created + if txContext != nil { + txContext.CleanupQueryContext("") + } + } + }() + getStateByRange := &pb.GetStateByRange{} err := proto.Unmarshal(msg.Payload, getStateByRange) if err != nil { @@ -750,6 +761,12 @@ func (h *Handler) HandleGetStateByRange(msg *pb.ChaincodeMessage, txContext *Tra isPaginated := false namespaceID := txContext.NamespaceID collection := getStateByRange.Collection + + // Validate start and end keys + if getStateByRange.StartKey == "" && getStateByRange.EndKey == "" { + return nil, errors.New("start key and end key must not both be empty") + } + if isCollectionSet(collection) { if txContext.IsInitTransaction { return nil, errors.New("private data APIs are not allowed in chaincode Init()") @@ -767,14 +784,29 @@ func (h *Handler) HandleGetStateByRange(msg *pb.ChaincodeMessage, txContext *Tra startKey = metadata.Bookmark } } + + // Validate page size + if metadata.PageSize < 1 { + return nil, errors.New("page size must be greater than zero") + } + rangeIter, err = txContext.TXSimulator.GetStateRangeScanIteratorWithPagination(namespaceID, startKey, getStateByRange.EndKey, metadata.PageSize) } else { rangeIter, err = txContext.TXSimulator.GetStateRangeScanIterator(namespaceID, getStateByRange.StartKey, getStateByRange.EndKey) } + if err != nil { + // Clean up any resources if error occurs + txContext.CleanupQueryContext(iterID) return nil, errors.WithStack(err) } + + if rangeIter == nil { + txContext.CleanupQueryContext(iterID) + return nil, errors.New("range query iterator is nil") + } + txContext.InitializeQueryContext(iterID, rangeIter) payload, err := h.QueryResponseBuilder.BuildQueryResponse(txContext, rangeIter, iterID, isPaginated, totalReturnLimit) diff --git a/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go b/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go index 6865630d1e2..9e25786a15e 100644 --- a/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go +++ b/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go @@ -8,6 +8,7 @@ package stateleveldb import ( "bytes" + "fmt" "github.com/hyperledger/fabric-lib-go/common/flogging" "github.com/hyperledger/fabric/common/ledger/dataformat" @@ -164,10 +165,28 @@ func (vdb *versionedDB) GetStateRangeScanIteratorWithPagination(namespace string if endKey == "" { dataEndKey[len(dataEndKey)-1] = lastKeyIndicator } - dbItr, err := vdb.db.GetIterator(dataStartKey, dataEndKey) + + // Wrap the iterator creation in a recover block + var dbItr iterator.Iterator + var err error + + func() { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic while creating iterator: %v", r) + } + }() + dbItr, err = vdb.db.GetIterator(dataStartKey, dataEndKey) + }() + if err != nil { return nil, err } + + if dbItr == nil { + return nil, errors.New("nil iterator returned from db") + } + return newKVScanner(namespace, dbItr, pageSize), nil }