Skip to content

fix: prevent deadlock when argument's String() calls MethodCalled#1876

Open
ChihebBENCHEIKH1 wants to merge 1 commit intostretchr:masterfrom
ChihebBENCHEIKH1:fix/mock-stringer-deadlock
Open

fix: prevent deadlock when argument's String() calls MethodCalled#1876
ChihebBENCHEIKH1 wants to merge 1 commit intostretchr:masterfrom
ChihebBENCHEIKH1:fix/mock-stringer-deadlock

Conversation

@ChihebBENCHEIKH1
Copy link
Copy Markdown

Summary

Fixes #1719.

When Mock.MethodCalled is called with an argument that implements Stringer, and that String() method calls MethodCalled (e.g. a mock passed as its own argument), a deadlock occurs. This happens because findExpectedCall calls Arguments.Diff which uses %v to format arguments — triggering String() — while the mutex is already held.

Root cause

MethodCalled("DoAThing", mockObj)
  → mutex.Lock()
    → findExpectedCall()
      → Arguments.Diff()
        → fmt.Sprintf("(%[1]T=%[1]v)", mockObj)  // triggers String()
          → mockObj.String()
            → MethodCalled("String")
              → mutex.Lock()  // DEADLOCK

Fix

Added an unexported matchCount method that checks argument equality without formatting strings, avoiding any calls to user-defined String() or GoString() methods. Used it in:

  • findExpectedCall — the main matching path (hot path under mutex)
  • findClosestCall — the error path (also under mutex)

Diff formatting now happens after the mutex is released in MethodCalled, so it's safe for arguments to call back into the mock.

Test

Added TestIssue1719StringerDeadlock with a 5-second timeout that reproduces the exact scenario from the issue (mock passed as its own argument where String() calls MethodCalled).

Results

All existing tests pass with zero regressions:

ok  github.com/stretchr/testify/mock    0.028s
ok  github.com/stretchr/testify/suite   12.247s
ok  github.com/stretchr/testify/assert  0.377s

When Mock.MethodCalled is called with an argument that implements
Stringer, and that String() method calls MethodCalled (e.g. a mock
passed as its own argument), a deadlock occurs because Diff uses
%v to format arguments while the mutex is held.

This adds an unexported matchCount method that checks argument
equality without formatting strings, and uses it in findExpectedCall
and findClosestCall instead of Diff. The Diff formatting now happens
after the mutex is released.

All existing tests pass with zero regressions.

Fixes stretchr#1719
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mock.MethodCalled should not call methods of arguments

1 participant