Skip to content

GenerateJSONPath race condition in ApplyRulesToRuleSet #622

@czeslavo

Description

@czeslavo

Description

There's a data race condition in motor.ApplyRulesToRuleSet function that's triggered by concurrent read and write to a single base.Foundation instance:

=== RUN   TestMinimalRaceExample
==================
WARNING: DATA RACE
Read at 0x00c0001b0518 by goroutine 248:
  github.com/pb33f/doctor/model/high/base.(*Foundation).GenerateJSONPath()
      /Users/user/go/pkg/mod/github.com/pb33f/doctor@v0.0.18/model/high/base/foundation.go:335 +0x3c
  github.com/pb33f/doctor/model/high/v3.(*Operation).GenerateJSONPath()
      <autogenerated>:1 +0x24
  github.com/daveshanley/vacuum/functions/core.(*Truthy).RunRule()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/functions/core/truthy.go:102 +0x1070
  github.com/daveshanley/vacuum/motor.buildResults()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:923 +0x7e8
  github.com/daveshanley/vacuum/motor.runRule()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:837 +0xf80
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.func7.gowrap1()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:693 +0x88

Previous write at 0x00c0001b0518 by goroutine 262:
  github.com/pb33f/doctor/model/high/base.(*Foundation).GenerateJSONPath()
      /Users/user/go/pkg/mod/github.com/pb33f/doctor@v0.0.18/model/high/base/foundation.go:339 +0x578
  github.com/daveshanley/vacuum/functions/openapi.OperationDescription.RunRule()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/functions/openapi/operation_descriptions.go:133 +0x50c
  github.com/daveshanley/vacuum/functions/openapi.(*OperationDescription).RunRule()
      <autogenerated>:1 +0xd0
  github.com/daveshanley/vacuum/motor.buildResults()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:923 +0x7e8
  github.com/daveshanley/vacuum/motor.runRule()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:837 +0xf80
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.func7.gowrap1()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:693 +0x88

Goroutine 248 (running) created at:
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.func7()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:693 +0x3c4
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.gowrap1()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:703 +0x54

Goroutine 262 (running) created at:
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.func7()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:693 +0x3c4
  github.com/daveshanley/vacuum/motor.ApplyRulesToRuleSet.gowrap1()
      /Users/user/go/pkg/mod/github.com/daveshanley/vacuum@v0.16.3/motor/rule_applicator.go:703 +0x54
==================
    testing.go:1399: race detected during execution of test
--- FAIL: TestMinimalRaceExample (0.03s)

Reproduction

Run the following test with go test -race -count=10 (the race condition occurs every 3rd or 4th run).

func TestMinimalRaceExample(t *testing.T) {
	const (
		rules = `extends: [[spectral:oas, recommended]]
rules:
  x-visibility-truthy:
    description: x-visibility check
    message: "Make sure x-visibility is available for every operation"
    severity: "error"
    given: "$.paths.*.*"
    then:
      field: x-visibility
      function: truthy
`
		oapi = `openapi: 3.0.2
info:
  title: Sample API
  description: This is a sample API with internal and external routes.
  version: 1.0.0
servers:
  - url: https://internal.api.com/
  - url: https://external.api.com/
paths:
  /internalRoute:
    servers:
      - url: https://internal.api.com/
    get:
      summary: Internal route
      operationId: internalRouteOp
      responses:
        '200':
          description: Success

  /externalRoute:
    servers:
      - url: https://external.api.com/
    get:
      summary: External route
      operationId: externalRouteOp
      responses:
        '200':
          description: Success
`
	)

	customRuleSet, err := rulesets.CreateRuleSetFromData([]byte(rules))
	require.NoError(t, err)

	defaultRuleSet := rulesets.BuildDefaultRuleSets()
	customRuleSet = defaultRuleSet.GenerateRuleSetFromSuppliedRuleSet(customRuleSet)

	ruleSetResults := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
		RuleSet:     customRuleSet,
		Spec:        []byte(oapi),
		AllowLookup: true,
	})

	require.NotNil(t, ruleSetResults)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions