Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash on Windows when leaked mock exists #3982

Closed
basejumpa opened this issue Aug 18, 2022 · 3 comments
Closed

Crash on Windows when leaked mock exists #3982

basejumpa opened this issue Aug 18, 2022 · 3 comments
Assignees
Labels

Comments

@basejumpa
Copy link

basejumpa commented Aug 18, 2022

Describe the bug

When you instantiate a mock object globally instead within a test function this produces a "leaked mock" in terms of gmock.

This is not clean because the mock object need to terminate at the end of each test function to check the actual calls of the code inside the test function against the expectations. When you do it as intended then you place the mock object variable inside the test function. Then the mock object will be deleted automatically on test function return and the destructor is called to do the checks.

A leaked mock leads ...

  • on Linux this leads to a message on the console but the test executable exits gracefully. It also gives warnings on the console about leaked object(s);
  • on Windows leads to a crash due to a segmentation fault of the test executable.

Expected: gmock/gtest shall handle leaked mock objects on windows as it does on Linux.

Steps to reproduce the bug

I created a repository with github-actions which reproduces the issue: https://github.com/basejumpa/gmock_segfault

The build output on Windows see https://github.com/basejumpa/gmock_segfault/runs/7897083476?check_suite_focus=true#step:3:113

The build output on Linux see https://github.com/basejumpa/gmock_segfault/runs/7897083469?check_suite_focus=true#step:4:78

Does the bug persist in the most recent commit?

The problem occurs with googletest release-1.12.1

and also with HEAD of branch main as of this writing (commit c0e032e )

What operating system and version are you using?

Windows . See https://github.com/actions/runner-images/blob/win22/20220816.1/images/win/Windows2022-Readme.md

What compiler and version are you using?

Visual Studio Enterprise 2022.

But it also occurs with clang 14.x

What build system are you using?

CMake 3.24.0

Additional context

@basejumpa basejumpa added the bug label Aug 18, 2022
@dinord dinord self-assigned this Aug 22, 2022
@higher-performance
Copy link
Collaborator

higher-performance commented Oct 18, 2022

I managed to reproduce this. It turns out this happens during the build phase in Visual Studio, not during normal execution, because the build process itself calls the compiled executable as a post-build event. To reproduce the crash, we need to run: test_expectation_not_met__leaked.exe --gtest_list_tests.

It turns out the issue is that Mockup in your .cc file uses FunctionMocker, which inherits from UntypedFunctionMockerBase, which uses g_mock_object_registry on construction, which is a static variable initialized in global scope in gmock-spec-builders.cc, which means its construction and destruction order are not actually guaranteed to be correct with respect to the Mockup variable. In particular, on destruction, Mock::UnregisterLocked(this) uses g_mock_object_registry, which is already destructed by the time it is called. I am guessing the reason this "works" on Linux is that MSVC in debug mode performs extra checks on destruction, whereas Linux lets it slide. It certainly does not appear to be well-defined on either platform.

@dinord dinord assigned higher-performance and unassigned dinord Oct 19, 2022
@higher-performance
Copy link
Collaborator

Before we attempt to add a workaround for this, could you please clarify what the use case for this is? Normally I don't believe GoogleTest supports global mock objects, and I'm afraid I don't understand why they are needed in your case. If you could please explain/illustrate what the motivation for this is and why we should support it then we may consider seeing if a workaround is warranted.

Please do also note that the current behavior on Linux is broken just as it is on Windows; it's just that there is no explicit crash due to the lack of extra checks being done on destruction. So if the Linux behavior is something whose correctness you rely on, please explain how and what the use case is. Otherwise, we may close this issue as not-supported. Thanks!

@higher-performance
Copy link
Collaborator

I'm closing this issue now since we haven't heard back regarding a motivation for this. However, if you do find a need for this, feel free to reopen and explain the use case. (Otherwise, we prefer to avoid adding support for this without a clear benefit, as providing an implicit guarantee on static initialization order would run the risk of constraining future development and making it more difficult.)

In the meantime, if you really need this locally, the patch is relatively simple right now. You can simply replace

MockObjectRegistry g_mock_object_registry;

with

MockObjectRegistry* GetMockObjectRegistry() {
  static std::atomic<MockObjectRegistry*> g_instance;
  MockObjectRegistry* instance = g_instance.load(std::memory_order_relaxed);
  if (instance == nullptr) {
    internal::MutexLock l(&internal::g_gmock_mutex);
    instance = new MockObjectRegistry();
    g_instance.store(instance, std::memory_order_relaxed);
    ::atexit([]() { delete g_instance.load(std::memory_order_relaxed); });
  }
  return instance;
}

// Ensures initialization occurs at the same time as other globals or earlier.
std::nullptr_t g_mock_object_ctor = ((void)GetMockObjectRegistry(), nullptr);

and then find/replace all uses of g_mock_object_registry with GetMockObjectRegistry().

@higher-performance higher-performance closed this as not planned Won't fix, can't repro, duplicate, stale Nov 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants