Skip to content

Commit 89b8750

Browse files
authored
Merge pull request #31 from varnish/file_descriptors
Managed file descriptors, loading dynamic executables
2 parents 162af82 + 9815137 commit 89b8750

19 files changed

+1482
-721
lines changed

CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,20 @@ target_compile_definitions(tinykvm PUBLIC
2222

2323
add_executable(bench
2424
src/bench.cpp
25-
src/functions.cpp
2625
)
2726
target_link_libraries(bench tinykvm)
2827

2928
add_executable(tinytest
3029
src/tests.cpp
31-
src/functions.cpp
3230
)
3331
target_link_libraries(tinytest tinykvm)
3432

3533
add_executable(simplekvm
3634
src/simple.cpp
37-
src/functions.cpp
3835
)
3936
target_link_libraries(simplekvm tinykvm)
4037

4138
add_executable(storagekvm
4239
src/storage.cpp
43-
src/functions.cpp
4440
)
4541
target_link_libraries(storagekvm tinykvm)

lib/CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ set (SOURCES
2222
tinykvm/memory_bank.cpp
2323
tinykvm/page_streaming.cpp
2424
tinykvm/remote.cpp
25-
tinykvm/signals.cpp
2625
tinykvm/smp.cpp
27-
tinykvm/threads.cpp
2826
tinykvm/vcpu.cpp
2927
tinykvm/vcpu_run.cpp
28+
29+
tinykvm/linux/fds.cpp
30+
tinykvm/linux/signals.cpp
31+
tinykvm/linux/system_calls.cpp
32+
tinykvm/linux/threads.cpp
3033
)
3134
if (TINYKVM_ARCH STREQUAL "AMD64")
3235
list(APPEND SOURCES

lib/tinykvm/common.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,14 @@ namespace tinykvm
119119
int userval2;
120120
int userval3;
121121
};
122+
123+
struct DynamicElf {
124+
std::string interpreter;
125+
bool is_dynamic;
126+
127+
bool has_interpreter() const noexcept {
128+
return !interpreter.empty();
129+
}
130+
};
131+
extern DynamicElf is_dynamic_elf(std::string_view bin);
122132
}

