From 3ad9525a149cd198445653944ac890bfc22c4c93 Mon Sep 17 00:00:00 2001 From: spaceeye Date: Sun, 11 Jun 2023 18:40:04 +0300 Subject: [PATCH] merge --- src/io.h | 5 +- src/isolated_io.h | 142 ++++++++++++++++++++++++++++++++++++++++++++++ src/pocketpy.h | 6 +- src/vm.h | 15 +++-- 4 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 src/isolated_io.h diff --git a/src/io.h b/src/io.h index 44deb7fb..be950072 100644 --- a/src/io.h +++ b/src/io.h @@ -31,11 +31,12 @@ struct FileIO { Str file; Str mode; - FILE* fp; + FILE* fp= nullptr; bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; } FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) { + if (vm->check_is_invalid_io_path(file)) {vm->IOError(strerror(errno));} fp = fopen(file.c_str(), mode.c_str()); if(!fp) vm->IOError(strerror(errno)); } @@ -125,7 +126,7 @@ inline void add_module_os(VM* vm){ di = std::filesystem::directory_iterator(path); }catch(std::filesystem::filesystem_error& e){ std::string msg = e.what(); - auto pos = msg.find_last_of(":"); + auto pos = msg.find_last_of(':'); if(pos != std::string::npos) msg = msg.substr(pos + 1); vm->IOError(Str(msg).lstrip()); } diff --git a/src/isolated_io.h b/src/isolated_io.h new file mode 100644 index 00000000..f24b1c4c --- /dev/null +++ b/src/isolated_io.h @@ -0,0 +1,142 @@ +#pragma once + +#include "ceval.h" +#include "cffi.h" +#include "common.h" + +#if PK_ENABLE_OS + +#include +#include + +namespace pkpy { + +inline std::filesystem::path get_rel(const VM* vm, const std::filesystem::path & rel) { + return rel.lexically_relative(vm->_lowest_isolated_cwd_path); +} + +bool check_if_path_is_isolated(const std::string & path_to_check_str, + const std::string & toplevel_path_str) { + //.lexically_normal() expands somepath/somdir/.. to somepath/ + auto toplevel_path = std::filesystem::path(toplevel_path_str).lexically_normal(); + auto path_to_check = std::filesystem::path(toplevel_path_str+path_to_check_str).lexically_normal(); + + //toplevel_path is part of toplevel_path, so if toplevel_path is less than path_to_check, then toplevel_path is certainly not a part of it + if (path_to_check < toplevel_path) {return false;} + if (path_to_check == toplevel_path) {return true;} + while (true) { + auto temp = path_to_check.parent_path(); + if (path_to_check == temp) { break;} + path_to_check = temp; + if (path_to_check < toplevel_path) {return false;} + if (path_to_check == toplevel_path) {return true;} + } + + return false; +} + +inline void add_module_isolated_os(VM *vm) { + if (vm->_lowest_isolated_cwd_path.empty()) { + throw std::invalid_argument("vm->_lowest_isolated_cwd_path is empty. Change it to a path of VM's allowed operation."); + } + if (!std::filesystem::exists(vm->_lowest_isolated_cwd_path)) { + throw std::invalid_argument("vm->_lowest_isolated_cwd_path doesn't exist."); + } + + PyObject* mod = vm->new_module("os"); + PyObject* path_obj = vm->heap.gcnew(vm->tp_object, {}); + mod->attr().set("path", path_obj); + + vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){ + return VAR(get_rel(vm, std::filesystem::current_path()).string()); + }); + + vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) { + std::filesystem::current_path(vm->_lowest_isolated_cwd_path); + return vm->None; + } + std::filesystem::current_path(vm->_lowest_isolated_cwd_path / path); + return vm->None; + }); + + vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Invalid path."));} + path = vm->_lowest_isolated_cwd_path / path; + + std::filesystem::directory_iterator di; + try{ + di = std::filesystem::directory_iterator(path); + }catch(std::filesystem::filesystem_error& e){ + std::string msg = e.what(); + auto pos = msg.find_last_of(':'); + if(pos != std::string::npos) msg = msg.substr(pos + 1); + vm->IOError(Str(msg).lstrip()); + } + List ret; + for(auto& p: di) ret.push_back(VAR(get_rel(vm, p.path()).filename().string())); + return VAR(ret); + }); + + vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));} + path = vm->_lowest_isolated_cwd_path / path; + + bool ok = std::filesystem::remove(path); + if(!ok) vm->IOError("Operation failed."); + return vm->None; + }); + + vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));} + path = vm->_lowest_isolated_cwd_path / path; + + bool ok = std::filesystem::create_directory(path); + if(!ok) vm->IOError("Operation failed."); + return vm->None; + }); + + vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));} + path = vm->_lowest_isolated_cwd_path / path; + + bool ok = std::filesystem::remove(path); + if(!ok) vm->IOError("Operation failed."); + return vm->None; + }); + + vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){ + std::filesystem::path path; + for(int i=0; ibind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + path = vm->_lowest_isolated_cwd_path / path; + bool exists = std::filesystem::exists(path); + return VAR(exists); + }); + + vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){ + std::filesystem::path path(CAST(Str&, args[0]).sv()); + return VAR(path.filename().string()); + }); +} + +}// namespace pkpy + +#else + +namespace pkpy{ +inline void add_module_isolated_os(void* vm){} +} // namespace pkpy + +#endif diff --git a/src/pocketpy.h b/src/pocketpy.h index bc62198b..8bf7977f 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -1333,7 +1333,11 @@ inline void VM::post_init(){ if(enable_os){ add_module_io(this); - add_module_os(this); + if (isolated_os) { + add_module_isolated_os(this); + } else { + add_module_os(this); + } add_module_requests(this); _import_handler = _default_import_handler; } diff --git a/src/vm.h b/src/vm.h index f50d7dd0..66c58062 100644 --- a/src/vm.h +++ b/src/vm.h @@ -25,6 +25,8 @@ namespace pkpy{ #define POPX() (s_data.popx()) #define STACK_VIEW(n) (s_data.view(n)) +typedef std::function ReadFileCwdFunc; + #define DEF_NATIVE_2(ctype, ptype) \ template<> inline ctype py_cast(VM* vm, PyObject* obj) { \ vm->check_non_tagged_type(obj, vm->ptype); \ @@ -98,7 +100,7 @@ struct FrameId{ Frame* operator->() const { return &data->operator[](index); } }; -typedef void(*PrintFunc)(VM*, const Str&); +typedef std::function PrintFunc; class VM { VM* vm; // self reference for simplify code @@ -123,7 +125,7 @@ public: PrintFunc _stdout; PrintFunc _stderr; - Bytes (*_import_handler)(const Str& name); + std::function _import_handler; // for quick access Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str; @@ -133,9 +135,14 @@ public: Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; Type tp_dict, tp_property, tp_star_wrapper; - const bool enable_os; + std::function check_is_invalid_io_path = [](const std::string&){return false;}; + std::string _lowest_isolated_cwd_path; // should be absolute, only used in isolated_io.h - VM(bool enable_os=true) : heap(this), enable_os(enable_os) { + const bool enable_os; + const bool isolated_os; + + VM(bool enable_os=true, bool isolated_os=false): + heap(this), enable_os(enable_os), isolated_os(isolated_os) { this->vm = this; _stdout = [](VM* vm, const Str& s) { std::cout << s; }; _stderr = [](VM* vm, const Str& s) { std::cerr << s; };