5. 샌드박스 사용자와 커뮤니케이션

기본적으로 실행자는 파일 설명자를 통해 샌드박스와 통신할 수 있습니다. 예를 들어 Sandboxee와 파일을 공유하거나 Sandboxee의 표준 출력을 읽으려는 경우 이 작업만 필요할 수도 있습니다.

그러나 실행자와 Sandboxee 간에 더 복잡한 통신 로직이 필요할 수 있습니다. 커뮤니케이션 API (comms.h 헤더 파일 참고)는 정수, 문자열, 바이트 버퍼, protobuf, 파일 설명자를 전송하는 데 사용할 수 있습니다.

파일 설명자 공유

Inter-Process Communication API (ipc.h 참고)를 사용하면 MapFd() 또는 ReceiveFd()를 사용할 수 있습니다.

  • MapFd()를 사용하여 실행자의 파일 설명자를 Sandboxee로 매핑합니다. Sandboxee에서 사용하기 위해 실행자에서 연 파일을 공유하는 데 사용할 수 있습니다. 사용 예는 정적에서 볼 수 있습니다.

    // 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);
    

커뮤니케이션 API 사용

Sandbox2는 편리한 커뮤니케이션 API를 제공합니다. 이는 실행자와 Sandboxee 간에 정수, 문자열 또는 바이트 버퍼를 공유할 수 있는 간단하고 쉬운 방법입니다. 다음은 crc4 예에서 확인할 수 있는 코드 스니펫입니다.

커뮤니케이션 API를 시작하려면 먼저 Sandbox2 객체에서 커뮤니케이션 객체를 가져와야 합니다.

sandbox2::Comms* comms = s2.comms();

커뮤니케이션 객체를 사용할 수 있게 되면 Send* 함수 제품군 중 하나를 사용하여 Sandboxee에 데이터를 전송할 수 있습니다. 커뮤니케이션 API의 사용 예는 crc4 예에서 확인할 수 있습니다. 아래의 코드 스니펫은 이 예의 일부를 발췌한 것입니다. 실행자는 SendBytes(buf, size)와 함께 unsigned char buf[size]를 전송합니다.

if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
  /* handle error */
}

Sandboxee에서 데이터를 수신하려면 Recv* 함수 중 하나를 사용합니다. 아래의 코드 스니펫은 crc4 예에서 발췌한 것입니다. 실행자는 부호 없는 32비트 정수로 체크섬을 수신합니다(uint32_t crc4).

if (!(comms->RecvUint32(&crc4))) {
  /* handle error */
}

버퍼와 데이터 공유

또 다른 데이터 공유 기능은 버퍼 API를 사용하여 많은 양의 데이터를 공유하고 실행자와 Sandboxee 간에 전송되는 비용이 많이 드는 복사본을 피하는 것입니다.

실행자는 전달할 크기와 데이터를 기준으로 또는 파일 설명자에서 직접 Buffer를 만들어 실행자의 comms->SendFD()와 Sandboxee의 comms->RecvFD()를 사용해 Buffer를 샌드박스에 전달합니다.

아래 코드 스니펫에서는 실행자 측면을 확인할 수 있습니다. 샌드박스는 비동기식으로 실행되며 버퍼를 통해 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());

Sandboxee 측면에서도 버퍼 객체를 만들고 실행자가 전송한 파일 설명자에서 데이터를 읽어야 합니다.

// 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 */