lib/tinykvm/linux/fds.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#include "fds.hpp"
2+
3+
#include "../machine.hpp"
4+
#include "threads.hpp"
5+
#include <fcntl.h>
6+
#include <cstring>
7+
#include <sys/stat.h>
8+
#include <unistd.h>
9+
10+
namespace tinykvm
11+
{
12+
FileDescriptors& Machine::fds()
13+
{
14+
if (!m_fds) {
15+
m_fds = std::make_unique<FileDescriptors>(*this);
16+
}
17+
return *m_fds;
18+
}
19+
const FileDescriptors& Machine::fds() const
20+
{
21+
return const_cast<Machine&>(*this).fds();
22+
}
23+
24+
FileDescriptors::FileDescriptors(Machine& machine)
25+
: m_machine(machine)
26+
{
27+
m_allowed_readable_paths =
28+
std::make_shared<std::unordered_set<std::string>>();
29+
// Add all common standard libraries to the list of allowed readable paths
30+
this->add_readonly_file("/lib64/ld-linux-x86-64.so.2");
31+
this->add_readonly_file("/lib/x86_64-linux-gnu/libgcc_s.so.1");
32+
this->add_readonly_file("/lib/x86_64-linux-gnu/libc.so.6");
33+
this->add_readonly_file("/lib/x86_64-linux-gnu/libm.so.6");
34+
this->add_readonly_file("/lib/x86_64-linux-gnu/libpthread.so.0");
35+
this->add_readonly_file("/lib/x86_64-linux-gnu/libdl.so.2");
36+
this->add_readonly_file("/lib/x86_64-linux-gnu/libstdc++.so.6");
37+
this->add_readonly_file("/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v2/libstdc++.so.6");
38+
this->add_readonly_file("/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3/libstdc++.so.6");
39+
}
40+
41+
FileDescriptors::~FileDescriptors()
42+
{
43+
for (auto& [fd, entry] : m_fds) {
44+
if (entry.real_fd >= 0) {
45+
close(entry.real_fd);
46+
}
47+
}
48+
}
49+
50+
void FileDescriptors::reset_to(const FileDescriptors& other)
51+
{
52+
// Close all current file descriptors
53+
// We don't have a sandbox-safe way to share
54+
// file descriptors between VMs, so just close.
55+
for (auto& [fd, entry] : m_fds) {
56+
if (entry.real_fd >= 0) {
57+
close(entry.real_fd);
58+
}
59+
}
60+
// Clear the current file descriptors
61+
m_fds.clear();
62+
m_next_file_fd = other.m_next_file_fd;
63+
m_next_socket_fd = other.m_next_socket_fd;
64+
m_allowed_readable_paths = other.m_allowed_readable_paths;
65+
}
66+
67+
int FileDescriptors::manage(int fd, bool is_socket, bool is_writable)
68+
{
69+
if (fd < 0) {
70+
throw std::runtime_error("Invalid file descriptor in FileDescriptors::add()");
71+
}
72+
if (is_socket) {
73+
m_fds[m_next_socket_fd] = {fd, is_writable};
74+
return m_next_socket_fd++;
75+
} else {
76+
m_fds[m_next_file_fd] = {fd, is_writable};
77+
return m_next_file_fd++;
78+
}
79+
}
80+
81+
std::optional<FileDescriptors::Entry*> FileDescriptors::entry_for_vfd(int vfd)
82+
{
83+
auto it = m_fds.find(vfd);
84+
if (it != m_fds.end()) {
85+
return &it->second;
86+
}
87+
return std::nullopt;
88+
}
89+
90+
int FileDescriptors::translate(int vfd) const
91+
{
92+
auto it = m_fds.find(vfd);
93+
if (it != m_fds.end()) {
94+
return it->second.real_fd;
95+
}
96+
throw std::runtime_error("Invalid virtual file descriptor");
97+
}
98+
99+
void FileDescriptors::free(int vfd)
100+
{
101+
auto it = m_fds.find(vfd);
102+
if (it != m_fds.end()) {
103+
close(it->second.real_fd);
104+
m_fds.erase(it);
105+
}
106+
}
107+
108+
void FileDescriptors::add_readonly_file(const std::string& path)
109+
{
110+
this->m_allowed_readable_paths->insert(path);
111+
}
112+
113+
bool FileDescriptors::is_readable_path(std::string& modifiable_path) const noexcept
114+
{
115+
if (m_open_readable)
116+
{
117+
if (m_open_readable(modifiable_path))
118+
return true;
119+
}
120+
auto it = m_allowed_readable_paths->find(modifiable_path);
121+
return (it != m_allowed_readable_paths->end());
122+
}
123+
124+
bool FileDescriptors::is_writable_path(std::string& modifiable_path) const noexcept
125+
{
126+
if (m_open_writable) {
127+
return m_open_writable(modifiable_path);
128+
}
129+
return false;
130+
}
131+
132+
} // tinykvm

