| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2014 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 17 | #include <pty.h> | 
|  | 18 |  | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 19 | #include <gtest/gtest.h> | 
|  | 20 |  | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 21 | #include <pthread.h> | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 22 | #include <sys/ioctl.h> | 
|  | 23 |  | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 24 | #include <atomic> | 
|  | 25 |  | 
|  | 26 | #include <android-base/file.h> | 
|  | 27 |  | 
| Elliott Hughes | 33697a0 | 2016-01-26 13:04:57 -0800 | [diff] [blame] | 28 | #include "utils.h" | 
|  | 29 |  | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 30 | TEST(pty, openpty) { | 
|  | 31 | int master, slave; | 
|  | 32 | char name[32]; | 
|  | 33 | struct winsize w = { 123, 456, 9999, 999 }; | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 34 | ASSERT_EQ(0, openpty(&master, &slave, name, nullptr, &w)); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 35 | ASSERT_NE(-1, master); | 
|  | 36 | ASSERT_NE(-1, slave); | 
|  | 37 | ASSERT_NE(master, slave); | 
|  | 38 |  | 
|  | 39 | char tty_name[32]; | 
|  | 40 | ASSERT_EQ(0, ttyname_r(slave, tty_name, sizeof(tty_name))); | 
|  | 41 | ASSERT_STREQ(tty_name, name); | 
|  | 42 |  | 
|  | 43 | struct winsize w_actual; | 
|  | 44 | ASSERT_EQ(0, ioctl(slave, TIOCGWINSZ, &w_actual)); | 
|  | 45 | ASSERT_EQ(w_actual.ws_row, w.ws_row); | 
|  | 46 | ASSERT_EQ(w_actual.ws_col, w.ws_col); | 
|  | 47 | ASSERT_EQ(w_actual.ws_xpixel, w.ws_xpixel); | 
|  | 48 | ASSERT_EQ(w_actual.ws_ypixel, w.ws_ypixel); | 
|  | 49 |  | 
|  | 50 | close(master); | 
|  | 51 | close(slave); | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | TEST(pty, forkpty) { | 
|  | 55 | pid_t sid = getsid(0); | 
|  | 56 |  | 
|  | 57 | int master; | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 58 | pid_t pid = forkpty(&master, nullptr, nullptr, nullptr); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 59 | ASSERT_NE(-1, pid); | 
|  | 60 |  | 
|  | 61 | if (pid == 0) { | 
|  | 62 | // We're the child. | 
|  | 63 | ASSERT_NE(sid, getsid(0)); | 
|  | 64 | _exit(0); | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | ASSERT_EQ(sid, getsid(0)); | 
|  | 68 |  | 
| Elliott Hughes | 33697a0 | 2016-01-26 13:04:57 -0800 | [diff] [blame] | 69 | AssertChildExited(pid, 0); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 70 |  | 
|  | 71 | close(master); | 
|  | 72 | } | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 73 |  | 
|  | 74 | struct PtyReader_28979140_Arg { | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 75 | int main_cpu_id; | 
|  | 76 | int slave_fd; | 
|  | 77 | uint32_t data_count; | 
|  | 78 | bool finished; | 
|  | 79 | std::atomic<bool> matched; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 80 | }; | 
|  | 81 |  | 
|  | 82 | static void PtyReader_28979140(PtyReader_28979140_Arg* arg) { | 
|  | 83 | arg->finished = false; | 
|  | 84 | cpu_set_t cpus; | 
|  | 85 | ASSERT_EQ(0, sched_getaffinity(0, sizeof(cpu_set_t), &cpus)); | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 86 | CPU_CLR(arg->main_cpu_id, &cpus); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 87 | ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus)); | 
|  | 88 |  | 
|  | 89 | uint32_t counter = 0; | 
|  | 90 | while (counter <= arg->data_count) { | 
|  | 91 | char buf[4096];  // Use big buffer to read to hit the bug more easily. | 
|  | 92 | size_t to_read = std::min(sizeof(buf), (arg->data_count + 1 - counter) * sizeof(uint32_t)); | 
|  | 93 | ASSERT_TRUE(android::base::ReadFully(arg->slave_fd, buf, to_read)); | 
|  | 94 | size_t num_of_value = to_read / sizeof(uint32_t); | 
|  | 95 | uint32_t* p = reinterpret_cast<uint32_t*>(buf); | 
|  | 96 | while (num_of_value-- > 0) { | 
|  | 97 | if (*p++ != counter++) { | 
|  | 98 | arg->matched = false; | 
|  | 99 | } | 
|  | 100 | } | 
|  | 101 | } | 
|  | 102 | close(arg->slave_fd); | 
|  | 103 | arg->finished = true; | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | TEST(pty, bug_28979140) { | 
|  | 107 | // This test is to test a kernel bug, which uses a lock free ring-buffer to | 
|  | 108 | // pass data through a raw pty, but missing necessary memory barriers. | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 109 | cpu_set_t cpus; | 
|  | 110 | ASSERT_EQ(0, sched_getaffinity(0, sizeof(cpu_set_t), &cpus)); | 
|  | 111 | if (CPU_COUNT(&cpus) < 2) { | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 112 | GTEST_LOG_(INFO) << "This test tests bug happens only on multiprocessors."; | 
|  | 113 | return; | 
|  | 114 | } | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 115 | constexpr uint32_t TEST_DATA_COUNT = 2000000; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 116 |  | 
|  | 117 | // 1. Open raw pty. | 
|  | 118 | int master; | 
|  | 119 | int slave; | 
|  | 120 | ASSERT_EQ(0, openpty(&master, &slave, nullptr, nullptr, nullptr)); | 
|  | 121 | termios tattr; | 
|  | 122 | ASSERT_EQ(0, tcgetattr(slave, &tattr)); | 
|  | 123 | cfmakeraw(&tattr); | 
|  | 124 | ASSERT_EQ(0, tcsetattr(slave, TCSADRAIN, &tattr)); | 
|  | 125 |  | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 126 | // 2. Make master thread and slave thread running on different cpus: | 
|  | 127 | // master thread uses first available cpu, and slave thread uses other cpus. | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 128 | PtyReader_28979140_Arg arg; | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 129 | arg.main_cpu_id = -1; | 
|  | 130 | for (int i = 0; i < CPU_SETSIZE; i++) { | 
|  | 131 | if (CPU_ISSET(i, &cpus)) { | 
|  | 132 | arg.main_cpu_id = i; | 
|  | 133 | break; | 
|  | 134 | } | 
|  | 135 | } | 
|  | 136 | ASSERT_GE(arg.main_cpu_id, 0); | 
|  | 137 |  | 
|  | 138 | // 3. Create thread for slave reader. | 
|  | 139 | pthread_t thread; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 140 | arg.slave_fd = slave; | 
|  | 141 | arg.data_count = TEST_DATA_COUNT; | 
|  | 142 | arg.matched = true; | 
|  | 143 | ASSERT_EQ(0, pthread_create(&thread, nullptr, | 
|  | 144 | reinterpret_cast<void*(*)(void*)>(PtyReader_28979140), | 
|  | 145 | &arg)); | 
|  | 146 |  | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 147 | CPU_ZERO(&cpus); | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 148 | CPU_SET(arg.main_cpu_id, &cpus); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 149 | ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus)); | 
|  | 150 |  | 
|  | 151 | // 4. Send data to slave. | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 152 | // Send a bunch of data at a time, so it is easier to catch the bug that some data isn't seen | 
|  | 153 | // by the reader thread on another cpu. | 
|  | 154 | uint32_t counter_buf[100]; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 155 | uint32_t counter = 0; | 
|  | 156 | while (counter <= TEST_DATA_COUNT) { | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 157 | for (size_t i = 0; i < sizeof(counter_buf) / sizeof(counter_buf[0]); ++i) { | 
|  | 158 | counter_buf[i] = counter++; | 
|  | 159 | } | 
|  | 160 | ASSERT_TRUE(android::base::WriteFully(master, &counter_buf, sizeof(counter_buf))); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 161 | ASSERT_TRUE(arg.matched) << "failed at count = " << counter; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 162 | } | 
|  | 163 | ASSERT_EQ(0, pthread_join(thread, nullptr)); | 
|  | 164 | ASSERT_TRUE(arg.finished); | 
|  | 165 | ASSERT_TRUE(arg.matched); | 
|  | 166 | close(master); | 
|  | 167 | } |