xref: /aosp_15_r20/external/cronet/base/memory/shared_memory_switch_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/memory/shared_memory_switch.h"
6 
7 #include "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/metrics/persistent_histogram_allocator.h"
10 #include "base/process/launch.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/multiprocess_test.h"
15 #include "base/test/scoped_feature_list.h"
16 #include "base/test/test_timeouts.h"
17 #include "base/unguessable_token.h"
18 #include "build/build_config.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "testing/multiprocess_func_list.h"
21 
22 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
23 #include "base/files/platform_file.h"
24 #include "base/posix/global_descriptors.h"
25 #endif
26 
27 namespace base {
28 namespace shared_memory {
29 namespace {
30 
31 constexpr char kSharedMemoryData[] = "shared_memory_data";
32 constexpr char kSharedMemoryGUID[] = "shared_memory_guid";
33 constexpr char kIsReadOnly[] = "is_read_only";
34 constexpr size_t kArbitrarySize = 64 << 10;
35 
36 #if BUILDFLAG(IS_APPLE)
37 constexpr MachPortsForRendezvous::key_type kArbitraryRendezvousKey = 'smsh';
38 #elif BUILDFLAG(IS_POSIX)
39 constexpr GlobalDescriptors::Key kArbitraryDescriptorKey = 42;
40 #endif
41 
42 }  // namespace
43 
MULTIPROCESS_TEST_MAIN(InitFromSwitchValue)44 MULTIPROCESS_TEST_MAIN(InitFromSwitchValue) {
45   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
46   CHECK(command_line.HasSwitch(kSharedMemoryData));
47   CHECK(command_line.HasSwitch(kSharedMemoryGUID));
48   CHECK(command_line.HasSwitch(kIsReadOnly));
49 
50   // On POSIX we generally use the descriptor map to look up inherited handles.
51   // On most POSIX platforms we have to manually sure the mapping is updated,
52   // for the purposes of this test.
53   //
54   // Note:
55   //  - This doesn't apply on Apple platforms (which use Rendezvous Keys)
56   //  - On Android the global descriptor table is managed by the launcher
57   //    service, so we don't have to manually update the mapping here.
58 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
59   GlobalDescriptors::GetInstance()->Set(
60       kArbitraryDescriptorKey,
61       kArbitraryDescriptorKey + GlobalDescriptors::kBaseDescriptor);
62 #endif
63   const std::string shared_memory_data =
64       command_line.GetSwitchValueASCII(kSharedMemoryData);
65   const bool is_read_only =
66       command_line.GetSwitchValueASCII(kIsReadOnly) == "true";
67   const std::string guid_string =
68       command_line.GetSwitchValueASCII(kSharedMemoryGUID);
69 
70   if (is_read_only) {
71     auto read_only_region = ReadOnlySharedMemoryRegionFrom(shared_memory_data);
72     CHECK(read_only_region.has_value());
73     CHECK_EQ(guid_string, read_only_region.value().GetGUID().ToString());
74   } else {
75     auto unsafe_region = UnsafeSharedMemoryRegionFrom(shared_memory_data);
76     CHECK(unsafe_region.has_value());
77     CHECK_EQ(guid_string, unsafe_region.value().GetGUID().ToString());
78   }
79 
80   return 0;
81 }
82 
83 // The test suite takes two boolean parameters.
84 using SharedMemorySwitchTest = ::testing::TestWithParam<std::tuple<bool, bool>>;
85 
86 // Instantiate tests for all combinations of the two boolean parameters.
87 INSTANTIATE_TEST_SUITE_P(All,
88                          SharedMemorySwitchTest,
89                          ::testing::Combine(testing::Bool(), testing::Bool()));
90 
TEST_P(SharedMemorySwitchTest,PassViaSwitch)91 TEST_P(SharedMemorySwitchTest, PassViaSwitch) {
92   const bool read_only = std::get<0>(GetParam());
93   const bool elevated = std::get<1>(GetParam());
94 
95   SCOPED_TRACE(
96       base::StringPrintf("read_only=%d; elevated=%d", read_only, elevated));
97 
98   // Create a shared memory region(s) to pass.
99   auto unsafe_region = UnsafeSharedMemoryRegion::Create(kArbitrarySize);
100   auto read_only_region = ReadOnlySharedMemoryRegion::Create(kArbitrarySize);
101   ASSERT_TRUE(unsafe_region.IsValid());
102   ASSERT_TRUE(read_only_region.IsValid());
103 
104   // Initialize the command line and launch options.
105   CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
106   command_line.AppendSwitchASCII(
107       kSharedMemoryGUID,
108       (read_only ? read_only_region.region.GetGUID() : unsafe_region.GetGUID())
109           .ToString());
110   command_line.AppendSwitchASCII(kIsReadOnly, read_only ? "true" : "false");
111   LaunchOptions launch_options;
112 
113   // On windows, check both the elevated and non-elevated launches.
114 #if BUILDFLAG(IS_WIN)
115   launch_options.start_hidden = true;
116   launch_options.elevated = elevated;
117 #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
118   ScopedFD descriptor_to_share;
119 #endif
120 
121   // Update the launch parameters.
122   if (read_only) {
123     AddToLaunchParameters(kSharedMemoryData,
124                           read_only_region.region.Duplicate(),
125 #if BUILDFLAG(IS_APPLE)
126                           kArbitraryRendezvousKey,
127 #elif BUILDFLAG(IS_POSIX)
128                           kArbitraryDescriptorKey, descriptor_to_share,
129 #endif
130                           &command_line, &launch_options);
131   } else {
132     AddToLaunchParameters(kSharedMemoryData, unsafe_region.Duplicate(),
133 #if BUILDFLAG(IS_APPLE)
134                           kArbitraryRendezvousKey,
135 #elif BUILDFLAG(IS_POSIX)
136                           kArbitraryDescriptorKey, descriptor_to_share,
137 #endif
138                           &command_line, &launch_options);
139   }
140 
141   // The metrics shared memory handle should be added to the command line.
142   ASSERT_TRUE(command_line.HasSwitch(kSharedMemoryData));
143   SCOPED_TRACE(command_line.GetSwitchValueASCII(kSharedMemoryData));
144 
145 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
146   // On posix, AddToLaunchParameters() ignores the launch options and instead
147   // returns the descriptor to be shared. This is because the browser child
148   // launcher helper manages a separate list of files to share via the zygote,
149   // if available. If, like in this test scenario, there's ultimately no zygote
150   // to use, launch helper updates the launch options to share the descriptor
151   // mapping relative to a base descriptor.
152   launch_options.fds_to_remap.emplace_back(descriptor_to_share.get(),
153                                            kArbitraryDescriptorKey);
154 #if !BUILDFLAG(IS_ANDROID)
155   for (auto& pair : launch_options.fds_to_remap) {
156     pair.second += base::GlobalDescriptors::kBaseDescriptor;
157   }
158 #endif  // !BUILDFLAG(IS_ANDROID)
159 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
160 
161   // Launch the child process.
162   Process process = SpawnMultiProcessTestChild("InitFromSwitchValue",
163                                                command_line, launch_options);
164 
165   // The child process returns non-zero if it could not open the shared memory
166   // region based on the launch parameters.
167   int exit_code;
168   EXPECT_TRUE(WaitForMultiprocessTestChildExit(
169       process, TestTimeouts::action_timeout(), &exit_code));
170   EXPECT_EQ(0, exit_code);
171 }
172 
173 }  // namespace shared_memory
174 }  // namespace base
175