Skip to content

Commit

Permalink
fix: preemptive panic recovery (#75)
Browse files Browse the repository at this point in the history
This protects the library from potential dos vectors introduced by go-fraud users in the proof deserialization and verification code paths.
  • Loading branch information
Wondertan authored Apr 20, 2024
1 parent 642bb01 commit 8b5120a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 4 deletions.
12 changes: 11 additions & 1 deletion fraudserv/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,22 @@ func (f *ProofService[H]) processIncoming(
proofType fraud.ProofType,
from peer.ID,
msg *pubsub.Message,
) pubsub.ValidationResult {
) (res pubsub.ValidationResult) {
ctx, span := tracer.Start(ctx, "process_proof", trace.WithAttributes(
attribute.String("proof_type", string(proofType)),
))
defer span.End()

defer func() {
r := recover()
if r != nil {
err := fmt.Errorf("PANIC while processing a proof: %s", r)
log.Error(err)
span.RecordError(err)
res = pubsub.ValidationReject
}
}()

// unmarshal message to the Proof.
// Peer will be added to black list if unmarshalling fails.
proof, err := f.unmarshal.Unmarshal(proofType, msg.Data)
Expand Down
22 changes: 22 additions & 0 deletions fraudserv/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ import (
"github.com/celestiaorg/go-fraud/fraudtest"
)

func TestService_processIncomingRecovery(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
t.Cleanup(cancel)

serv := newTestService(ctx, t, false)
require.NoError(t, serv.Start(ctx))

fraud := fraudtest.NewPanickingProof[*headertest.DummyHeader]()
sub, err := serv.Subscribe(fraud.Type())
require.NoError(t, err)
defer sub.Cancel()

err = serv.Broadcast(ctx, fraud)
require.Error(t, err)

ctx2, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
t.Cleanup(cancel)

_, err = sub.Proof(ctx2)
require.Error(t, err)
}

func TestService_SubscribeBroadcastValid(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
t.Cleanup(cancel)
Expand Down
14 changes: 11 additions & 3 deletions fraudtest/dummy_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import (
const DummyProofType fraud.ProofType = "DummyProof"

type DummyProof[H header.Header[H]] struct {
Valid bool
Valid bool
Panics bool
}

func NewValidProof[H header.Header[H]]() *DummyProof[H] {
return &DummyProof[H]{true}
return &DummyProof[H]{true, false}
}

func NewInvalidProof[H header.Header[H]]() *DummyProof[H] {
return &DummyProof[H]{false}
return &DummyProof[H]{false, false}
}

func NewPanickingProof[H header.Header[H]]() *DummyProof[H] {
return &DummyProof[H]{false, true}
}

func (m *DummyProof[H]) Type() fraud.ProofType {
Expand All @@ -35,6 +40,9 @@ func (m *DummyProof[H]) Height() uint64 {
}

func (m *DummyProof[H]) Validate(H) error {
if m.Panics {
panic("crippling anxiety panic attack")
}
if !m.Valid {
return errors.New("DummyProof: proof is not valid")
}
Expand Down

0 comments on commit 8b5120a

Please sign in to comment.