38. 세션 간 공유 직렬화 버퍼 오염: 데이터 유출·프레임 붕괴 (C#)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: MMORPG 게이트웨이. 각 클라이언트 세션으로 메시지를 보낼 때, 매번 배열을
// 새로 할당하지 않으려고 직렬화용 스크래치 버퍼를 재사용한다. 직렬화한 뒤 그 버퍼를
// 소켓으로 비동기 전송한다. 여러 세션이 서로 다른 스레드에서 동시에 전송한다.
//
// 요구사항:
// - 각 세션은 자기 메시지 바이트만 자기 소켓으로 보내야 한다.
// - 한 세션의 데이터가 다른 세션의 프레임에 섞이거나 다른 소켓으로 나가면 안 된다.
// - 직렬화 결과는 전송이 끝날 때까지 온전히 유지되어야 한다.
//
// 과제: SendAsync 를 검토하고, 동시에 여러 세션이 전송할 때 무엇이 잘못될 수 있는지,
// 어떤 조건에서 드러나는지, 어떻게 고칠지 설명하라. (A)(B) 지점을 살펴보라.
using System.Net.Sockets;
using System.Threading.Tasks;
public interface IMessage { int Serialize(byte[] dest); }
public sealed class MessageSender
{
// 할당을 줄이려고 직렬화 스크래치 버퍼를 재사용한다.
private static readonly byte[] _scratch = new byte[4096]; // (A)
public async Task SendAsync(Socket socket, IMessage msg)
{
int len = msg.Serialize(_scratch); // 스크래치에 직렬화
await socket.SendAsync(
new ArraySegment<byte>(_scratch, 0, len), SocketFlags.None); // (B)
}
} 결함 코드 · C++
// 시나리오: MMORPG 게이트웨이. 각 클라이언트 세션으로 메시지를 보낼 때, 매번 버퍼를
// 새로 할당하지 않으려고 직렬화용 스크래치 버퍼를 재사용한다. 직렬화한 뒤 그 버퍼를
// 소켓으로 전송한다. 여러 I/O 스레드가 각자 담당 세션의 전송을 동시에 수행한다.
//
// 요구사항:
// - 각 세션은 자기 메시지 바이트만 자기 소켓으로 보내야 한다.
// - 한 세션의 데이터가 다른 세션의 프레임에 섞이거나 다른 소켓으로 나가면 안 된다.
// - 직렬화 결과는 전송이 끝날 때까지 온전히 유지되어야 한다.
//
// 과제: send 를 검토하고, 여러 세션이 동시에 전송할 때 무엇이 잘못될 수 있는지, 어떤
// 조건에서 드러나는지, 어떻게 고칠지 설명하라. (A)(B) 지점을 살펴보라.
#include <cstdint>
#include <cstddef>
struct Message { size_t serialize(uint8_t* dest, size_t cap) const; };
int sys_send(int fd, const uint8_t* p, size_t n); // 보낸 바이트 수 반환(부분 전송 가능)
struct MessageSender {
// 할당을 줄이려고 직렬화 스크래치 버퍼를 재사용한다.
static uint8_t scratch[4096]; // (A)
void send(int fd, const Message& msg) {
size_t len = msg.serialize(scratch, sizeof(scratch)); // 스크래치에 직렬화
sys_send(fd, scratch, len); // (B)
}
};
uint8_t MessageSender::scratch[4096]; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.