[libc-commits] [libc] c88708e - [libc] Fix the values of TCOFLUSH and TCIOFLUSH (#236513)

via libc-commits libc-commits at lists.llvm.org
Thu Oct 29 10:50:14 PDT 2026


Author: Mike Lothian
Date: 2026-10-29T10:50:09-07:00
New Revision: c88708ea8e617eef102e0e6390f88adcc6fe3a5f

URL: https://github.com/llvm/llvm-project/commit/c88708ea8e617eef102e0e6390f88adcc6fe3a5f
DIFF: https://github.com/llvm/llvm-project/commit/c88708ea8e617eef102e0e6390f88adcc6fe3a5f.diff

LOG: [libc] Fix the values of TCOFLUSH and TCIOFLUSH (#236513)

tcflush passes its argument straight to the TCFLSH ioctl, so the values
must be the kernel's, which are 0 for TCIFLUSH, 1 for TCOFLUSH and 2 for
TCIOFLUSH. The last two were swapped, so a caller asking to discard
unsent output also discarded the input it had not read yet

Add a test that leaves a line of input waiting on a pseudoterminal and
checks which of the three flushes drops it

AI disclosure: this patch was written with Claude Code. I reviewed the
change and ran the new test; it fails without the fix and passes with it

Assisted-by: Claude

Added: 
    libc/test/src/termios/tcflush_test.cpp

Modified: 
    libc/include/llvm-libc-macros/linux/termios-macros.h
    libc/test/src/termios/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/include/llvm-libc-macros/linux/termios-macros.h b/libc/include/llvm-libc-macros/linux/termios-macros.h
index 668cfe27abaabf..4998f3dc3a32a2 100644
--- a/libc/include/llvm-libc-macros/linux/termios-macros.h
+++ b/libc/include/llvm-libc-macros/linux/termios-macros.h
@@ -1,10 +1,15 @@
-//===-- Definition of macros from termios.h -------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of macros from termios.h.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_MACROS_LINUX_TERMIOS_MACROS_H
 #define LLVM_LIBC_MACROS_LINUX_TERMIOS_MACROS_H
@@ -155,8 +160,8 @@
 
 // Symbolic constants for use with tcflush function.
 #define TCIFLUSH 0  // Flush pending input
-#define TCIOFLUSH 1 // Flush pending input and unstransmitted output
-#define TCOFLUSH 2  // Flush unstransmitted output
+#define TCOFLUSH 1  // Flush untransmitted output
+#define TCIOFLUSH 2 // Flush pending input and untransmitted output
 
 // Symbolic constantf for use with tcflow function.
 #define TCOOFF 0 // Transmit a STOP character, intended to suspend input data

diff  --git a/libc/test/src/termios/CMakeLists.txt b/libc/test/src/termios/CMakeLists.txt
index b85203367b603d..30096fececc91b 100644
--- a/libc/test/src/termios/CMakeLists.txt
+++ b/libc/test/src/termios/CMakeLists.txt
@@ -24,6 +24,31 @@ add_libc_test(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_test(
+  tcflush_test
+  SUITE
+    libc-termios-tests
+  SRCS
+    tcflush_test.cpp
+  DEPENDS
+    libc.hdr.fcntl_macros
+    libc.hdr.poll_macros
+    libc.hdr.sys_ioctl_macros
+    libc.hdr.termios_macros
+    libc.hdr.types.struct_pollfd
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.fcntl.open
+    libc.src.poll.poll
+    libc.src.stdio.snprintf
+    libc.src.sys.ioctl.ioctl
+    libc.src.termios.tcflush
+    libc.src.unistd.close
+    libc.src.unistd.write
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_test(
   tcgetwinsize_test
   SUITE

diff  --git a/libc/test/src/termios/tcflush_test.cpp b/libc/test/src/termios/tcflush_test.cpp
new file mode 100644
index 00000000000000..23b1c8f3a23bc7
--- /dev/null
+++ b/libc/test/src/termios/tcflush_test.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for tcflush.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/poll_macros.h"
+#include "hdr/sys_ioctl_macros.h"
+#include "hdr/termios_macros.h"
+#include "hdr/types/struct_pollfd.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/open.h"
+#include "src/poll/poll.h"
+#include "src/stdio/snprintf.h"
+#include "src/sys/ioctl/ioctl.h"
+#include "src/termios/tcflush.h"
+#include "src/unistd/close.h"
+#include "src/unistd/write.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace {
+
+bool has_pending_input(int fd) {
+  int pending = 0;
+  LIBC_NAMESPACE::ioctl(fd, FIONREAD, &pending);
+  return pending > 0;
+}
+
+} // namespace
+
+// Each test gets a pseudoterminal with a line of input waiting on the
+// subsidiary side. The descriptors are closed in TearDown, so no path out of
+// a test or of the setup leaks them. When there is no pseudoterminal to use,
+// have_pty stays false and the tests return without checking anything.
+class LlvmLibcTcflushTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
+public:
+  int manager = -1;
+  int subsidiary = -1;
+  bool have_pty = false;
+
+  void SetUp() override {
+    LIBC_NAMESPACE::testing::ErrnoCheckingTest::SetUp();
+    have_pty = open_pseudoterminal_with_input();
+    if (have_pty)
+      ASSERT_ERRNO_SUCCESS();
+    else
+      LIBC_NAMESPACE::libc_errno = 0;
+  }
+
+  void TearDown() override {
+    if (subsidiary >= 0)
+      LIBC_NAMESPACE::close(subsidiary);
+    if (manager >= 0)
+      LIBC_NAMESPACE::close(manager);
+    LIBC_NAMESPACE::testing::ErrnoCheckingTest::TearDown();
+  }
+
+private:
+  bool open_pseudoterminal_with_input() {
+    manager = LIBC_NAMESPACE::open("/dev/ptmx", O_RDWR | O_NOCTTY);
+    if (manager < 0)
+      return false;
+
+    int unlock = 0;
+    unsigned int number = 0;
+    if (LIBC_NAMESPACE::ioctl(manager, TIOCSPTLCK, &unlock) != 0 ||
+        LIBC_NAMESPACE::ioctl(manager, TIOCGPTN, &number) != 0)
+      return false;
+
+    char path[32];
+    LIBC_NAMESPACE::snprintf(path, sizeof(path), "/dev/pts/%u", number);
+    subsidiary = LIBC_NAMESPACE::open(path, O_RDWR | O_NOCTTY);
+    if (subsidiary < 0)
+      return false;
+
+    if (LIBC_NAMESPACE::write(manager, "input\n", 6) != 6)
+      return false;
+
+    // The terminal hands input to the subsidiary side in the background, and a
+    // line is readable once the newline has been through.
+    struct pollfd pfd = {subsidiary, POLLIN, 0};
+    return LIBC_NAMESPACE::poll(&pfd, 1, 5000) == 1;
+  }
+};
+
+TEST_F(LlvmLibcTcflushTest, OutputFlushKeepsInput) {
+  if (!have_pty)
+    return;
+
+  ASSERT_TRUE(has_pending_input(subsidiary));
+  ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCOFLUSH), Succeeds(0));
+  ASSERT_TRUE(has_pending_input(subsidiary));
+}
+
+TEST_F(LlvmLibcTcflushTest, InputFlushDropsInput) {
+  if (!have_pty)
+    return;
+
+  ASSERT_TRUE(has_pending_input(subsidiary));
+  ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIFLUSH), Succeeds(0));
+  ASSERT_FALSE(has_pending_input(subsidiary));
+}
+
+TEST_F(LlvmLibcTcflushTest, BothFlushDropsInput) {
+  if (!have_pty)
+    return;
+
+  ASSERT_TRUE(has_pending_input(subsidiary));
+  ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIOFLUSH), Succeeds(0));
+  ASSERT_FALSE(has_pending_input(subsidiary));
+}


        


More information about the libc-commits mailing list