Skip to content

Commit 8a562d3

Browse files
Sandboxed API Teamcopybara-github
Sandboxed API Team
authored andcommitted
sandbox2: Buffer::Expand() implementation
Adds an API for resizing a shared Buffer after it has been created. PiperOrigin-RevId: 763920344 Change-Id: Ia45fb1cd5e89e3300f781e922b4644b9b9d6bd5e
1 parent 08f4d5d commit 8a562d3

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

sandboxed_api/sandbox2/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,8 +816,12 @@ cc_test(
816816
":buffer",
817817
":sandbox2",
818818
"//sandboxed_api:testing",
819+
"//sandboxed_api/util:file_base",
819820
"//sandboxed_api/util:fileops",
820821
"//sandboxed_api/util:status_matchers",
822+
"@abseil-cpp//absl/status",
823+
"@abseil-cpp//absl/status:statusor",
824+
"@abseil-cpp//absl/strings:string_view",
821825
"@googletest//:gtest_main",
822826
],
823827
)

sandboxed_api/sandbox2/buffer.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "sandboxed_api/sandbox2/buffer.h"
1616

17+
#include <fcntl.h>
1718
#include <sys/mman.h>
1819
#include <sys/stat.h>
1920
#include <unistd.h>
@@ -57,6 +58,39 @@ absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateFromFd(FDCloser fd,
5758
return absl::WrapUnique(new Buffer(std::move(fd), buf, size));
5859
}
5960

