Merge "Add a checksum to jmp_buf on mips and mips64."
diff --git a/libc/Android.bp b/libc/Android.bp
index ca8a4d5..8ec2b92 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1279,6 +1279,7 @@
         "bionic/ftruncate.cpp",
         "bionic/futimens.cpp",
         "bionic/getcwd.cpp",
+        "bionic/getdomainname.cpp",
         "bionic/gethostname.cpp",
         "bionic/getpgrp.cpp",
         "bionic/getpid.cpp",
diff --git a/libc/arch-x86/bionic/setjmp.S b/libc/arch-x86/bionic/setjmp.S
index 86e6e3c..efb6459 100644
--- a/libc/arch-x86/bionic/setjmp.S
+++ b/libc/arch-x86/bionic/setjmp.S
@@ -32,6 +32,21 @@
 
 #include <private/bionic_asm.h>
 
+// The internal structure of a jmp_buf is totally private.
+// Current layout (changes from release to release):
+//
+// word   name            description
+// 0      edx             registers
+// 1      ebx
+// 2      esp
+// 3      ebp
+// 4      esi
+// 5      edi
+// 6      sigmask         signal mask (not used with _setjmp / _longjmp)
+// 7      sigflag/cookie  setjmp cookie in top 31 bits, signal mask flag in low bit
+// 8      checksum        checksum of the core registers, to give better error messages.
+// 9      reserved
+
 #define _JB_EDX 0
 #define _JB_EBX 1
 #define _JB_ESP 2
@@ -40,6 +55,7 @@
 #define _JB_EDI 5
 #define _JB_SIGMASK 6
 #define _JB_SIGFLAG 7
+#define _JB_CHECKSUM 8
 
 .macro m_mangle_registers reg
   xorl \reg,%edx
@@ -54,6 +70,13 @@
   m_mangle_registers \reg
 .endm
 
+.macro m_calculate_checksum dst, src
+  movl $0, \dst
+  .irp i,0,1,2,3,4,5
+    xorl (\i*4)(\src), \dst
+  .endr
+.endm
+
 ENTRY(setjmp)
   movl 4(%esp),%ecx
   mov $1,%eax
@@ -111,13 +134,22 @@
   movl %edi,(_JB_EDI * 4)(%ecx)
   m_unmangle_registers %eax
 
+  m_calculate_checksum %eax, %ecx
+  movl %eax, (_JB_CHECKSUM * 4)(%ecx)
+
   xorl %eax,%eax
   ret
 END(sigsetjmp)
 
 ENTRY(siglongjmp)
-  // Do we have a signal mask to restore?
   movl 4(%esp),%edx
+
+  // Check the checksum before doing anything.
+  m_calculate_checksum %eax, %edx
+  xorl (_JB_CHECKSUM * 4)(%edx), %eax
+  jnz 3f
+
+  // Do we have a signal mask to restore?
   movl (_JB_SIGFLAG * 4)(%edx), %eax
   testl $1,%eax
   jz 1f
@@ -165,6 +197,11 @@
 2:
   movl %ecx,0(%esp)
   ret
+
+3:
+  PIC_PROLOGUE
+  pushl (_JB_SIGMASK * 4)(%edx)
+  call PIC_PLT(__bionic_setjmp_checksum_mismatch)
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/libc/arch-x86_64/bionic/setjmp.S b/libc/arch-x86_64/bionic/setjmp.S
index 56ebb07..34b4365 100644
--- a/libc/arch-x86_64/bionic/setjmp.S
+++ b/libc/arch-x86_64/bionic/setjmp.S
@@ -35,8 +35,22 @@
 
 #include <private/bionic_asm.h>
 
-// These are only the callee-saved registers. Code calling setjmp
-// will expect the rest to be clobbered anyway.
+
+// The internal structure of a jmp_buf is totally private.
+// Current layout (changes from release to release):
+//
+// word   name            description
+// 0      rbx             registers
+// 1      rbp
+// 2      r12
+// 3      r13
+// 4      r14
+// 5      r15
+// 6      rsp
+// 7      pc
+// 8      sigflag/cookie  setjmp cookie in top 31 bits, signal mask flag in low bit
+// 9      sigmask         signal mask (includes rt signals as well)
+// 10     checksum        checksum of the core registers, to give better error messages.
 
 #define _JB_RBX 0
 #define _JB_RBP 1
