Skip to content

Commit dd45581

Browse files
paulsemelcopybara-github
authored andcommitted
Introduce a sandboxee_read_index field in AsynchronousByteTransport
The point is that the sandboxee might `execve` at some point, and lose track of where its read_index previously was. The ensures that the sandboxee will keep reading at the correct index after the `exec` system call. PiperOrigin-RevId: 910687145 Change-Id: I7c729afc171be14f4a0932c12ae44625a77517bd
1 parent a7d14a7 commit dd45581

3 files changed

Lines changed: 41 additions & 17 deletions

File tree

sandboxed_api/sandbox2/util/asynchronous_byte_transport.cc

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ AsynchronousByteTransport::AsynchronousByteTransport(
208208
GetHeader()->h2s.state.store(0, std::memory_order_release);
209209
GetHeader()->s2h.state.store(0, std::memory_order_release);
210210
GetHeader()->synchronization_type = synchronization_type;
211+
GetHeader()->sandboxee_read_index = 0;
211212
}
212213
}
213214

@@ -278,12 +279,13 @@ absl::Status AsynchronousByteTransport::Read(absl::Span<uint8_t> data) {
278279
return absl::AbortedError("Write index out of bounds");
279280
}
280281

281-
if (read_index_ > write_index) {
282+
uint32_t& read_index = GetReadIndex();
283+
if (read_index > write_index) {
282284
return absl::AbortedError("Read index out of bounds");
283285
}
284286

285287
absl::Span<const uint8_t> read_buffer =
286-
read_data_.subspan(read_index_, write_index);
288+
read_data_.subspan(read_index, write_index);
287289
if (read_buffer.empty()) {
288290
uint32_t termination_state =
289291
termination_state_.load(std::memory_order_relaxed);
@@ -296,18 +298,18 @@ absl::Status AsynchronousByteTransport::Read(absl::Span<uint8_t> data) {
296298
}
297299

298300
size_t chunk_size =
299-
std::min(kChunkSize, static_cast<size_t>(write_index - read_index_));
301+
std::min(kChunkSize, static_cast<size_t>(write_index - read_index));
300302
absl::Span<uint8_t> chunk = data.subspan(0, chunk_size);
301303

302304
memcpy(chunk.data(), read_buffer.data(), chunk.size());
303-
read_index_ += chunk.size();
305+
read_index += chunk.size();
304306
data.remove_prefix(chunk.size());
305307

306-
if (read_index_ == write_index) {
308+
if (read_index == write_index) {
307309
// If the connection is closed, we will not read any more data at this
308310
// point, but we will still acknowledge the last bit of data that was sent
309311
// from the other side. Note that in the event of an attacker controlled
310-
// client, we will read from read_index_ up to the size of the buffer
312+
// client, we will read from read_index up to the size of the buffer
311313
// until we acknowledge the connection is closed. This is needed so that
312314
// we can support legitimate scenarios where the client sends size_of_buf
313315
// - 1 bytes and then closes the connection. In this case, the host still
@@ -348,7 +350,8 @@ bool AsynchronousByteTransport::ResetWriteIndex(ChannelHeader* channel,
348350
if (state & kWaitForWritingBit) {
349351
WakeInternal(channel);
350352
}
351-
read_index_ = 0;
353+
uint32_t& read_index = GetReadIndex();
354+
read_index = 0;
352355
return true;
353356
}
354357

@@ -430,7 +433,7 @@ void AsynchronousByteTransport::WakeInternal(ChannelHeader* channel,
430433

431434
int AsynchronousByteTransport::TransferInternal(ChannelHeader* write_channel,
432435
ChannelHeader* read_channel) {
433-
uint32_t read_state = read_index_;
436+
uint32_t read_state = GetReadIndex();
434437
// If the counterpart has already written data and is waiting for reading,
435438
// we just need to wake them up and get back to reading.
436439
if (!read_channel->state.compare_exchange_strong(

sandboxed_api/sandbox2/util/asynchronous_byte_transport.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class AsynchronousByteTransport {
126126

127127
struct Header {
128128
SynchronizationType synchronization_type;
129+
uint32_t sandboxee_read_index;
129130
ChannelHeader h2s; // Host to Sandboxee
130131
ChannelHeader s2h; // Sandboxee to Host
131132
// Must be last.
@@ -164,6 +165,12 @@ class AsynchronousByteTransport {
164165
int TransferInternal(ChannelHeader* write_channel,
165166
ChannelHeader* read_channel);
166167

168+
uint32_t& GetReadIndex() {
169+
return client_type_ == ClientType::kHost
170+
? read_index_
171+
: GetHeader()->sandboxee_read_index;
172+
}
173+
167174
std::unique_ptr<sandbox2::Buffer> buffer_;
168175
ClientType client_type_;
169176
uint32_t read_index_ = 0;

sandboxed_api/sandbox2/util/asynchronous_byte_transport_test.cc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <cstddef>
2323
#include <cstdint>
24+
#include <cstring>
2425
#include <memory>
2526
#include <utility>
2627
#include <vector>
@@ -30,6 +31,7 @@
3031
#include "absl/log/check.h"
3132
#include "absl/status/status.h"
3233
#include "absl/synchronization/notification.h"
34+
#include "absl/time/clock.h"
3335
#include "absl/time/time.h"
3436
#include "absl/types/span.h"
3537
#include "sandboxed_api/sandbox2/buffer.h"
@@ -139,25 +141,25 @@ class TestHelper {
139141
while (comms.RecvInt32(reinterpret_cast<int32_t*>(&action_type))) {
140142
if (action_type == ActionType::kSend) {
141143
std::vector<uint8_t> data;
142-
ASSERT_TRUE(comms.RecvBytes(&data));
143-
SAPI_ASSERT_OK(transport_client->Send(data));
144+
CHECK_EQ(comms.RecvBytes(&data), true);
145+
CHECK_OK(transport_client->Send(data));
144146
} else if (action_type == ActionType::kExchange) {
145147
std::vector<uint8_t> data_to_send;
146-
ASSERT_TRUE(comms.RecvBytes(&data_to_send));
148+
CHECK_EQ(comms.RecvBytes(&data_to_send), true);
147149
std::vector<uint8_t> data_to_recv;
148-
ASSERT_TRUE(comms.RecvBytes(&data_to_recv));
150+
CHECK_EQ(comms.RecvBytes(&data_to_recv), true);
149151
std::vector<uint8_t> recv_data(data_to_recv.size());
150-
SAPI_ASSERT_OK(transport_client->Exchange(
152+
CHECK_OK(transport_client->Exchange(
151153
data_to_send,
152154
absl::Span<uint8_t>(recv_data.data(), recv_data.size())));
153155
ASSERT_EQ(recv_data, data_to_recv);
154156
} else if (action_type == ActionType::kRecv) {
155157
std::vector<uint8_t> data;
156-
ASSERT_TRUE(comms.RecvBytes(&data));
158+
CHECK_EQ(comms.RecvBytes(&data), true);
157159
std::vector<uint8_t> data_recv(data.size());
158-
SAPI_ASSERT_OK(transport_client->Recv(
160+
CHECK_OK(transport_client->Recv(
159161
absl::Span<uint8_t>(data_recv.data(), data_recv.size())));
160-
ASSERT_EQ(data, data_recv);
162+
CHECK_EQ(data, data_recv);
161163
} else if (action_type == ActionType::kTerminate) {
162164
transport_client->Terminate();
163165
}
@@ -185,6 +187,7 @@ class AsynchronousByteTransportTest
185187
void SetUp() override {
186188
SAPI_ASSERT_OK_AND_ASSIGN(auto buffer,
187189
sandbox2::Buffer::CreateWithSize(buffer_size_));
190+
memset(buffer->data(), 0x41, 4 << 10);
188191
int memfd = buffer->fd();
189192
size_t size = buffer->size();
190193
sandbox2::AsynchronousByteTransport::SynchronizationType
@@ -212,7 +215,7 @@ class AsynchronousByteTransportTest
212215
TestHelper test_helper_;
213216

214217
private:
215-
size_t buffer_size_ = 128 << 10;
218+
size_t buffer_size_ = 132 << 10;
216219
std::unique_ptr<ScopedTimeout> timeout_;
217220
std::unique_ptr<sandbox2::AsynchronousByteTransport> transport_;
218221
};
@@ -402,6 +405,17 @@ TEST_P(AsynchronousByteTransportTest, TwoReadsAfterConnectionClosedFails) {
402405
StatusIs(absl::StatusCode::kAborted));
403406
}
404407

408+
TEST_P(AsynchronousByteTransportTest,
409+
ReadMoreThanBufferWillNotTriggerOutOfBounds) {
410+
std::vector<uint8_t> data(10, 'a');
411+
SAPI_ASSERT_OK(GetTransport()->Send(
412+
absl::Span<const uint8_t>(data.data(), data.size() / 2)));
413+
test_helper_.RequestRecv(data);
414+
absl::SleepFor(absl::Milliseconds(10));
415+
SAPI_ASSERT_OK(GetTransport()->Send(absl::Span<const uint8_t>(
416+
data.data() + data.size() / 2, data.size() / 2)));
417+
}
418+
405419
INSTANTIATE_TEST_SUITE_P(
406420
AsynchronousByteTransportTest, AsynchronousByteTransportTest,
407421
::testing::Values(

0 commit comments

Comments
 (0)