61+
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::Expand(
62+
std::unique_ptr<Buffer> other, size_t size) {
63+
if (!other) {
64+
return absl::InvalidArgumentError("Buffer is null");
65+
}
66+
if (other->buf_ == nullptr) {
67+
return absl::FailedPreconditionError("Buffer is not initialized");
68+
}
69+
if (other->fd_.get() < 0) {
70+
return absl::FailedPreconditionError("Buffer is not backed by a valid fd");
71+
}
72+
if (size < other->size_) {
73+
return absl::InvalidArgumentError("Buffer size cannot be reduced");
74+
}
75+
if (other->size_ == size) {
76+
return other;
77+
}
78+
if (fallocate(other->fd_.get(), 0, 0, size) != 0) {
79+
return absl::ErrnoToStatus(errno, "Could not extend buffer fd");
80+
}
81+
uint8_t* new_buf_ = reinterpret_cast<uint8_t*>(
82+
mremap(other->buf_, other->size_, size, MREMAP_MAYMOVE));
83+
if (new_buf_ == MAP_FAILED) {
84+
// At this point fallocate succeeded, and remapping failed.
85+
// The incoming buffer is in an undefined state. It will be destroyed
86+
// when this function returns.
87+
return absl::ErrnoToStatus(errno, "Could not map buffer fd");
88+
}
89+
other->buf_ = new_buf_;
90+
other->size_ = size;
91+
return other;
92+
}
93+
6094
// Creates a new Buffer of the specified size, backed by a temporary file that
6195
// will be immediately deleted.
6296
absl::StatusOr<std::unique_ptr<Buffer>> Buffer::CreateWithSize(size_t size) {

sandboxed_api/sandbox2/buffer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <utility>
2222

2323
#include "absl/base/macros.h"
24+
#include "absl/status/status.h"
2425
#include "absl/status/statusor.h"
2526
#include "sandboxed_api/util/fileops.h"
2627

@@ -54,6 +55,12 @@ class Buffer final {
5455
// will be immediately deleted.
5556
static absl::StatusOr<std::unique_ptr<Buffer>> CreateWithSize(size_t size);
5657

58+
// Expands the input buffer to the specified size.
59+
// Unlike CreateWithSize, this function will pre-allocate the memory.
60+
// If size is smaller than the current mapped size, the function will fail.
61+
static absl::StatusOr<std::unique_ptr<Buffer>> Expand(
62+
std::unique_ptr<Buffer> other, size_t size);
63+
5764
// Returns a pointer to the buffer, which is read/write.
5865
uint8_t* data() const { return buf_; }
5966

sandboxed_api/sandbox2/buffer_test.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
#include "sandboxed_api/sandbox2/buffer.h"
1616

17+
#include <fcntl.h>
1718
#include <sys/stat.h>
19+
#include <sys/types.h>
1820
#include <unistd.h>
1921

2022
#include <cstdint>
@@ -25,13 +27,16 @@
2527

2628
#include "gmock/gmock.h"
2729
#include "gtest/gtest.h"
30+
#include "absl/status/statusor.h"
31+
#include "absl/strings/string_view.h"
2832
#include "sandboxed_api/sandbox2/executor.h"
2933
#include "sandboxed_api/sandbox2/ipc.h"
3034
#include "sandboxed_api/sandbox2/policy.h"
3135
#include "sandboxed_api/sandbox2/result.h"
3236
#include "sandboxed_api/sandbox2/sandbox2.h"
3337
#include "sandboxed_api/testing.h"
3438
#include "sandboxed_api/util/fileops.h"
39+
#include "sandboxed_api/util/path.h"
3540
#include "sandboxed_api/util/status_matchers.h"
3641

3742
namespace sandbox2 {
@@ -94,5 +99,45 @@ TEST(BufferTest, TestWithSandboxeeMapFd) {
9499
struct stat stat_buf;
95100
EXPECT_THAT(fstat(buffer->fd(), &stat_buf), Ne(-1));
96101
}
102+
103+
TEST(BufferTest, TestResize) {
104+
constexpr int kSize = 1024;
105+
SAPI_ASSERT_OK_AND_ASSIGN(auto buffer, Buffer::CreateWithSize(kSize));
106+
EXPECT_THAT(buffer->size(), Eq(kSize));
107+
uint8_t* raw_buf = buffer->data();
108+
for (int i = 0; i < kSize; i++) {
109+
raw_buf[i] = 'X';
110+
}
111+
int fd = buffer->fd();
112+
SAPI_ASSERT_OK_AND_ASSIGN(buffer,
113+
Buffer::Expand(std::move(buffer), kSize * 2));
114+
EXPECT_THAT(buffer->size(), Eq(kSize * 2));
115+
EXPECT_THAT(buffer->data(), Ne(nullptr));
116+
EXPECT_THAT(buffer->fd(), Eq(fd)); // fd should not have changed.
117+
absl::string_view buf_begin_view(reinterpret_cast<char*>(buffer->data()),
118+
kSize);
119+
EXPECT_THAT(buf_begin_view, Eq(std::string(kSize, 'X')));
120+
}
121+
122+
TEST(BufferTest, TestResizeDoesNotTruncateFile) {
123+
const std::string tmp_file =
124+
sapi::file::JoinPath(testing::TempDir(), "1mb.file");
125+
FDCloser fdcloser(open(tmp_file.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600));
126+
int fd = fdcloser.get();
127+
ASSERT_THAT(fd, Ne(-1));
128+
ASSERT_THAT(ftruncate(fd, 1 << 20), Ne(-1));
129+
// Map only 4k of the 1mb file.
130+
SAPI_ASSERT_OK_AND_ASSIGN(absl::StatusOr<std::unique_ptr<Buffer>> buf,
131+
Buffer::CreateFromFd(std::move(fdcloser), 4096));
132+
// Expand the mapped buffer to 8k.
133+
SAPI_ASSERT_OK_AND_ASSIGN(auto expanded,
134+
Buffer::Expand(std::move(*buf), 8192));
135+
EXPECT_THAT(expanded->size(), Eq(8192));
136+
struct stat stat_buf;
137+
// File size should not have changed.
138+
ASSERT_THAT(stat(tmp_file.c_str(), &stat_buf), Ne(-1));
139+
EXPECT_THAT(stat_buf.st_size, Eq(1 << 20));
140+
}
141+
97142
} // namespace
98143
} // namespace sandbox2

0 commit comments

Comments
 (0)