@@ -48,9 +62,10 @@
 #define _JB_PC 7
 #define _JB_SIGFLAG 8
 #define _JB_SIGMASK 9
-#define _JB_SIGMASK_RT 10 // sigprocmask will write here too.
+#define _JB_CHECKSUM 10
 
 #define MANGLE_REGISTERS 1
+
 .macro m_mangle_registers reg
 #if MANGLE_REGISTERS
   xorq \reg,%rbx
@@ -68,6 +83,12 @@
   m_mangle_registers \reg
 .endm
 
+.macro m_calculate_checksum dst, src
+  movq $0, \dst
+  .irp i,0,1,2,3,4,5,6,7
+    xorq (\i*8)(\src), \dst
+  .endr
+.endm
 
 ENTRY(setjmp)
   movl $1,%esi
@@ -118,6 +139,9 @@
   movq %r11,(_JB_PC  * 8)(%rdi)
   m_unmangle_registers %rax
 
+  m_calculate_checksum %rax, %rdi
+  movq %rax, (_JB_CHECKSUM * 8)(%rdi)
+
   xorl %eax,%eax
   ret
 END(sigsetjmp)
@@ -127,6 +151,10 @@
   movq %rdi,%r12
   pushq %rsi // Push 'value'.
 
+  m_calculate_checksum %rax, %rdi
+  xorq (_JB_CHECKSUM * 8)(%rdi), %rax
+  jnz 3f
+
   // Do we need to restore the signal mask?
   movq (_JB_SIGFLAG * 8)(%rdi), %rdi
   pushq %rdi // Push cookie
@@ -172,6 +200,9 @@
 1:
   movq %r11,0(%rsp)
   ret
+
+3:
+  call PIC_PLT(__bionic_setjmp_checksum_mismatch)
 END(siglongjmp)
 
 ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/libc/bionic/clock_nanosleep.cpp b/libc/bionic/clock_nanosleep.cpp
index 8e2146f..eade850 100644
--- a/libc/bionic/clock_nanosleep.cpp
+++ b/libc/bionic/clock_nanosleep.cpp
@@ -33,6 +33,8 @@
 extern "C" int ___clock_nanosleep(clockid_t, int, const timespec*, timespec*);
 
 int clock_nanosleep(clockid_t clock_id, int flags, const timespec* in, timespec* out) {
+  if (clock_id == CLOCK_THREAD_CPUTIME_ID) return EINVAL;
+
   ErrnoRestorer errno_restorer;
   return (___clock_nanosleep(clock_id, flags, in, out) == 0) ? 0 : errno;
 }
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index a7c3fb0..2fc8af0 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -68,7 +68,7 @@
 static void* dlopen_ext(const char* filename, int flags,
                         const android_dlextinfo* extinfo, void* caller_addr) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
-  soinfo* result = do_dlopen(filename, flags, extinfo, caller_addr);
+  void* result = do_dlopen(filename, flags, extinfo, caller_addr);
   if (result == nullptr) {
     __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
     return nullptr;
@@ -86,8 +86,6 @@
   return dlopen_ext(filename, flags, nullptr, caller_addr);
 }
 
-extern android_namespace_t* g_anonymous_namespace;
-
 void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
   void* result;
@@ -116,9 +114,11 @@
 
 int dlclose(void* handle) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
-  do_dlclose(reinterpret_cast<soinfo*>(handle));
-  // dlclose has no defined errors.
-  return 0;
+  int result = do_dlclose(handle);
+  if (result != 0) {
+    __bionic_format_dlerror("dlclose failed", linker_get_error_buffer());
+  }
+  return result;
 }
 
 int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) {
@@ -263,6 +263,7 @@
     __libdl_info->local_group_root_ = __libdl_info;
     __libdl_info->soname_ = "libdl.so";
     __libdl_info->target_sdk_version_ = __ANDROID_API__;
+    __libdl_info->generate_handle();
 #if defined(__work_around_b_24465209__)
     strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_));
 #endif
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 88386b0..092e831 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -43,7 +43,7 @@
     return *this;
   }
 
-  T* operator*() {
+  T* const operator*() {
     return entry_->element;
   }
 
@@ -190,15 +190,15 @@
     return nullptr;
   }
 
