|  | // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "tools/fidlcat/interception_tests/interception_workflow_test.h" | 
|  |  | 
|  | namespace fidlcat { | 
|  |  | 
|  | // zx_futex_wait tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexWait(int64_t result, std::string_view result_name, | 
|  | const zx_futex_t* value_ptr, zx_futex_t current_value, | 
|  | zx_handle_t new_futex_owner, zx_time_t deadline) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_wait", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(current_value); | 
|  | value->AddInput(new_futex_owner); | 
|  | value->AddInput(deadline); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_WAIT_DISPLAY_TEST_CONTENT(result, expected)                                    \ | 
|  | zx_futex_t value = kFutex;                                                                 \ | 
|  | PerformDisplayTest("$plt(zx_futex_wait)",                                                  \ | 
|  | ZxFutexWait(result, #result, &value, value, kHandle, ZX_TIME_INFINITE), \ | 
|  | expected); | 
|  |  | 
|  | #define FUTEX_WAIT_DISPLAY_TEST(name, errno, expected)                                            \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) { FUTEX_WAIT_DISPLAY_TEST_CONTENT(errno, expected); } \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) { FUTEX_WAIT_DISPLAY_TEST_CONTENT(errno, expected); } | 
|  |  | 
|  | FUTEX_WAIT_DISPLAY_TEST(ZxFutexWait, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_wait(" | 
|  | "value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "current_value: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "new_futex_owner: \x1B[32mhandle\x1B[0m = \x1B[31mcefa1db0\x1B[0m, " | 
|  | "deadline: \x1B[32mtime\x1B[0m = \x1B[34mZX_TIME_INFINITE\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m\n"); | 
|  |  | 
|  | // zx_futex_wake tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexWake(int64_t result, std::string_view result_name, | 
|  | const zx_futex_t* value_ptr, uint32_t wake_count) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_wake", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(wake_count); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_WAKE_DISPLAY_TEST_CONTENT(result, expected) \ | 
|  | zx_futex_t value = kFutex;                              \ | 
|  | PerformDisplayTest("$plt(zx_futex_wake)", ZxFutexWake(result, #result, &value, 3), expected); | 
|  |  | 
|  | #define FUTEX_WAKE_DISPLAY_TEST(name, errno, expected)                                            \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) { FUTEX_WAKE_DISPLAY_TEST_CONTENT(errno, expected); } \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) { FUTEX_WAKE_DISPLAY_TEST_CONTENT(errno, expected); } | 
|  |  | 
|  | FUTEX_WAKE_DISPLAY_TEST(ZxFutexWake, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_wake(" | 
|  | "value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "wake_count: \x1B[32muint32\x1B[0m = \x1B[34m3\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m\n"); | 
|  |  | 
|  | // zx_futex_requeue tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexRequeue(int64_t result, std::string_view result_name, | 
|  | const zx_futex_t* value_ptr, uint32_t wake_count, | 
|  | zx_futex_t current_value, | 
|  | const zx_futex_t* requeue_ptr, | 
|  | uint32_t requeue_count, | 
|  | zx_handle_t new_requeue_owner) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_requeue", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(wake_count); | 
|  | value->AddInput(current_value); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(requeue_ptr)); | 
|  | value->AddInput(requeue_count); | 
|  | value->AddInput(new_requeue_owner); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_REQUEUE_DISPLAY_TEST_CONTENT(result, expected)                                  \ | 
|  | zx_futex_t value = kFutex;                                                                  \ | 
|  | zx_futex_t requeue = kFutex2;                                                               \ | 
|  | PerformDisplayTest("$plt(zx_futex_requeue)",                                                \ | 
|  | ZxFutexRequeue(result, #result, &value, 2, value, &requeue, 3, kHandle), \ | 
|  | expected); | 
|  |  | 
|  | #define FUTEX_REQUEUE_DISPLAY_TEST(name, errno, expected) \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) {             \ | 
|  | FUTEX_REQUEUE_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | }                                                       \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) { FUTEX_REQUEUE_DISPLAY_TEST_CONTENT(errno, expected); } | 
|  |  | 
|  | FUTEX_REQUEUE_DISPLAY_TEST(ZxFutexRequeue, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_requeue(" | 
|  | "value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "wake_count: \x1B[32muint32\x1B[0m = \x1B[34m2\x1B[0m, " | 
|  | "current_value: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "requeue_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m98765\x1B[0m, " | 
|  | "requeue_count: \x1B[32muint32\x1B[0m = \x1B[34m3\x1B[0m, " | 
|  | "new_requeue_owner: \x1B[32mhandle\x1B[0m = \x1B[31mcefa1db0\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m\n"); | 
|  |  | 
|  | // zx_futex_wake_single_owner tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexWakeSingleOwner(int64_t result, std::string_view result_name, | 
|  | const zx_futex_t* value_ptr) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_wake_single_owner", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_WAKE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(result, expected) \ | 
|  | zx_futex_t value = kFutex;                                           \ | 
|  | PerformDisplayTest("$plt(zx_futex_wake_single_owner)",               \ | 
|  | ZxFutexWakeSingleOwner(result, #result, &value), expected); | 
|  |  | 
|  | #define FUTEX_WAKE_SINGLE_OWNER_DISPLAY_TEST(name, errno, expected) \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) {                       \ | 
|  | FUTEX_WAKE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | }                                                                 \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) {                       \ | 
|  | FUTEX_WAKE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | } | 
|  |  | 
|  | FUTEX_WAKE_SINGLE_OWNER_DISPLAY_TEST( | 
|  | ZxFutexWakeSingleOwner, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_wake_single_owner(value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m\n"); | 
|  |  | 
|  | // zx_futex_requeue_single_owner tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexRequeueSingleOwner( | 
|  | int64_t result, std::string_view result_name, const zx_futex_t* value_ptr, | 
|  | zx_futex_t current_value, const zx_futex_t* requeue_ptr, uint32_t requeue_count, | 
|  | zx_handle_t new_requeue_owner) { | 
|  | auto value = | 
|  | std::make_unique<SystemCallTest>("zx_futex_requeue_single_owner", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(current_value); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(requeue_ptr)); | 
|  | value->AddInput(requeue_count); | 
|  | value->AddInput(new_requeue_owner); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_REQUEUE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(result, expected) \ | 
|  | zx_futex_t value = kFutex;                                              \ | 
|  | zx_futex_t requeue = kFutex2;                                           \ | 
|  | PerformDisplayTest(                                                     \ | 
|  | "$plt(zx_futex_requeue_single_owner)",                              \ | 
|  | ZxFutexRequeueSingleOwner(result, #result, &value, value, &requeue, 3, kHandle), expected); | 
|  |  | 
|  | #define FUTEX_REQUEUE_SINGLE_OWNER_DISPLAY_TEST(name, errno, expected) \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) {                          \ | 
|  | FUTEX_REQUEUE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | }                                                                    \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) {                          \ | 
|  | FUTEX_REQUEUE_SINGLE_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | } | 
|  |  | 
|  | FUTEX_REQUEUE_SINGLE_OWNER_DISPLAY_TEST( | 
|  | ZxFutexRequeueSingleOwner, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_requeue_single_owner(" | 
|  | "value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "current_value: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "requeue_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m98765\x1B[0m, " | 
|  | "requeue_count: \x1B[32muint32\x1B[0m = \x1B[34m3\x1B[0m, " | 
|  | "new_requeue_owner: \x1B[32mhandle\x1B[0m = \x1B[31mcefa1db0\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m\n"); | 
|  |  | 
|  | // zx_futex_get_owner tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexGetOwner(int64_t result, std::string_view result_name, | 
|  | const zx_futex_t* value_ptr, zx_koid_t* koid) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_get_owner", result, result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(koid)); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_GET_OWNER_DISPLAY_TEST_CONTENT(result, expected)                                    \ | 
|  | zx_futex_t value = kFutex;                                                                      \ | 
|  | zx_koid_t koid = kKoid;                                                                         \ | 
|  | PerformDisplayTest("$plt(zx_futex_get_owner)", ZxFutexGetOwner(result, #result, &value, &koid), \ | 
|  | expected); | 
|  |  | 
|  | #define FUTEX_GET_OWNER_DISPLAY_TEST(name, errno, expected) \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) {               \ | 
|  | FUTEX_GET_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | }                                                         \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) {               \ | 
|  | FUTEX_GET_OWNER_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | } | 
|  |  | 
|  | FUTEX_GET_OWNER_DISPLAY_TEST( | 
|  | ZxFutexGetOwner, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_get_owner(value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m)\n" | 
|  | "  -> \x1B[32mZX_OK\x1B[0m (koid: \x1B[32mzx_koid_t\x1B[0m = \x1B[31m4252\x1B[0m)\n"); | 
|  |  | 
|  | // zx_futex_wake_handle_close_thread_exit tests. | 
|  |  | 
|  | std::unique_ptr<SystemCallTest> ZxFutexWakeHandleCloseThreadExit( | 
|  | int64_t result, std::string_view result_name, const zx_futex_t* value_ptr, uint32_t wake_count, | 
|  | int32_t new_value, zx_handle_t close_handle) { | 
|  | auto value = std::make_unique<SystemCallTest>("zx_futex_wake_handle_close_thread_exit", result, | 
|  | result_name); | 
|  | value->AddInput(reinterpret_cast<uint64_t>(value_ptr)); | 
|  | value->AddInput(wake_count); | 
|  | value->AddInput(new_value); | 
|  | value->AddInput(close_handle); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | #define FUTEX_WAKE_HANDLE_CLOSE_THREAD_EXIT_DISPLAY_TEST_CONTENT(result, expected) \ | 
|  | zx_futex_t value = kFutex;                                                       \ | 
|  | PerformNoReturnDisplayTest(                                                      \ | 
|  | "$plt(zx_futex_wake_handle_close_thread_exit)",                              \ | 
|  | ZxFutexWakeHandleCloseThreadExit(result, #result, &value, 2, -1, kHandle), expected); | 
|  |  | 
|  | #define FUTEX_WAKE_HANDLE_CLOSE_THREAD_EXIT_DISPLAY_TEST(name, errno, expected) \ | 
|  | TEST_F(InterceptionWorkflowTestX64, name) {                                   \ | 
|  | FUTEX_WAKE_HANDLE_CLOSE_THREAD_EXIT_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | }                                                                             \ | 
|  | TEST_F(InterceptionWorkflowTestArm, name) {                                   \ | 
|  | FUTEX_WAKE_HANDLE_CLOSE_THREAD_EXIT_DISPLAY_TEST_CONTENT(errno, expected);  \ | 
|  | } | 
|  |  | 
|  | FUTEX_WAKE_HANDLE_CLOSE_THREAD_EXIT_DISPLAY_TEST( | 
|  | ZxFutexWakeHandleCloseThreadExit, ZX_OK, | 
|  | "\n" | 
|  | "test_3141 \x1B[31m3141\x1B[0m:\x1B[31m8764\x1B[0m " | 
|  | "zx_futex_wake_handle_close_thread_exit(" | 
|  | "value_ptr: \x1B[32mzx_futex_t\x1B[0m = \x1B[31m56789\x1B[0m, " | 
|  | "wake_count: \x1B[32muint32\x1B[0m = \x1B[34m2\x1B[0m, " | 
|  | "new_value: \x1B[32mint32\x1B[0m = \x1B[34m-1\x1B[0m, " | 
|  | "close_handle: \x1B[32mhandle\x1B[0m = \x1B[31mcefa1db0\x1B[0m)\n"); | 
|  |  | 
|  | }  // namespace fidlcat |