Browse Source

Kernel: Ignore closed fd's when considering select() unblock

This fixes a null RefPtr deref (which asserts) in the scheduler if a
file descriptor being select()'ed is closed by a second thread while
blocked in select().

Test: Kernel/null-deref-close-during-select.cpp
Andreas Kling 5 years ago
parent
commit
76c20642f0
2 changed files with 40 additions and 0 deletions
  1. 4 0
      Kernel/Scheduler.cpp
  2. 36 0
      Tests/Kernel/null-deref-close-during-select.cpp

+ 4 - 0
Kernel/Scheduler.cpp

@@ -179,10 +179,14 @@ bool Thread::SelectBlocker::should_unblock(Thread& thread, time_t now_sec, long
 
 
     auto& process = thread.process();
     auto& process = thread.process();
     for (int fd : m_select_read_fds) {
     for (int fd : m_select_read_fds) {
+        if (!process.m_fds[fd])
+            continue;
         if (process.m_fds[fd].description->can_read())
         if (process.m_fds[fd].description->can_read())
             return true;
             return true;
     }
     }
     for (int fd : m_select_write_fds) {
     for (int fd : m_select_write_fds) {
+        if (!process.m_fds[fd])
+            continue;
         if (process.m_fds[fd].description->can_write())
         if (process.m_fds[fd].description->can_write())
             return true;
             return true;
     }
     }

+ 36 - 0
Tests/Kernel/null-deref-close-during-select.cpp

@@ -0,0 +1,36 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+int pipefds[2];
+
+int main(int, char**)
+{
+    pipe(pipefds);
+
+    pthread_t tid;
+    pthread_create(
+        &tid, nullptr, [](void*) -> void* {
+            sleep(1);
+            printf("ST: close()\n");
+            close(pipefds[1]);
+            pthread_exit(nullptr);
+            return nullptr;
+        },
+        nullptr);
+
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(pipefds[1], &rfds);
+
+    printf("MT: select()\n");
+    int rc = select(pipefds[1] + 1, &rfds, nullptr, nullptr, nullptr);
+    if (rc < 0) {
+        perror("select");
+        return 1;
+    }
+
+    printf("ok\n");
+    return 0;
+}