-  iterator begin() {
+  iterator begin() const {
     return iterator(head_);
   }
 
-  iterator end() {
+  iterator end() const {
     return iterator(nullptr);
   }
 
-  iterator find(T* value) {
+  iterator find(T* value) const {
     for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
       if (e->element == value) {
         return iterator(e);
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 2cdfd2c..855b32b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -103,7 +103,23 @@
     permitted_paths_ = permitted_paths;
   }
 
-  soinfo::soinfo_list_t& soinfo_list() { return soinfo_list_; }
+  void add_soinfo(soinfo* si) {
+    soinfo_list_.push_back(si);
+  }
+
+  void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
+    for (auto si : soinfos) {
+      add_soinfo(si);
+    }
+  }
+
+  void remove_soinfo(soinfo* si) {
+    soinfo_list_.remove_if([&](soinfo* candidate) {
+      return si == candidate;
+    });
+  }
+
+  const soinfo::soinfo_list_t& soinfo_list() const { return soinfo_list_; }
 
   // For isolated namespaces - checks if the file is on the search path;
   // always returns true for not isolated namespace.
@@ -121,7 +137,8 @@
 };
 
 android_namespace_t g_default_namespace;
-android_namespace_t* g_anonymous_namespace = &g_default_namespace;
+static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
+static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
 
 static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
 
@@ -283,7 +300,8 @@
   sonext->next = si;
   sonext = si;
 
-  ns->soinfo_list().push_back(si);
+  si->generate_handle();
+  ns->add_soinfo(si);
 
   TRACE("name %s: allocated soinfo @ %p", name, si);
   return si;
@@ -332,9 +350,7 @@
   }
 
   // remove from the namespace
-  si->get_namespace()->soinfo_list().remove_if([&](soinfo* candidate) {
-    return si == candidate;
-  });
+  si->get_namespace()->remove_soinfo(si);
 
   si->~soinfo();
   g_soinfo_allocator.free(si);
@@ -830,6 +846,10 @@
   this->namespace_ = ns;
 }
 
+soinfo::~soinfo() {
+  g_soinfo_handles_map.erase(handle_);
+}
+
 static uint32_t calculate_elf_hash(const char* name) {
   const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
   uint32_t h = 0, g;
@@ -1252,21 +1272,21 @@
                                             void* handle) {
   SymbolName symbol_name(name);
 
-  soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list();
-  soinfo::soinfo_list_t::iterator start = soinfo_list.begin();
+  auto& soinfo_list = ns->soinfo_list();
+  auto start = soinfo_list.begin();
 
   if (handle == RTLD_NEXT) {
     if (caller == nullptr) {
       return nullptr;
     } else {
-      soinfo::soinfo_list_t::iterator it = soinfo_list.find(caller);
+      auto it = soinfo_list.find(caller);
       CHECK (it != soinfo_list.end());
       start = ++it;
     }
   }
 
   const ElfW(Sym)* s = nullptr;
-  for (soinfo::soinfo_list_t::iterator it = start, end = soinfo_list.end(); it != end; ++it) {
+  for (auto it = start, end = soinfo_list.end(); it != end; ++it) {
     soinfo* si = *it;
     // Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...)
     // if the library is opened by application with target api level <= 22
@@ -1638,7 +1658,7 @@
     if (si == nullptr) {
       si = g_public_namespace.find_if(predicate);
       if (si != nullptr) {
-        ns->soinfo_list().push_back(si);
+        ns->add_soinfo(si);
       }
     }
 
@@ -1812,7 +1832,7 @@
     });
 
     if (candidate != nullptr) {
-      ns->soinfo_list().push_back(candidate);
+      ns->add_soinfo(candidate);
       task->set_soinfo(candidate);
       return true;
     }
@@ -2179,7 +2199,7 @@
   parse_LD_LIBRARY_PATH(ld_library_path);
 }
 
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
+void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
                   void* caller_addr) {
   soinfo* const caller = find_containing_library(caller_addr);
 
@@ -2223,9 +2243,10 @@
   soinfo* si = find_library(ns, name, flags, extinfo, caller);
   if (si != nullptr) {
     si->call_constructors();
+    return si->to_handle();
   }
 
-  return si;
+  return nullptr;
 }
 
 int do_dladdr(const void* addr, Dl_info* info) {
@@ -2251,6 +2272,19 @@
   return 1;
 }
 
