With O_TMPFILE, open(2) takes a mode argument.

Strictly, the mode isn't really meaningful unless you supply O_EXCL,
but the kernel will take it and fstat will return it even if you
never give the file a name.

Also warn for O_TMPFILE without a mode at compile time where possible.

Bug: N/A
Test: ran tests
Change-Id: I729b6d6e6190676fd017a1190b6200bf9abdbfd8
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index b264e53..0dc54d0 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -824,7 +824,7 @@
   const std::string lib_path = get_testlib_root() + "/libtest_simple.so";
 
   int tmpfd = TEMP_FAILURE_RETRY(
-        open(get_testlib_root().c_str(), O_TMPFILE | O_CLOEXEC | O_RDWR | O_EXCL));
+        open(get_testlib_root().c_str(), O_TMPFILE | O_CLOEXEC | O_RDWR | O_EXCL, 0));
 
   // Ignore kernels without O_TMPFILE flag support
   if (tmpfd == -1 && (errno == EISDIR || errno == EINVAL || errno == EOPNOTSUPP)) {
diff --git a/tests/fcntl_test.cpp b/tests/fcntl_test.cpp
index 1bef0f4..4532a4b 100644
--- a/tests/fcntl_test.cpp
+++ b/tests/fcntl_test.cpp
@@ -23,6 +23,8 @@
 
 #include "TemporaryFile.h"
 
+#include <android-base/stringprintf.h>
+
 // Glibc v2.19 doesn't include these in fcntl.h so host builds will fail without.
 #if !defined(FALLOC_FL_PUNCH_HOLE) || !defined(FALLOC_FL_KEEP_SIZE)
 #include <linux/falloc.h>
@@ -292,3 +294,43 @@
     ASSERT_EQ(errno, EOPNOTSUPP);
   }
 }
+
+TEST(fcntl, open_O_TMPFILE_mode) {
+#if __BIONIC__ // Our glibc is too old for O_TMPFILE.
+  TemporaryDir dir;
+  // Without O_EXCL, we're allowed to give this a name later.
+  // (This is unrelated to the O_CREAT interaction with O_EXCL.)
+  const mode_t perms = S_IRUSR | S_IWUSR;
+  int fd = open(dir.dirname, O_TMPFILE | O_RDWR, perms);
+
+  // Ignore kernels without O_TMPFILE support (< 3.11).
+  if (fd == -1 && (errno == EISDIR || errno == EINVAL || errno == EOPNOTSUPP)) return;
+
+  ASSERT_TRUE(fd != -1) << strerror(errno);
+
+  // Does the fd claim to have the mode we set?
+  struct stat sb = {};
+  ASSERT_EQ(0, fstat(fd, &sb));
+  ASSERT_EQ(perms, (sb.st_mode & ~S_IFMT));
+
+  std::string final_path = android::base::StringPrintf("%s/named_now", dir.dirname);
+  ASSERT_EQ(0, linkat(AT_FDCWD, android::base::StringPrintf("/proc/self/fd/%d", fd).c_str(),
+                      AT_FDCWD, final_path.c_str(),
+                      AT_SYMLINK_FOLLOW));
+  ASSERT_EQ(0, close(fd));
+
+  // Does the resulting file claim to have the mode we set?
+  ASSERT_EQ(0, stat(final_path.c_str(), &sb));
+  ASSERT_EQ(perms, (sb.st_mode & ~S_IFMT));
+
+  // With O_EXCL, you're not allowed to add a name later.
+  fd = open(dir.dirname, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
+  ASSERT_TRUE(fd != -1) << strerror(errno);
+  errno = 0;
+  ASSERT_EQ(-1, linkat(AT_FDCWD, android::base::StringPrintf("/proc/self/fd/%d", fd).c_str(),
+                       AT_FDCWD, android::base::StringPrintf("%s/no_chance", dir.dirname).c_str(),
+                       AT_SYMLINK_FOLLOW));
+  ASSERT_EQ(ENOENT, errno);
+  ASSERT_EQ(0, close(fd));
+#endif
+}
diff --git a/tests/fortify_compilation_test.cpp b/tests/fortify_compilation_test.cpp
index 307a9c5..d859ef1 100644
--- a/tests/fortify_compilation_test.cpp
+++ b/tests/fortify_compilation_test.cpp
@@ -224,15 +224,23 @@
 
 void test_open() {
   // NOLINTNEXTLINE(whitespace/line_length)
-  // GCC: error: call to '__creat_missing_mode' declared with attribute error: called with O_CREAT, but missing mode
-  // CLANG: error: 'open' called with O_CREAT, but missing mode
+  // GCC: error: call to '__creat_missing_mode' declared with attribute error: called with O_CREAT or O_TMPFILE, but missing mode
+  // CLANG: error: 'open' called with O_CREAT or O_TMPFILE, but missing mode
   open("/dev/null", O_CREAT);
 
+  // GCC: error: call to '__creat_missing_mode' declared with attribute error: called with O_CREAT or O_TMPFILE, but missing mode
+  // CLANG: error: 'open' called with O_CREAT or O_TMPFILE, but missing mode
+  open("/dev/null", O_TMPFILE);
+
   // NOLINTNEXTLINE(whitespace/line_length)
   // GCC: error: call to '__creat_too_many_args' declared with attribute error: too many arguments
   // CLANG: error: call to unavailable function 'open': too many arguments
   open("/dev/null", O_CREAT, 0, 0);
 
+  // GCC: error: call to '__creat_too_many_args' declared with attribute error: too many arguments
+  // CLANG: error: call to unavailable function 'open': too many arguments
+  open("/dev/null", O_TMPFILE, 0, 0);
+
   // CLANG: warning: 'open' has superfluous mode bits; missing O_CREAT?
   open("/dev/null", O_RDONLY, 0644);
 
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index 984a657..2946e23 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -985,3 +985,15 @@
   timeout.tv_sec = timeout.tv_nsec = 0;
   ASSERT_FORTIFY(ppoll(buf, fd_count, &timeout, NULL));
 }
+
+TEST_F(DEATHTEST, open_O_CREAT_without_mode_fortified) {
+  int flags = O_CREAT; // Fool the compiler.
+  ASSERT_FORTIFY(open("", flags));
+}
+
+TEST_F(DEATHTEST, open_O_TMPFILE_without_mode_fortified) {
+#if __BIONIC__ // Our glibc is too old for O_TMPFILE.
+  int flags = O_TMPFILE; // Fool the compiler.
+  ASSERT_FORTIFY(open("", flags));
+#endif
+}