lib/tinykvm/linux/fds.hpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <functional>
5+
#include <map>
6+
#include <memory>
7+
#include <optional>
8+
#include <string>
9+
#include <unordered_set>
10+
11+
namespace tinykvm
12+
{
13+
struct Machine;
14+
15+
struct FileDescriptors
16+
{
17+
static constexpr int SOCKET_BIT = 0x40000000;
18+
struct Entry
19+
{
20+
int real_fd = -1;
21+
bool is_writable = false;
22+
};
23+
using open_readable_t = std::function<bool(std::string&)>;
24+
using open_writable_t = std::function<bool(std::string&)>;
25+
26+
FileDescriptors(Machine& machine);
27+
~FileDescriptors();
28+
void reset_to(const FileDescriptors& other);
29+
30+
/// @brief Add a file descriptor to the list of managed FDs.
31+
/// @param fd The real file descriptor.
32+
/// @param is_socket True if the file descriptor is a socket.
33+
/// @return The virtual file descriptor.
34+
int manage(int fd, bool is_socket, bool is_writable = false);
35+
36+
/// @brief Remove a virtual file descriptor from the list of managed FDs.
37+
/// @param vfd The virtual file descriptor to remove.
38+
void free(int vfd);
39+
40+
std::optional<Entry*> entry_for_vfd(int vfd);
41+
42+
/// @brief Translate a virtual file descriptor to a real file descriptor,
43+
/// or throw an exception, failing execution.
44+
/// @param vfd The virtual file descriptor to translate.
45+
/// @return The real file descriptor.
46+
int translate(int vfd) const;
47+
48+
bool is_socket_vfd(int vfd) const noexcept {
49+
return (vfd & SOCKET_BIT) != 0;
50+
}
51+
bool is_file_vfd(int vfd) const noexcept {
52+
return (vfd & SOCKET_BIT) == 0;
53+
}
54+
55+
/// @brief Set the callback for checking if a path is allowed to be opened
56+
/// for reading. This is used to check if a path is allowed to be opened
57+
/// for reading. The callback should return true if the path is allowed,
58+
/// and false otherwise. The path may be modified by the callback,
59+
/// indicating which real path to open.
60+
/// @param callback The callback to set.
61+
void set_open_readable_callback(open_readable_t callback) noexcept {
62+
m_open_readable = callback;
63+
}
64+
65+
/// @brief Set the callback for checking if a path is allowed to be opened
66+
/// for writing. This is used to check if a path is allowed to be opened
67+
/// for writing. The callback should return true if the path is allowed,
68+
/// and false otherwise. The path may be modified by the callback,
69+
/// indicating which real path to open.
70+
/// @param callback The callback to set.
71+
void set_open_writable_callback(open_writable_t callback) noexcept {
72+
m_open_writable = callback;
73+
}
74+
75+
/// @brief Add a read-only file
76+
void add_readonly_file(const std::string& path);
77+
78+
/// @brief Check if a path is allowed to be opened for reading.
79+
/// @param modifiable_path The path to check. This may be modified by
80+
/// the callback to indicate which real path to open.
81+
/// @return True if the path is allowed to be opened for reading, false
82+
/// otherwise. The path may be modified by the callback, indicating
83+
/// which real path to open.
84+
bool is_readable_path(std::string& modifiable_path) const noexcept;
85+
86+
/// @brief Check if a path is writable. Paths are usually not writable,
87+
/// but this can be overridden by setting the open_writable callback.
88+
/// @param modifiable_path
89+
/// @return True if the path is writable, false otherwise. The path
90+
/// may be modified by the callback, indicating which real path to open.
91+
bool is_writable_path(std::string& modifiable_path) const noexcept;
92+
93+
private:
94+
Machine& m_machine;
95+
std::map<int, Entry> m_fds;
96+
std::shared_ptr<std::unordered_set<std::string>> m_allowed_readable_paths;
97+
int m_next_file_fd = 0x1000;
98+
int m_next_socket_fd = 0x1000 | SOCKET_BIT;
99+
bool m_verbose = true;
100+
open_readable_t m_open_readable;
101+
open_writable_t m_open_writable;
102+
};
103+
}

lib/tinykvm/signals.cpp renamed to lib/tinykvm/linux/signals.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "machine.hpp"
1+
#include "../machine.hpp"
22
#include "threads.hpp"
33

44
namespace tinykvm {
@@ -33,4 +33,9 @@ void Signals::enter(vCPU& cpu, int sig)
3333
cpu.set_registers(regs);
3434
}
3535

36+
SignalAction& Machine::sigaction(int sig)
37+
{
38+
return signals().get(sig);
39+
}
40+
3641
} // tinykvm

lib/tinykvm/signals.hpp renamed to lib/tinykvm/linux/signals.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#pragma once
2-
#include "forward.hpp"
2+
#include "../forward.hpp"
33
#include <array>
44
#include <map>
55
#include <memory>

0 commit comments

Comments
 (0)