| 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) { | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 31 |   int pty, tty; | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 32 |   char name[32]; | 
 | 33 |   struct winsize w = { 123, 456, 9999, 999 }; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 34 |   ASSERT_EQ(0, openpty(&pty, &tty, name, nullptr, &w)); | 
 | 35 |   ASSERT_NE(-1, pty); | 
 | 36 |   ASSERT_NE(-1, tty); | 
 | 37 |   ASSERT_NE(pty, tty); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 38 |  | 
 | 39 |   char tty_name[32]; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 40 |   ASSERT_EQ(0, ttyname_r(tty, tty_name, sizeof(tty_name))); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 41 |   ASSERT_STREQ(tty_name, name); | 
 | 42 |  | 
 | 43 |   struct winsize w_actual; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 44 |   ASSERT_EQ(0, ioctl(tty, TIOCGWINSZ, &w_actual)); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 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 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 50 |   close(pty); | 
 | 51 |   close(tty); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 52 | } | 
 | 53 |  | 
 | 54 | TEST(pty, forkpty) { | 
 | 55 |   pid_t sid = getsid(0); | 
 | 56 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 57 |   int pty; | 
 | 58 |   pid_t pid = forkpty(&pty, 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 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 71 |   close(pty); | 
| Elliott Hughes | 65f0df7 | 2014-12-03 14:39:20 -0800 | [diff] [blame] | 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; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 76 |   int fd; | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 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)); | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 93 |     ASSERT_TRUE(android::base::ReadFully(arg->fd, buf, to_read)); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 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 |   } | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 102 |   close(arg->fd); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 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) { | 
| Elliott Hughes | bcaa454 | 2019-03-08 15:20:23 -0800 | [diff] [blame] | 112 |     GTEST_SKIP() << "This bug only happens on multiprocessors"; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 113 |   } | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 114 |   constexpr uint32_t TEST_DATA_COUNT = 2000000; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 115 |  | 
 | 116 |   // 1. Open raw pty. | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 117 |   int pty; | 
 | 118 |   int tty; | 
 | 119 |   ASSERT_EQ(0, openpty(&pty, &tty, nullptr, nullptr, nullptr)); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 120 |   termios tattr; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 121 |   ASSERT_EQ(0, tcgetattr(tty, &tattr)); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 122 |   cfmakeraw(&tattr); | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 123 |   ASSERT_EQ(0, tcsetattr(tty, TCSADRAIN, &tattr)); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 124 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 125 |   // 2. Make two threads running on different cpus: | 
 | 126 |   // pty thread uses first available cpu, and tty thread uses other cpus. | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 127 |   PtyReader_28979140_Arg arg; | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 128 |   arg.main_cpu_id = -1; | 
 | 129 |   for (int i = 0; i < CPU_SETSIZE; i++) { | 
 | 130 |     if (CPU_ISSET(i, &cpus)) { | 
 | 131 |       arg.main_cpu_id = i; | 
 | 132 |       break; | 
 | 133 |     } | 
 | 134 |   } | 
 | 135 |   ASSERT_GE(arg.main_cpu_id, 0); | 
 | 136 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 137 |   // 3. Create thread for tty reader. | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 138 |   pthread_t thread; | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 139 |   arg.fd = tty; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 140 |   arg.data_count = TEST_DATA_COUNT; | 
 | 141 |   arg.matched = true; | 
 | 142 |   ASSERT_EQ(0, pthread_create(&thread, nullptr, | 
 | 143 |                               reinterpret_cast<void*(*)(void*)>(PtyReader_28979140), | 
 | 144 |                               &arg)); | 
 | 145 |  | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 146 |   CPU_ZERO(&cpus); | 
| Alex Vakulenko | c14d7f0 | 2016-07-11 17:26:35 -0700 | [diff] [blame] | 147 |   CPU_SET(arg.main_cpu_id, &cpus); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 148 |   ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus)); | 
 | 149 |  | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 150 |   // 4. Send data to tty reader. | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 151 |   // Send a bunch of data at a time, so it is easier to catch the bug that some data isn't seen | 
 | 152 |   // by the reader thread on another cpu. | 
 | 153 |   uint32_t counter_buf[100]; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 154 |   uint32_t counter = 0; | 
 | 155 |   while (counter <= TEST_DATA_COUNT) { | 
| Yabin Cui | 57a4006 | 2017-11-16 15:25:12 -0800 | [diff] [blame] | 156 |     for (size_t i = 0; i < sizeof(counter_buf) / sizeof(counter_buf[0]); ++i) { | 
 | 157 |       counter_buf[i] = counter++; | 
 | 158 |     } | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 159 |     ASSERT_TRUE(android::base::WriteFully(pty, &counter_buf, sizeof(counter_buf))); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 160 |     ASSERT_TRUE(arg.matched) << "failed at count = " << counter; | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 161 |   } | 
 | 162 |   ASSERT_EQ(0, pthread_join(thread, nullptr)); | 
 | 163 |   ASSERT_TRUE(arg.finished); | 
 | 164 |   ASSERT_TRUE(arg.matched); | 
| Elliott Hughes | fce3187 | 2020-07-28 12:06:23 -0700 | [diff] [blame] | 165 |   close(pty); | 
| Yabin Cui | f35c6bc | 2016-06-09 16:36:28 -0700 | [diff] [blame] | 166 | } |