From b33ae5ba7dd53c8736a79fe19870ae856deebadb Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Thu, 12 Mar 2015 15:44:39 +0000 Subject: [PATCH] Add option to disable access to the global filesystem namespace. 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!) git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@232049 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 6 +++++ include/__config | 8 ++++++ include/cstdio | 4 +++ include/fstream | 26 +++++++++++++++++++ test/CMakeLists.txt | 1 + test/libcxx/test/config.py | 10 +++++++ test/libcxx/test/format.py | 4 +++ test/lit.site.cfg.in | 1 + .../file.streams/c.files/cstdio.pass.cpp | 4 +++ .../fopen.fail.cpp | 15 +++++++++++ .../lit.local.cfg | 2 ++ .../rename.fail.cpp | 15 +++++++++++ .../file.streams/fstreams/lit.local.cfg | 2 ++ .../conversions.buffer/lit.local.cfg | 2 ++ test/support/platform_support.h | 4 +++ 15 files changed, 104 insertions(+) create mode 100644 test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp create mode 100644 test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg create mode 100644 test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp create mode 100644 test/std/input.output/file.streams/fstreams/lit.local.cfg create mode 100644 test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg diff --git a/CMakeLists.txt b/CMakeLists.txt index 230a3c6ae..bb80182c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/__config b/include/__config index db9fb2a81..fdb06fa88 100644 --- a/include/__config +++ b/include/__config @@ -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 diff --git a/include/cstdio b/include/cstdio index 37814ef11..8c4e627fc 100644 --- a/include/cstdio +++ b/include/cstdio @@ -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; diff --git a/include/fstream b/include/fstream index ace5eb99b..1f289eddc 100644 --- a/include/fstream +++ b/include/fstream @@ -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 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 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* 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 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 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 inline _LIBCPP_INLINE_VISIBILITY @@ -1163,8 +1175,10 @@ public: basic_filebuf* 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 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 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 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* 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 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 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 inline _LIBCPP_INLINE_VISIBILITY diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6af4c98fe..cb78fcf23 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 diff --git a/test/libcxx/test/config.py b/test/libcxx/test/config.py index a974adf7f..07c4f0c62 100644 --- a/test/libcxx/test/config.py +++ b/test/libcxx/test/config.py @@ -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') diff --git a/test/libcxx/test/format.py b/test/libcxx/test/format.py index a0f984a41..57fe23306 100644 --- a/test/libcxx/test/format.py +++ b/test/libcxx/test/format.py @@ -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 diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 5a4445bb1..db79581fd 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -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@" diff --git a/test/std/input.output/file.streams/c.files/cstdio.pass.cpp b/test/std/input.output/file.streams/c.files/cstdio.pass.cpp index 1a60dd6b4..2ea09bc7b 100644 --- a/test/std/input.output/file.streams/c.files/cstdio.pass.cpp +++ b/test/std/input.output/file.streams/c.files/cstdio.pass.cpp @@ -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::value), ""); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); +#endif static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); +#ifndef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); +#endif static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); diff --git a/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp new file mode 100644 index 000000000..4d83296f0 --- /dev/null +++ b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/fopen.fail.cpp @@ -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 + +int main() { + // fopen is not available on systems without a global filesystem namespace. + std::fopen("", ""); +} diff --git a/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg new file mode 100644 index 000000000..4ea670935 --- /dev/null +++ b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/lit.local.cfg @@ -0,0 +1,2 @@ +if 'libcpp-has-no-global-filesystem-namespace' not in config.available_features: + config.unsupported = True diff --git a/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp new file mode 100644 index 000000000..deca9bf5b --- /dev/null +++ b/test/std/input.output/file.streams/c.files/no.global.filesystem.namespace/rename.fail.cpp @@ -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 + +int main() { + // rename is not available on systems without a global filesystem namespace. + std::rename("", ""); +} diff --git a/test/std/input.output/file.streams/fstreams/lit.local.cfg b/test/std/input.output/file.streams/fstreams/lit.local.cfg new file mode 100644 index 000000000..25ac02ba7 --- /dev/null +++ b/test/std/input.output/file.streams/fstreams/lit.local.cfg @@ -0,0 +1,2 @@ +if 'libcpp-has-no-global-filesystem-namespace' in config.available_features: + config.unsupported = True diff --git a/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg b/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg new file mode 100644 index 000000000..25ac02ba7 --- /dev/null +++ b/test/std/localization/locales/locale.convenience/conversions/conversions.buffer/lit.local.cfg @@ -0,0 +1,2 @@ +if 'libcpp-has-no-global-filesystem-namespace' in config.available_features: + config.unsupported = True diff --git a/test/support/platform_support.h b/test/support/platform_support.h index ec6efce89..233e721ff 100644 --- a/test/support/platform_support.h +++ b/test/support/platform_support.h @@ -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