Skip to content

Commit 8329ad0

Browse files
Sandboxed API Teamcopybara-github
authored andcommitted
Reland using shared memory in s2
PiperOrigin-RevId: 867535377 Change-Id: I131223a942549ba7f433cb88670356086c730b3b
1 parent c8f1e04 commit 8329ad0

File tree

17 files changed

+312
-15
lines changed

17 files changed

+312
-15
lines changed

sandboxed_api/sandbox2/BUILD

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ cc_library(
185185
":logserver",
186186
":logsink",
187187
"//sandboxed_api/util:thread",
188-
"@abseil-cpp//absl/base:core_headers",
189188
"@abseil-cpp//absl/log",
190189
"@abseil-cpp//absl/strings",
191190
],
@@ -349,6 +348,7 @@ cc_library(
349348
copts = sapi_platform_copts(),
350349
visibility = ["//visibility:public"],
351350
deps = [
351+
":buffer",
352352
":client",
353353
":comms",
354354
":executor",
@@ -374,6 +374,7 @@ cc_library(
374374
"//sandboxed_api/sandbox2/network_proxy:client",
375375
"//sandboxed_api/sandbox2/network_proxy:filtering",
376376
"//sandboxed_api/util:fileops",
377+
"//sandboxed_api/util:status",
377378
"@abseil-cpp//absl/base",
378379
"@abseil-cpp//absl/base:core_headers",
379380
"@abseil-cpp//absl/container:flat_hash_map",
@@ -594,6 +595,7 @@ cc_library(
594595
copts = sapi_platform_copts(),
595596
visibility = ["//visibility:public"],
596597
deps = [
598+
":buffer",
597599
":comms",
598600
":logsink",
599601
":policy",
@@ -602,10 +604,13 @@ cc_library(
602604
"//sandboxed_api:config",
603605
"//sandboxed_api/sandbox2/network_proxy:client",
604606
"//sandboxed_api/sandbox2/util:bpf_helper",
607+
"//sandboxed_api/util:fileops",
605608
"//sandboxed_api/util:raw_logging",
609+
"//sandboxed_api/util:status",
606610
"@abseil-cpp//absl/base:core_headers",
607611
"@abseil-cpp//absl/container:flat_hash_map",
608612
"@abseil-cpp//absl/status",
613+
"@abseil-cpp//absl/status:statusor",
609614
"@abseil-cpp//absl/strings",
610615
],
611616
)
@@ -946,7 +951,6 @@ cc_test(
946951
deps = [
947952
":limits",
948953
":sandbox2",
949-
"//sandboxed_api:config",
950954
"//sandboxed_api:testing",
951955
"@googletest//:gtest_main",
952956
],
@@ -989,6 +993,7 @@ cc_test(
989993
"//sandboxed_api/sandbox2/testcases:policy",
990994
"//sandboxed_api/sandbox2/testcases:posix_timers",
991995
"//sandboxed_api/sandbox2/testcases:sandbox_detection",
996+
"//sandboxed_api/sandbox2/testcases:shared_memory",
992997
],
993998
tags = ["no_qemu_user_mode"],
994999
deps = [
@@ -1015,6 +1020,7 @@ cc_test(
10151020
"//sandboxed_api/sandbox2/testcases:abort",
10161021
"//sandboxed_api/sandbox2/testcases:custom_fork",
10171022
"//sandboxed_api/sandbox2/testcases:minimal",
1023+
"//sandboxed_api/sandbox2/testcases:shared_memory",
10181024
"//sandboxed_api/sandbox2/testcases:sleep",
10191025
"//sandboxed_api/sandbox2/testcases:starve",
10201026
"//sandboxed_api/sandbox2/testcases:terminate_process_group",
@@ -1030,7 +1036,6 @@ cc_test(
10301036
":util",
10311037
"//sandboxed_api:config",
10321038
"//sandboxed_api:testing",
1033-
"//sandboxed_api/sandbox2/allowlists:testonly_map_exec",
10341039
"//sandboxed_api/util:thread",
10351040
"@abseil-cpp//absl/log",
10361041
"@abseil-cpp//absl/status",

sandboxed_api/sandbox2/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ target_link_libraries(sandbox2_global_forkserver
247247
absl::cleanup
248248
absl::strings
249249
absl::status
250-
absl::statusor
251250
absl::log
252251
sandbox2::client
253252
sandbox2::flags
@@ -336,6 +335,7 @@ target_link_libraries(sandbox2_sandbox2
336335
sapi::config
337336
sapi::fileops
338337
sapi::temp_file
338+
sandbox2::buffer
339339
sandbox2::client
340340
sandbox2::comms
341341
sandbox2::executor
@@ -567,13 +567,17 @@ add_library(sandbox2::client ALIAS sandbox2_client)
567567
target_link_libraries(sandbox2_client
568568
PRIVATE absl::core_headers
569569
absl::strings
570+
absl::statusor
570571
sandbox2::bpf_helper
572+
sandbox2::buffer
571573
sandbox2::policy
572574
sandbox2::sanitizer
573575
sandbox2::syscall
574576
sapi::base
575577
sapi::config
578+
sapi::fileops
576579
sapi::raw_logging
580+
sapi::status
577581
PUBLIC absl::flat_hash_map
578582
absl::status
579583
sandbox2::comms

sandboxed_api/sandbox2/buffer.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ absl::StatusOr<std::unique_ptr<Buffer>> Buffer::Expand(
9393

9494
// Creates a new Buffer of the specified size, backed by a temporary file that
9595
// will be immediately deleted.
96-
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(size_t size) {
97-
absl::StatusOr<FDCloser> fd = util::CreateMemFd();
96+
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(
97+
size_t size, const char* name) {
98+
absl::StatusOr<FDCloser> fd = util::CreateMemFd(name);
9899
if (!fd.ok()) {
99100
return fd.status();
100101
}

sandboxed_api/sandbox2/buffer.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ class Buffer final {
5151
return CreateFromFd(sapi::file_util::fileops::FDCloser(fd));
5252
}
5353

54-
// Creates a new Buffer of the specified size, backed by a temporary file that
55-
// will be immediately deleted.
56-
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(size_t size);
54+
// Creates a new Buffer of the specified size, backed by a temporary file
55+
// (using memfd_create) that will be immediately deleted.
56+
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(
57+
size_t size, const char* name = "buffer_file");
5758

5859
// Expands the input buffer to the specified size.
5960
// Unlike CreateWithSize, this function will pre-allocate the memory.

sandboxed_api/sandbox2/client.cc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include <linux/bpf_common.h>
2121
#include <linux/filter.h>
2222
#include <linux/seccomp.h>
23+
#include <sys/mman.h>
2324
#include <sys/prctl.h>
25+
#include <sys/stat.h>
2426
#include <syscall.h>
2527
#include <unistd.h>
2628

@@ -40,20 +42,24 @@
4042
#include "absl/base/macros.h"
4143
#include "absl/container/flat_hash_map.h"
4244
#include "absl/status/status.h"
45+
#include "absl/status/statusor.h"
4346
#include "absl/strings/numbers.h"
4447
#include "absl/strings/str_cat.h"
4548
#include "absl/strings/str_join.h"
4649
#include "absl/strings/str_split.h"
4750
#include "absl/strings/string_view.h"
4851
#include "sandboxed_api/config.h"
52+
#include "sandboxed_api/sandbox2/buffer.h"
4953
#include "sandboxed_api/sandbox2/comms.h"
5054
#include "sandboxed_api/sandbox2/logsink.h"
5155
#include "sandboxed_api/sandbox2/network_proxy/client.h"
5256
#include "sandboxed_api/sandbox2/policy.h"
5357
#include "sandboxed_api/sandbox2/sanitizer.h"
5458
#include "sandboxed_api/sandbox2/syscall.h"
5559
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
60+
#include "sandboxed_api/util/fileops.h"
5661
#include "sandboxed_api/util/raw_logging.h"
62+
#include "sandboxed_api/util/status_macros.h"
5763

5864
#ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
5965
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
@@ -384,7 +390,7 @@ int Client::GetMappedFD(const std::string& name) {
384390
return fd;
385391
}
386392

387-
bool Client::HasMappedFD(const std::string& name) {
393+
bool Client::HasMappedFD(const std::string& name) const {
388394
return fd_map_.find(name) != fd_map_.end();
389395
}
390396

@@ -413,4 +419,24 @@ absl::Status Client::InstallNetworkProxyHandler() {
413419
GetNetworkProxyClient());
414420
}
415421

422+
absl::StatusOr<const Buffer*> Client::GetSharedMemoryMapping() {
423+
if (shared_memory_mapping_ != nullptr) {
424+
return shared_memory_mapping_.get();
425+
}
426+
427+
if (!IsSharedMemoryEnabled()) {
428+
return absl::FailedPreconditionError("Shared memory is not enabled");
429+
}
430+
431+
int shared_memory_fd = GetMappedFD("s2_shared_memory");
432+
SAPI_ASSIGN_OR_RETURN(shared_memory_mapping_,
433+
Buffer::CreateFromFd(sapi::file_util::fileops::FDCloser(
434+
shared_memory_fd)));
435+
return shared_memory_mapping_.get();
436+
}
437+
438+
bool Client::IsSharedMemoryEnabled() const {
439+
return shared_memory_mapping_ != nullptr || HasMappedFD("s2_shared_memory");
440+
}
441+
416442
} // namespace sandbox2

sandboxed_api/sandbox2/client.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include "absl/container/flat_hash_map.h"
2727
#include "absl/status/status.h"
28+
#include "absl/status/statusor.h"
29+
#include "sandboxed_api/sandbox2/buffer.h"
2830
#include "sandboxed_api/sandbox2/comms.h"
2931
#include "sandboxed_api/sandbox2/logsink.h"
3032
#include "sandboxed_api/sandbox2/network_proxy/client.h"
@@ -63,7 +65,7 @@ class Client {
6365
// Returns the file descriptor that was mapped to the sandboxee using
6466
// IPC::ReceiveFd(name).
6567
int GetMappedFD(const std::string& name);
66-
bool HasMappedFD(const std::string& name);
68+
bool HasMappedFD(const std::string& name) const;
6769

6870
// Registers a LogSink that forwards all logs to the supervisor.
6971
void SendLogsToSupervisor();
@@ -76,6 +78,12 @@ class Client {
7678
// the NetworkProxyClient class.
7779
absl::Status InstallNetworkProxyHandler();
7880

81+
// Gets the shared memory buffer if it is enabled.
82+
absl::StatusOr<const Buffer*> GetSharedMemoryMapping();
83+
84+
// Returns true if shared memory was enabled.
85+
bool IsSharedMemoryEnabled() const;
86+
7987
protected:
8088
// Comms used for synchronization with the monitor, not owned by the object.
8189
Comms* comms_;
@@ -100,6 +108,10 @@ class Client {
100108
// this case that is parsed in the Client constructor if present.
101109
absl::flat_hash_map<std::string, int> fd_map_;
102110

111+
// The mapping of the shared memory region in the sandboxee's address space.
112+
// This is only set if shared memory is enabled.
113+
std::unique_ptr<Buffer> shared_memory_mapping_;
114+
103115
std::string GetFdMapEnvVar() const;
104116

105117
// Sets up communication channels with the sandbox.

sandboxed_api/sandbox2/ipc.cc

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,36 @@ namespace sandbox2 {
3535

3636
void IPC::SetUpServerSideComms(int fd) { comms_ = std::make_unique<Comms>(fd); }
3737

38-
void IPC::MapFd(int local_fd, int remote_fd) {
38+
void IPC::MapFd(int local_fd, int remote_fd, absl::string_view name) {
3939
VLOG(3) << "Will send: " << local_fd << ", to overwrite: " << remote_fd;
40-
fd_map_.push_back(std::make_tuple(local_fd, remote_fd, ""));
40+
fd_map_.push_back(std::make_tuple(local_fd, remote_fd, std::string(name)));
4141
}
4242

43-
void IPC::MapDupedFd(int local_fd, int remote_fd) {
43+
void IPC::MapFd(int local_fd, absl::string_view name) {
44+
return MapFd(local_fd, -1, name);
45+
}
46+
47+
void IPC::MapFd(int local_fd, int remote_fd) {
48+
return MapFd(local_fd, remote_fd, "");
49+
}
50+
51+
void IPC::MapDupedFd(int local_fd, int remote_fd, absl::string_view name) {
4452
const int dup_local_fd = dup(local_fd);
4553
if (dup_local_fd == -1) {
4654
PLOG(FATAL) << "dup(" << local_fd << ")";
4755
}
4856
VLOG(3) << "Will send: " << dup_local_fd << " (dup of " << local_fd
4957
<< "), to overwrite: " << remote_fd;
50-
fd_map_.push_back(std::make_tuple(dup_local_fd, remote_fd, ""));
58+
fd_map_.push_back(
59+
std::make_tuple(dup_local_fd, remote_fd, std::string(name)));
60+
}
61+
62+
void IPC::MapDupedFd(int local_fd, absl::string_view name) {
63+
return MapDupedFd(local_fd, -1, name);
64+
}
65+
66+
void IPC::MapDupedFd(int local_fd, int remote_fd) {
67+
return MapDupedFd(local_fd, remote_fd, "");
5168
}
5269

5370
int IPC::ReceiveFd(int remote_fd) { return ReceiveFd(remote_fd, ""); }

sandboxed_api/sandbox2/ipc.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,30 @@ class IPC final {
4747
// being sent (in SendFdsOverComms() which is called by the Monitor class when
4848
// Sandbox2::RunAsync() is called), so local_fd should not be used from that
4949
// point on. The application must not close local_fd after calling MapFd().
50+
// If a name is specified, users can use Client::GetMappedFD api to retrieve
51+
// the fd in the sandboxee.
52+
void MapFd(int local_fd, int remote_fd, absl::string_view name);
53+
54+
// Similar to MapFd() above, except that we do not name the remote_fd.
5055
void MapFd(int local_fd, int remote_fd);
5156

57+
// Similar to MapFd() above, except that we do not pin the remote_fd in the
58+
// sandboxee.
59+
void MapFd(int local_fd, absl::string_view name);
60+
5261
// Similar to MapFd(), except local_fd remains available for use in the
5362
// application even after Sandbox2::RunAsync() is called; the application
5463
// retains responsibility for closing local_fd and may do so at any time after
5564
// calling MapDupedFd().
65+
// If a name is specified, users can use Client::GetMappedFD api to retrieve
66+
// the fd in the sandboxee.
67+
void MapDupedFd(int local_fd, int remote_fd, absl::string_view name);
68+
69+
// Similar to MapDupedFd() above, except that we do not pin the remote_fd in
70+
// the sandboxee.
71+
void MapDupedFd(int local_fd, absl::string_view name);
72+
73+
// Similar to MapDupedFd() above, except that we do not name the remote_fd.
5674
void MapDupedFd(int local_fd, int remote_fd);
5775

5876
// Creates and returns a socketpair endpoint. The other endpoint of the

sandboxed_api/sandbox2/policy_test.cc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,46 @@ TEST_P(PolicyTest, OverridablePolicyOnSyscallsWorks) {
582582
EXPECT_THAT(result.reason_code(), Eq(0));
583583
}
584584

585+
TEST_P(PolicyTest, InstallSharedMemoryWithWrongPolicy) {
586+
SKIP_SANITIZERS;
587+
// The policy doesn't allow installing shared memory, so the sandboxee won't
588+
// be able to map the shared memory region.
589+
sandbox2::PolicyBuilder builder;
590+
builder.AllowStaticStartup();
591+
builder.AllowMmapWithoutExec();
592+
builder.AllowTcMalloc();
593+
builder.AllowExit();
594+
auto s2 = CreateTestSandbox({"shared_memory", "2"}, builder);
595+
ASSERT_THAT(s2->CreateSharedMemoryMapping(100ULL << 20), IsOk());
596+
auto result = s2->Run();
597+
598+
ASSERT_EQ(result.final_status(), sandbox2::Result::VIOLATION);
599+
EXPECT_EQ(result.reason_code(), sandbox2::Result::VIOLATION_SYSCALL);
600+
ASSERT_TRUE(result.GetSyscall() != nullptr);
601+
EXPECT_THAT(result.GetSyscall()->nr(), Eq(__NR_fstat));
602+
}
603+
604+
TEST_P(PolicyTest, InstallSharedMemoryWithMinimalPolicy) {
605+
SKIP_SANITIZERS;
606+
// The policy allows installing shared memory, so the sandboxee will be able
607+
// to map the shared memory region, even though we enabled sandbox before
608+
// execve.
609+
sandbox2::PolicyBuilder builder;
610+
builder.AllowStaticStartup();
611+
builder.AllowSharedMemory();
612+
builder.AllowTcMalloc();
613+
builder.AllowExit();
614+
auto s2 = CreateTestSandbox({"shared_memory", "2"}, builder);
615+
SAPI_ASSERT_OK_AND_ASSIGN(auto res,
616+
s2->CreateSharedMemoryMapping(100ULL << 20));
617+
res->data()[0] = 'Z';
618+
auto result = s2->Run();
619+
620+
ASSERT_EQ(result.final_status(), sandbox2::Result::OK);
621+
EXPECT_EQ(result.reason_code(), 0);
622+
EXPECT_EQ(res->data()[0], 'A');
623+
}
624+
585625
INSTANTIATE_TEST_SUITE_P(Sandbox2, PolicyTest, ::testing::Values(false, true),
586626
[](const ::testing::TestParamInfo<bool>& info) {
587627
return info.param ? "UnotifyMonitor"

sandboxed_api/sandbox2/policybuilder.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,27 @@ PolicyBuilder& PolicyBuilder::AllowDynamicStartup(MapExec) {
14611461
});
14621462
}
14631463

1464+
PolicyBuilder& PolicyBuilder::AllowSharedMemory() {
1465+
if (allowed_complex_.shared_memory) {
1466+
return *this;
1467+
}
1468+
allowed_complex_.shared_memory = true;
1469+
AddPolicyOnMmap([](bpf_labels& labels) -> std::vector<sock_filter> {
1470+
return {
1471+
ARG_32(2), // prot
1472+
JNE32(PROT_READ | PROT_WRITE, JUMP(&labels, mmap_end)),
1473+
1474+
ARG_32(3), // flags
1475+
JEQ32(MAP_SHARED, ALLOW),
1476+
1477+
LABEL(&labels, mmap_end),
1478+
};
1479+
});
1480+
AllowSyscalls({__NR_munmap, __NR_close});
1481+
AllowStat();
1482+
return *this;
1483+
}
1484+
14641485
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(
14651486
uint32_t num, absl::Span<const sock_filter> policy) {
14661487
return AddPolicyOnSyscalls({num}, policy);

0 commit comments

Comments
 (0)