Add option to disable access to the global filesystem namespace.
authorEd Schouten <ed@nuxi.nl>
Thu, 12 Mar 2015 15:44:39 +0000 (15:44 +0000)
committerEd Schouten <ed@nuxi.nl>
Thu, 12 Mar 2015 15:44:39 +0000 (15:44 +0000)
Systems like FreeBSD's Capsicum and Nuxi CloudABI apply the concept of
capability-based security on the way processes can interact with the
filesystem API. It is no longer possible to interact with the VFS
through calls like open(), unlink(), rename(), etc. Instead, processes
are only allowed to interact with files and directories to which they
have been granted access. The *at() functions can be used for this
purpose.

This change adds a new config switch called
_LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE. If set, all functionality
that requires the global filesystem namespace will be disabled. More
concretely:

- fstream's open() function will be removed.
- cstdio will no longer pull in fopen(), rename(), etc.
- The test suite's get_temp_file_name() will be removed. This will cause
  all tests that use the global filesystem namespace to break, but will
  at least make all the other tests run (as get_temp_file_name will not
  build anyway).

It is important to mention that this change will make fstream rather
useless on those systems for now. Still, I'd rather not have fstream
disabled entirely, as it is of course possible to come up with an
extension for fstream that would allow access to local filesystem
namespaces (e.g., by adding an openat() member function).

Differential revision: http://reviews.llvm.org/D8194
Reviewed by: jroelofs (thanks!)

llvm-svn: 232049

15 files changed:
libcxx/CMakeLists.txt
libcxx/include/__config
libcxx/include/cstdio
libcxx/include/fstream
libcxx/test/CMakeLists.txt
libcxx/test/libcxx/test/config.py
libcxx/test/libcxx/test/format.py
libcxx/test/lit.site.cfg.in
libcxx/test/std/input.output/file.streams/c.files/cstdio.pass.cpp
libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp [new file with mode: 0644]
libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg [new file with mode: 0644]
libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp [new file with mode: 0644]
libcxx/test/std/input.output/file.streams/fstreams/lit.local.cfg [new file with mode: 0644]
libcxx/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg [new file with mode: 0644]
libcxx/test/support/platform_support.h