+static soinfo* soinfo_from_handle(void* handle) {
+  if ((reinterpret_cast<uintptr_t>(handle) & 1) != 0) {
+    auto it = g_soinfo_handles_map.find(reinterpret_cast<uintptr_t>(handle));
+    if (it == g_soinfo_handles_map.end()) {
+      return nullptr;
+    } else {
+      return it->second;
+    }
+  }
+
+  return static_cast<soinfo*>(handle);
+}
+
 bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver,
               void* caller_addr, void** symbol) {
 #if !defined(__LP64__)
@@ -2282,7 +2316,12 @@
   if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
     sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle);
   } else {
-    sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, sym_name, vi);
+    soinfo* si = soinfo_from_handle(handle);
+    if (si == nullptr) {
+      DL_ERR("dlsym failed: invalid handle: %p", handle);
+      return false;
+    }
+    sym = dlsym_handle_lookup(si, &found, sym_name, vi);
   }
 
   if (sym != nullptr) {
@@ -2301,9 +2340,16 @@
   return false;
 }
 
-void do_dlclose(soinfo* si) {
+int do_dlclose(void* handle) {
   ProtectedDataGuard guard;
+  soinfo* si = soinfo_from_handle(handle);
+  if (si == nullptr) {
+    DL_ERR("invalid handle: %p", handle);
+    return -1;
+  }
+
   soinfo_unload(si);
+  return 0;
 }
 
 bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
@@ -2390,12 +2436,10 @@
 
   if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
     // If shared - clone the caller namespace
-    auto& soinfo_list = caller_ns->soinfo_list();
-    std::copy(soinfo_list.begin(), soinfo_list.end(), std::back_inserter(ns->soinfo_list()));
+    ns->add_soinfos(caller_ns->soinfo_list());
   } else {
     // If not shared - copy only the global group
-    auto global_group = make_global_group(caller_ns);
-    std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
+    ns->add_soinfos(make_global_group(caller_ns));
   }
 
   return ns;
@@ -3239,6 +3283,39 @@
   return local_group_root_->target_sdk_version_;
 }
 
+uintptr_t soinfo::get_handle() const {
+  CHECK(has_min_version(3));
+  CHECK(handle_ != 0);
+  return handle_;
+}
+
+void* soinfo::to_handle() {
+  if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) {
+    return this;
+  }
+
+  return reinterpret_cast<void*>(get_handle());
+}
+
+void soinfo::generate_handle() {
+  CHECK(has_min_version(3));
+  CHECK(handle_ == 0); // Make sure this is the first call
+
+  // Make sure the handle is unique and does not collide
+  // with special values which are RTLD_DEFAULT and RTLD_NEXT.
+  do {
+    arc4random_buf(&handle_, sizeof(handle_));
+    // the least significant bit for the handle is always 1
+    // making it easy to test the type of handle passed to
+    // dl* functions.
+    handle_ = handle_ | 1;
+  } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
+           handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
+           g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
+
+  g_soinfo_handles_map[handle_] = this;
+}
+
 bool soinfo::prelink_image() {
   /* Extract dynamic section */
   ElfW(Word) dynamic_flags = 0;
@@ -4212,7 +4289,7 @@
   // before get_libdl_info().
   solist = get_libdl_info();
   sonext = get_libdl_info();
-  g_default_namespace.soinfo_list().push_back(get_libdl_info());
+  g_default_namespace.add_soinfo(get_libdl_info());
 
   // We have successfully fixed our own relocations. It's safe to run
   // the main part of the linker now.
diff --git a/linker/linker.h b/linker/linker.h
index 9145454..81f93ac 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -268,6 +268,7 @@
  public:
   soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat,
          off64_t file_offset, int rtld_flags);
+  ~soinfo();
 
   void call_constructors();
   void call_destructors();
@@ -346,6 +347,10 @@
   void set_mapped_by_caller(bool reserved_map);
   bool is_mapped_by_caller() const;
 
+  uintptr_t get_handle() const;
+  void generate_handle();
+  void* to_handle();
+
  private:
   bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
   ElfW(Sym)* elf_addr_lookup(const void* addr);
@@ -410,6 +415,7 @@
   // version >= 3
   std::vector<std::string> dt_runpath_;
   android_namespace_t* namespace_;
+  uintptr_t handle_;
 
   friend soinfo* get_libdl_info();
 };
@@ -432,8 +438,8 @@
 
 void do_android_get_LD_LIBRARY_PATH(char*, size_t);
 void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr);
-void do_dlclose(soinfo* si);
+void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr);
+int do_dlclose(void* handle);
 
 int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data);
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 66d8859..bbdc024 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -1050,3 +1050,19 @@
 
   ASSERT_TRUE(ns_get_dlopened_string_anon() != ns_get_dlopened_string_private());
 }
