Merge "fcntl: remove some duplication, fix a bug."
diff --git a/linker/linker.cpp b/linker/linker.cpp
index c10e9f6..6246f8c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -39,6 +39,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <iterator>
 #include <new>
 #include <string>
 #include <unordered_map>
@@ -2484,11 +2485,12 @@
     return false;
   }
 
-  auto sonames = android::base::Split(shared_lib_sonames, ":");
-  std::unordered_set<std::string> sonames_set(sonames.begin(), sonames.end());
+  std::vector<std::string> sonames = android::base::Split(shared_lib_sonames, ":");
+  std::unordered_set<std::string> sonames_set(std::make_move_iterator(sonames.begin()),
+                                              std::make_move_iterator(sonames.end()));
 
   ProtectedDataGuard guard;
-  namespace_from->add_linked_namespace(namespace_to, sonames_set, false);
+  namespace_from->add_linked_namespace(namespace_to, std::move(sonames_set), false);
 
   return true;
 }
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 6817901..671e0b5 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -41,11 +41,11 @@
 struct android_namespace_link_t {
  public:
   android_namespace_link_t(android_namespace_t* linked_namespace,
-                           const std::unordered_set<std::string>& shared_lib_sonames,
+                           std::unordered_set<std::string> shared_lib_sonames,
                            bool allow_all_shared_libs)
-      : linked_namespace_(linked_namespace), shared_lib_sonames_(shared_lib_sonames),
-        allow_all_shared_libs_(allow_all_shared_libs)
-  {}
+      : linked_namespace_(linked_namespace),
+        shared_lib_sonames_(std::move(shared_lib_sonames)),
+        allow_all_shared_libs_(allow_all_shared_libs) {}
 
   android_namespace_t* linked_namespace() const {
     return linked_namespace_;
@@ -127,10 +127,10 @@
     return linked_namespaces_;
   }
   void add_linked_namespace(android_namespace_t* linked_namespace,
-                            const std::unordered_set<std::string>& shared_lib_sonames,
+                            std::unordered_set<std::string> shared_lib_sonames,
                             bool allow_all_shared_libs) {
-    linked_namespaces_.push_back(
-        android_namespace_link_t(linked_namespace, shared_lib_sonames, allow_all_shared_libs));
+    linked_namespaces_.emplace_back(linked_namespace, std::move(shared_lib_sonames),
+                                    allow_all_shared_libs);
   }
 
   void add_soinfo(soinfo* si) {
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 0c9a2e0..8ae2257 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -1661,6 +1661,7 @@
      },
    },
    header_libs: ["bionic_libc_platform_headers"],
+   cflags: ["-fexceptions"],
 }
 
 cc_test {
@@ -1676,6 +1677,7 @@
      },
    },
    header_libs: ["bionic_libc_platform_headers"],
+   cflags: ["-fexceptions"],
 }
 
 cc_genrule {
diff --git a/tests/libs/stack_tagging_helper.cpp b/tests/libs/stack_tagging_helper.cpp
index a239dc1..d29844d 100644
--- a/tests/libs/stack_tagging_helper.cpp
+++ b/tests/libs/stack_tagging_helper.cpp
@@ -260,6 +260,81 @@
   CHECK(memtag_stack);
 }
 
+static uintptr_t GetTag(void* addr) {
+  return reinterpret_cast<uintptr_t>(addr) & (0xFULL << 56);
+}
+
+static uintptr_t GetTag(volatile void* addr) {
+  return GetTag(const_cast<void*>(addr));
+}
+
+static volatile char* throw_frame;
+static volatile char* skip_frame3_frame;
+volatile char *x;
+
+__attribute__((noinline)) void throws() {
+  // Prevent optimization.
+  if (getpid() == 0) return;
+  throw_frame = reinterpret_cast<char*>(__builtin_frame_address(0));
+  throw "error";
+}
+
+__attribute__((noinline)) void maybe_throws() {
+  // These are all unique sizes so in case of a failure, we can see which ones
+  // are not untagged from the tag dump.
+  volatile char y[5 * 16]= {};
+  x = y;
+  // Make sure y is tagged.
+  CHECK(GetTag(&y) != GetTag(__builtin_frame_address(0)));
+  throws();
+}
+
+__attribute__((noinline, no_sanitize("memtag"))) void skip_frame() {
+  volatile char y[6*16] = {};
+  x = y;
+  // Make sure y is not tagged.
+  CHECK(GetTag(&y) == GetTag(__builtin_frame_address(0)));
+  maybe_throws();
+}
+
+__attribute__((noinline)) void skip_frame2() {
+  volatile char y[7*16] = {};
+  x = y;
+  // Make sure y is tagged.
+  CHECK(GetTag(&y) != GetTag(__builtin_frame_address(0)));
+  skip_frame();
+}
+
+__attribute__((noinline, no_sanitize("memtag"))) void skip_frame3() {
+  volatile char y[8*16] = {};
+  x = y;
+  skip_frame3_frame = reinterpret_cast<char*>(__builtin_frame_address(0));
+  // Make sure y is not tagged.
+  CHECK(GetTag(&y) == GetTag(__builtin_frame_address(0)));
+  skip_frame2();
+}
+
+void test_exception_cleanup() {
+  // This is here for debugging purposes, if something goes wrong we can
+  // verify that this placeholder did not get untagged.
+  volatile char placeholder[16*16] = {};
+  x = placeholder;
+  try {
+    skip_frame3();
+  } catch (const char* e) {
+  }
+  if (throw_frame >= skip_frame3_frame) {
+    fprintf(stderr, "invalid throw frame");
+    exit(1);
+  }
+  for (char* b = const_cast<char*>(throw_frame); b < skip_frame3_frame; ++b) {
+    if (mte_get_tag(b) != b) {
+      fprintf(stderr, "invalid tag at %p", b);
+      exit(1);
+    }
+  }
+}
+
 int main(int argc, char** argv) {
   if (argc < 2) {
     printf("nothing to do\n");
@@ -296,6 +371,11 @@
     return 0;
   }
 
+  if (strcmp(argv[1], "exception_cleanup") == 0) {
+    test_exception_cleanup();
+    return 0;
+  }
+
   printf("unrecognized command: %s\n", argv[1]);
   return 1;
 }
diff --git a/tests/memtag_stack_test.cpp b/tests/memtag_stack_test.cpp
index 84ee8d1..97084ec 100644
--- a/tests/memtag_stack_test.cpp
+++ b/tests/memtag_stack_test.cpp
@@ -44,13 +44,13 @@
 #endif
 }
 
-INSTANTIATE_TEST_SUITE_P(, MemtagStackTest,
-                         testing::Combine(testing::Values("vfork_execve", "vfork_execl",
-                                                          "vfork_exit", "longjmp",
-                                                          "longjmp_sigaltstack", "android_mallopt"),
-                                          testing::Bool()),
-                         [](const ::testing::TestParamInfo<MemtagStackTest::ParamType>& info) {
-                           std::string s = std::get<0>(info.param);
-                           if (std::get<1>(info.param)) s += "_static";
-                           return s;
-                         });
+INSTANTIATE_TEST_SUITE_P(
+    , MemtagStackTest,
+    testing::Combine(testing::Values("vfork_execve", "vfork_execl", "vfork_exit", "longjmp",
+                                     "longjmp_sigaltstack", "android_mallopt", "exception_cleanup"),
+                     testing::Bool()),
+    [](const ::testing::TestParamInfo<MemtagStackTest::ParamType>& info) {
+      std::string s = std::get<0>(info.param);
+      if (std::get<1>(info.param)) s += "_static";
+      return s;
+    });