index 230a3c6..bb80182 100644 (file)
@@ -54,6 +54,7 @@ option(LIBCXX_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
 option(LIBCXX_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
 option(LIBCXX_ENABLE_CXX1Y "Enable -std=c++1y and use of c++1y language features if the compiler supports it." OFF)
 option(LIBCXX_ENABLE_SHARED "Build libc++ as a shared library." ON)
+option(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE "Build libc++ with support for the global filesystem namespace." ON)
 option(LIBCXX_ENABLE_THREADS "Build libc++ with support for threads." ON)
 option(LIBCXX_BUILD_32_BITS "Build 32 bit libc++" OFF)
 option(LIBCXX_ENABLE_MONOTONIC_CLOCK
@@ -232,6 +233,11 @@ if (MSVC)
   add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 endif()
 
+# LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE configuration
+if (NOT LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE)
+  add_definitions(-D_LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE)
+endif()
+
 # LIBCXX_ENABLE_THREADS configuration
 if (NOT LIBCXX_ENABLE_THREADS)
   add_definitions(-D_LIBCPP_HAS_NO_THREADS)
index db9fb2a..fdb06fa 100644 (file)
@@ -724,6 +724,14 @@ extern "C" void __sanitizer_annotate_contiguous_container(
          _LIBCPP_HAS_NO_THREADS is defined.
 #endif
 
+// Systems that use capability-based security (FreeBSD with Capsicum,
+// Nuxi CloudABI) may only provide local filesystem access (using *at()).
+// Functions like open(), rename(), unlink() and stat() should not be
+// used, as they attempt to access the global filesystem namespace.
+#ifdef __CloudABI__
+#define _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
+#endif
+
 #if defined(__ANDROID__)
 #define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #endif
index 37814ef..8c4e627 100644 (file)
@@ -144,14 +144,18 @@ using ::FILE;
 using ::fpos_t;
 using ::size_t;
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 using ::remove;
 using ::rename;
 using ::tmpfile;
 using ::tmpnam;
+#endif
 using ::fclose;
 using ::fflush;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 using ::fopen;
 using ::freopen;
+#endif
 using ::setbuf;
 using ::setvbuf;
 using ::fprintf;
index ace5eb9..1f289ed 100644 (file)
@@ -206,8 +206,10 @@ public:
 
     // 27.9.1.4 Members:
     bool is_open() const;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     basic_filebuf* open(const char* __s, ios_base::openmode __mode);
     basic_filebuf* open(const string& __s, ios_base::openmode __mode);
+#endif
     basic_filebuf* close();
 
 protected:
@@ -463,6 +465,7 @@ basic_filebuf<_CharT, _Traits>::is_open() const
     return __file_ != 0;
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 basic_filebuf<_CharT, _Traits>*
 basic_filebuf<_CharT, _Traits>::open(const char* __s, ios_base::openmode __mode)
@@ -550,6 +553,7 @@ basic_filebuf<_CharT, _Traits>::open(const string& __s, ios_base::openmode __mod
 {
     return open(__s.c_str(), __mode);
 }
+#endif
 
 template <class _CharT, class _Traits>
 basic_filebuf<_CharT, _Traits>*
@@ -1005,8 +1009,10 @@ public:
     typedef typename traits_type::off_type off_type;
 
     basic_ifstream();
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     explicit basic_ifstream(const char* __s, ios_base::openmode __mode = ios_base::in);
     explicit basic_ifstream(const string& __s, ios_base::openmode __mode = ios_base::in);
+#endif
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
     basic_ifstream(basic_ifstream&& __rhs);
 #endif
@@ -1018,8 +1024,10 @@ public:
 
     basic_filebuf<char_type, traits_type>* rdbuf() const;
     bool is_open() const;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     void open(const char* __s, ios_base::openmode __mode = ios_base::in);
     void open(const string& __s, ios_base::openmode __mode = ios_base::in);
+#endif
     void close();
 
 private:
@@ -1033,6 +1041,7 @@ basic_ifstream<_CharT, _Traits>::basic_ifstream()
 {
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
 basic_ifstream<_CharT, _Traits>::basic_ifstream(const char* __s, ios_base::openmode __mode)
@@ -1050,6 +1059,7 @@ basic_ifstream<_CharT, _Traits>::basic_ifstream(const string& __s, ios_base::ope
     if (__sb_.open(__s, __mode | ios_base::in) == 0)
         this->setstate(ios_base::failbit);
 }
+#endif
 
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
 
@@ -1107,6 +1117,7 @@ basic_ifstream<_CharT, _Traits>::is_open() const
     return __sb_.is_open();
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 void
 basic_ifstream<_CharT, _Traits>::open(const char* __s, ios_base::openmode __mode)
@@ -1126,6 +1137,7 @@ basic_ifstream<_CharT, _Traits>::open(const string& __s, ios_base::openmode __mo
     else
         this->setstate(ios_base::failbit);
 }
+#endif
 
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
@@ -1163,8 +1175,10 @@ public:
 
     basic_filebuf<char_type, traits_type>* rdbuf() const;
     bool is_open() const;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     void open(const char* __s, ios_base::openmode __mode = ios_base::out);
     void open(const string& __s, ios_base::openmode __mode = ios_base::out);
+#endif
     void close();
 
 private:
@@ -1178,6 +1192,7 @@ basic_ofstream<_CharT, _Traits>::basic_ofstream()
 {
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
 basic_ofstream<_CharT, _Traits>::basic_ofstream(const char* __s, ios_base::openmode __mode)
@@ -1195,6 +1210,7 @@ basic_ofstream<_CharT, _Traits>::basic_ofstream(const string& __s, ios_base::ope
     if (__sb_.open(__s, __mode | ios_base::out) == 0)
         this->setstate(ios_base::failbit);
 }
+#endif
 
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
 
@@ -1252,6 +1268,7 @@ basic_ofstream<_CharT, _Traits>::is_open() const
     return __sb_.is_open();
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 void
 basic_ofstream<_CharT, _Traits>::open(const char* __s, ios_base::openmode __mode)
@@ -1271,6 +1288,7 @@ basic_ofstream<_CharT, _Traits>::open(const string& __s, ios_base::openmode __mo
     else
         this->setstate(ios_base::failbit);
 }
+#endif
 
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
@@ -1295,8 +1313,10 @@ public:
     typedef typename traits_type::off_type off_type;
 
     basic_fstream();
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     explicit basic_fstream(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
     explicit basic_fstream(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
+#endif
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
     basic_fstream(basic_fstream&& __rhs);
 #endif
@@ -1308,8 +1328,10 @@ public:
 
     basic_filebuf<char_type, traits_type>* rdbuf() const;
     bool is_open() const;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
     void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
+#endif
     void close();
 
 private:
@@ -1323,6 +1345,7 @@ basic_fstream<_CharT, _Traits>::basic_fstream()
 {
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
 basic_fstream<_CharT, _Traits>::basic_fstream(const char* __s, ios_base::openmode __mode)
@@ -1340,6 +1363,7 @@ basic_fstream<_CharT, _Traits>::basic_fstream(const string& __s, ios_base::openm
     if (__sb_.open(__s, __mode) == 0)
         this->setstate(ios_base::failbit);
 }
+#endif
 
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
 
@@ -1397,6 +1421,7 @@ basic_fstream<_CharT, _Traits>::is_open() const
     return __sb_.is_open();
 }
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 template <class _CharT, class _Traits>
 void
 basic_fstream<_CharT, _Traits>::open(const char* __s, ios_base::openmode __mode)
@@ -1416,6 +1441,7 @@ basic_fstream<_CharT, _Traits>::open(const string& __s, ios_base::openmode __mod
     else
         this->setstate(ios_base::failbit);
 }
+#endif
 
 template <class _CharT, class _Traits>
 inline _LIBCPP_INLINE_VISIBILITY
index 6af4c98..cb78fcf 100644 (file)
@@ -42,6 +42,7 @@ if (LIT_EXECUTABLE)
   pythonize_bool(LIBCXX_ENABLE_RTTI)
   pythonize_bool(LIBCXX_ENABLE_SHARED)
   pythonize_bool(LIBCXX_BUILD_32_BITS)
+  pythonize_bool(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE)
   pythonize_bool(LIBCXX_ENABLE_THREADS)
   pythonize_bool(LIBCXX_ENABLE_MONOTONIC_CLOCK)
   # The tests shouldn't link to any ABI library when it has been linked into
index a974adf..07c4f0c 100644 (file)
@@ -347,6 +347,7 @@ class Configuration(object):
         # Configure feature flags.
         self.configure_compile_flags_exceptions()
         self.configure_compile_flags_rtti()
+        self.configure_compile_flags_no_global_filesystem_namespace()
         enable_32bit = self.get_lit_bool('enable_32bit', False)
         if enable_32bit:
             self.cxx.flags += ['-m32']
@@ -395,6 +396,15 @@ class Configuration(object):
             self.config.available_features.add('libcpp-no-rtti')
             self.cxx.compile_flags += ['-fno-rtti', '-D_LIBCPP_NO_RTTI']
 
+    def configure_compile_flags_no_global_filesystem_namespace(self):
+        enable_global_filesystem_namespace = self.get_lit_bool(
+            'enable_global_filesystem_namespace', True)
+        if not enable_global_filesystem_namespace:
+            self.config.available_features.add(
+                'libcpp-has-no-global-filesystem-namespace')
+            self.cxx.compile_flags += [
+                '-D_LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE']
+
     def configure_compile_flags_no_threads(self):
         self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_THREADS']
         self.config.available_features.add('libcpp-has-no-threads')
index a0f984a..57fe233 100644 (file)
@@ -60,6 +60,10 @@ class LibcxxTestFormat(object):
         is_pass_test = name.endswith('.pass.cpp')
         is_fail_test = name.endswith('.fail.cpp')
 
+        if test.config.unsupported:
+            return (lit.Test.UNSUPPORTED,
+                    "A lit.local.cfg marked this unsupported")
+
         res = lit.TestRunner.parseIntegratedTestScript(
             test, require_script=is_sh_test)
         # Check if a result for the test was returned. If so return that
index 5a4445b..db79581 100644 (file)
@@ -8,6 +8,7 @@ config.enable_exceptions        = "@LIBCXX_ENABLE_EXCEPTIONS@"
 config.enable_rtti              = "@LIBCXX_ENABLE_RTTI@"
 config.enable_shared            = "@LIBCXX_ENABLE_SHARED@"
 config.enable_32bit             = "@LIBCXX_BUILD_32_BITS@"
+config.enable_global_filesystem_namespace = "@LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE@"
 config.enable_threads           = "@LIBCXX_ENABLE_THREADS@"
 config.enable_monotonic_clock   = "@LIBCXX_ENABLE_MONOTONIC_CLOCK@"
 config.cxx_abi                  = "@LIBCXX_CXX_ABI_LIBNAME@"
index 1a60dd6..2ea09bc 100644 (file)
@@ -88,14 +88,18 @@ int main()
     std::size_t s = 0;
     char* cp = 0;
     std::va_list va;
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     static_assert((std::is_same<decltype(std::remove("")), int>::value), "");
     static_assert((std::is_same<decltype(std::rename("","")), int>::value), "");
     static_assert((std::is_same<decltype(std::tmpfile()), std::FILE*>::value), "");
     static_assert((std::is_same<decltype(std::tmpnam(cp)), char*>::value), "");
+#endif
     static_assert((std::is_same<decltype(std::fclose(fp)), int>::value), "");
     static_assert((std::is_same<decltype(std::fflush(fp)), int>::value), "");
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
     static_assert((std::is_same<decltype(std::fopen("", "")), std::FILE*>::value), "");
     static_assert((std::is_same<decltype(std::freopen("", "", fp)), std::FILE*>::value), "");
+#endif
     static_assert((std::is_same<decltype(std::setbuf(fp,cp)), void>::value), "");
     static_assert((std::is_same<decltype(std::vfprintf(fp,"",va)), int>::value), "");
     static_assert((std::is_same<decltype(std::fprintf(fp," ")), int>::value), "");
diff --git a/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp b/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp
new file mode 100644 (file)
index 0000000..4d83296
--- /dev/null
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstdio>
+
+int main() {
+    // fopen is not available on systems without a global filesystem namespace.
+    std::fopen("", "");
+}
diff --git a/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg b/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg
new file mode 100644 (file)
index 0000000..4ea6709
--- /dev/null
@@ -0,0 +1,2 @@
+if 'libcpp-has-no-global-filesystem-namespace' not in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp b/libcxx/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp
new file mode 100644 (file)
index 0000000..deca9bf
--- /dev/null
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstdio>
+
+int main() {
+    // rename is not available on systems without a global filesystem namespace.
+    std::rename("", "");
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/lit.local.cfg b/libcxx/test/std/input.output/file.streams/fstreams/lit.local.cfg
new file mode 100644 (file)
index 0000000..25ac02b
--- /dev/null
@@ -0,0 +1,2 @@
+if 'libcpp-has-no-global-filesystem-namespace' in config.available_features:
+    config.unsupported = True
diff --git a/libcxx/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg b/libcxx/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg
new file mode 100644 (file)
index 0000000..25ac02b
--- /dev/null
@@ -0,0 +1,2 @@
+if 'libcpp-has-no-global-filesystem-namespace' in config.available_features:
+    config.unsupported = True
index ec6efce..233e721 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef PLATFORM_SUPPORT_H
 #define PLATFORM_SUPPORT_H
 
+#include <__config>
+
 // locale names
 #ifdef _WIN32
 // WARNING: Windows does not support UTF-8 codepages.
@@ -65,6 +67,7 @@ extern "C" {
 }
 #endif
 
+#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 inline
 std::string
 get_temp_file_name()
@@ -90,5 +93,6 @@ get_temp_file_name()
     return Name;
 #endif
 }
+#endif // _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE
 
 #endif // PLATFORM_SUPPORT_H