+
+TEST(dlext, dlopen_handle_value_platform) {
+  void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL);
+  ASSERT_TRUE((reinterpret_cast<uintptr_t>(handle) & 1) != 0)
+          << "dlopen should return odd value for the handle";
+  dlclose(handle);
+}
+
+TEST(dlext, dlopen_handle_value_app_compat) {
+  android_set_application_target_sdk_version(23);
+  void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL);
+  ASSERT_TRUE(reinterpret_cast<uintptr_t>(handle) % sizeof(uintptr_t) == 0)
+          << "dlopen should return valid pointer";
+  dlclose(handle);
+}
+
diff --git a/tests/getauxval_test.cpp b/tests/getauxval_test.cpp
index 54458c4..63bc963 100644
--- a/tests/getauxval_test.cpp
+++ b/tests/getauxval_test.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <sys/cdefs.h>
+#include <sys/utsname.h>
 #include <gtest/gtest.h>
 
 // getauxval() was only added as of glibc version 2.16.
@@ -64,15 +65,23 @@
 
 TEST(getauxval, arm_has_AT_HWCAP2) {
 #if defined(__arm__)
-  // If this test fails, apps that use getauxval to decide at runtime whether crypto hardware is
-  // available will incorrectly assume that it isn't, and will have really bad performance.
-  // If this test fails, ensure that you've enabled COMPAT_BINFMT_ELF in your kernel configuration.
-  // Note that 0 ("I don't support any of these things") is a legitimate response --- we need
-  // to check errno to see whether we got a "true" 0 or a "not found" 0.
-  errno = 0;
-  getauxval(AT_HWCAP2);
-  ASSERT_EQ(0, errno) << "kernel not reporting AT_HWCAP2 to 32-bit ARM process";
-#else
-  GTEST_LOG_(INFO) << "This test is only meaningful for 32-bit ARM code.\n";
+  // There are no known 32-bit processors that implement any of these instructions, so rather
+  // than require that OEMs backport kernel patches, let's just ignore old hardware. Strictly
+  // speaking this would be fooled by someone choosing to ship a 32-bit kernel on 64-bit hardware,
+  // but that doesn't seem very likely in 2016.
+  utsname u;
+  ASSERT_EQ(0, uname(&u));
+  if (strcmp(u.machine, "aarch64") == 0) {
+    // If this test fails, apps that use getauxval to decide at runtime whether crypto hardware is
+    // available will incorrectly assume that it isn't, and will have really bad performance.
+    // If this test fails, ensure that you've enabled COMPAT_BINFMT_ELF in your kernel configuration.
+    // Note that 0 ("I don't support any of these things") is a legitimate response --- we need
+    // to check errno to see whether we got a "true" 0 or a "not found" 0.
+    errno = 0;
+    getauxval(AT_HWCAP2);
+    ASSERT_EQ(0, errno) << "64-bit kernel not reporting AT_HWCAP2 to 32-bit ARM process";
+    return;
+  }
 #endif
+  GTEST_LOG_(INFO) << "This test is only meaningful for 32-bit ARM code on 64-bit devices.\n";
 }
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index 32308aa..c5128ea 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -411,4 +411,14 @@
                                             << sent.si_code << ", received " << received.si_code
                                             << error_msg;
 }
+
+#if defined(__arm__) || defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
+TEST(signal, sigset_size) {
+  // The setjmp implementations for ARM, AArch64, x86, and x86_64 assume that sigset_t can fit in a
+  // long. This is true because ARM and x86 have broken rt signal support, and AArch64 and x86_64
+  // both have a SIGRTMAX defined as 64.
+  static_assert(sizeof(sigset_t) <= sizeof(long), "sigset_t doesn't fit in a long");
+}
+
+#endif
 #endif
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 6cdabd2..b1f6364 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -573,3 +573,10 @@
   timespec out;
   ASSERT_EQ(EINVAL, clock_nanosleep(-1, 0, &in, &out));
 }
+
+TEST(time, clock_nanosleep_thread_cputime_id) {
+  timespec in;
+  in.tv_sec = 1;
+  in.tv_nsec = 0;
+  ASSERT_EQ(EINVAL, clock_nanosleep(CLOCK_THREAD_CPUTIME_ID, 0, &in, nullptr));
+}