5. 샌드박스 처리 대상과 통신
기본적으로 실행기는 파일 설명자를 통해 샌드박스와 통신할 수 있습니다. 예를 들어 샌드박스 사용자와 파일을 공유하거나 샌드박스 사용자의 표준 출력을 읽으려는 경우에만 필요할 수 있습니다.
하지만 실행자와 샌드박스 간에 더 복잡한 통신 로직이 필요할 가능성이 높습니다. 통신 API (comms.h 헤더 파일 참고)를 사용하여 정수, 문자열, 바이트 버퍼, protobuf 또는 파일 설명자를 전송할 수 있습니다.
파일 설명자 공유
프로세스 간 통신 API (ipc.h 참고)를 사용하여 MapFd()
또는 ReceiveFd()
을 사용할 수 있습니다.
MapFd()
를 사용하여 실행기에서 Sandboxee로 파일 설명자를 매핑합니다. 이는 샌드박스에서 사용할 실행기에서 열린 파일을 공유하는 데 사용할 수 있습니다. 사용 예는 static에서 확인할 수 있습니다.// The executor opened /proc/version and passes it to the sandboxee as stdin executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
ReceiveFd()
을 사용하여 소켓 쌍 엔드포인트를 만듭니다. 이는 샌드박스 처리된 프로그램의 표준 출력 또는 표준 오류를 읽는 데 사용할 수 있습니다. 사용 예는 도구에서 확인할 수 있습니다.// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
comms API 사용
Sandbox2는 편리한 comms API를 제공합니다. 이는 실행자와 Sandboxee 간에 정수, 문자열 또는 바이트 버퍼를 공유하는 간단하고 쉬운 방법입니다. 다음은 crc4 예에서 확인할 수 있는 코드 스니펫입니다.
커뮤니케이션 API를 시작하려면 먼저 Sandbox2 객체에서 커뮤니케이션 객체를 가져와야 합니다.
sandbox2::Comms* comms = s2.comms();
통신 객체를 사용할 수 있게 되면 Send* 함수 패밀리 중 하나를 사용하여 데이터를 샌드박스에 전송할 수 있습니다. crc4 예시에서 comms API의 사용 예를 확인할 수 있습니다. 아래 코드 스니펫은 해당 예의 발췌를 보여줍니다. 실행기는 SendBytes(buf, size)
이 포함된 unsigned char
buf[size]
를 전송합니다.
if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
/* handle error */
}
샌드박스에서 데이터를 수신하려면 Recv*
함수 중 하나를 사용하세요. 아래 코드 스니펫은 crc4 예에서 발췌한 것입니다. 실행기는 32비트 부호 없는 정수 uint32_t crc4로 체크섬을 수신합니다.
if (!(comms->RecvUint32(&crc4))) {
/* handle error */
}
버퍼와 데이터 공유
또 다른 데이터 공유 기능은 버퍼 API를 사용하여 대량의 데이터를 공유하고 실행기와 샌드박스 간에 앞뒤로 전송되는 비용이 많이 드는 복사를 방지하는 것입니다.
실행기는 전달할 크기와 데이터로 또는 파일 설명자에서 직접 버퍼를 만들고 실행기의 comms->SendFD()
와 Sandboxee의 comms->RecvFD()
를 사용하여 Sandboxee에 전달합니다.
아래 코드 스니펫에서 실행기 측을 확인할 수 있습니다. 샌드박스는 비동기적으로 실행되며 버퍼를 통해 Sandboxee와 데이터를 공유합니다.
// start the sandbox asynchronously
s2.RunAsync();
// instantiate the comms object
sandbox2::Comms* comms = s2.comms();
// random buffer data we want to send
constexpr unsigned char buffer_data[] = /* random data */;
constexpr unsigned int buffer_dataLen = 34;
// create sandbox2 buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();
// point to the sandbox2 buffer and fill with data
uint8_t* buf = buffer_ptr‑>data();
memcpy(buf, buffer_data, buffer_data_len);
// send the data to the sandboxee
comms‑>SendFd(buffer_ptr‑>fd());
샌드박스 측에서는 버퍼 객체를 만들고 실행기에서 전송한 파일 설명자에서 데이터를 읽어야 합니다.
// establish the communication with the executor
int fd;
comms.RecvFD(&fd);
// create the buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
sandbox2::Buffer::createFromFd(fd);
// get the data
auto buffer_ptr = std::move(buffer).value();
uint8_t* buf = buffer_ptr‑>data();
/* work with the buf object */