22. 메시지 봉투 타입 태그 신뢰: 핸들러 인덱스/길이 미검증과 버퍼 오버리드
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 메시지 봉투(envelope)의 타입 태그를 신뢰한 디스패치/역직렬화
// ----------------------------------------------------------------------------
// 시나리오 (프로토콜/직렬화 · 서버-클라/서버-서버):
// - 모든 패킷은 하나의 봉투로 들어온다: [type:u16][payloadLen:u16][payload...].
// type 은 메시지 종류(이동/거래/채팅 등), payloadLen 은 payload 바이트 수다.
// - 수신부는 type 으로 핸들러를 고르고, payload 를 그 종류에 맞는 필드로 읽어
// 처리한다. payload 의 바이트 배치는 종류마다 고정 오프셋이다.
// - 입력(buf)은 신뢰할 수 없다(클라/타 서버가 임의 바이트를 보낼 수 있다).
//
// 요구사항:
// - 알 수 없는/허용되지 않은 type 은 안전하게 거부해야 한다.
// - payload 길이가 종류가 기대하는 크기와 맞는지 확인하고, 버퍼 밖을 읽으면 안 된다.
// - 잘못된 패킷 하나가 서버를 죽이거나 인접 메모리를 오독/누출하면 안 된다.
//
// 과제:
// 이 디스패치/파싱의 잠재적 문제를 모두 찾고, 악의적 type/길이로 무엇이 가능한지
// (어떤 읽기가 버퍼 밖으로 나가는지, 어떤 분기로 빠지는지) 설명하고, 수정안과 더 나은
// 설계를 제시하라. (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
public enum MsgType : ushort { Move = 0, Trade = 1, Chat = 2 }
public class PacketRouter
{
// type 값을 인덱스로 그대로 쓰는 핸들러 테이블
private readonly Action<byte[], int, int>[] _handlers;
public PacketRouter()
{
_handlers = new Action<byte[], int, int>[]
{
HandleMove, // index 0
HandleTrade, // index 1
HandleChat, // index 2
};
}
// 봉투 한 개를 받아 처리
public void Dispatch(byte[] buf)
{
ushort type = BitConverter.ToUInt16(buf, 0);
ushort len = BitConverter.ToUInt16(buf, 2);
int p = 4; // payload 시작 오프셋
_handlers[type](buf, p, len); // (A) type 으로 핸들러 선택
}
private void HandleMove(byte[] buf, int p, int len)
{
float x = BitConverter.ToSingle(buf, p); // (B)
float y = BitConverter.ToSingle(buf, p + 4);
float z = BitConverter.ToSingle(buf, p + 8);
ApplyMove(x, y, z);
}
private void HandleTrade(byte[] buf, int p, int len)
{
long targetId = BitConverter.ToInt64(buf, p); // (C)
int gold = BitConverter.ToInt32(buf, p + 8);
ApplyTrade(targetId, gold);
}
private void HandleChat(byte[] buf, int p, int len)
{
ushort textLen = BitConverter.ToUInt16(buf, p); // (D)
string text = System.Text.Encoding.UTF8.GetString(buf, p + 2, textLen);
ApplyChat(text);
}
private void ApplyMove(float x, float y, float z) { /* ... */ }
private void ApplyTrade(long targetId, int gold) { /* ... */ }
private void ApplyChat(string text) { /* ... */ }
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 메시지 봉투(envelope)의 타입 태그를 신뢰한 디스패치/역직렬화
// ----------------------------------------------------------------------------
// 시나리오 (프로토콜/직렬화 · 서버-클라/서버-서버):
// - 모든 패킷은 하나의 봉투로 들어온다: [type:u16][payloadLen:u16][payload...].
// type 은 메시지 종류, payloadLen 은 payload 바이트 수다.
// - 수신부는 type 으로 핸들러를 고르고, payload 를 그 종류의 고정 레이아웃 구조체로
// 해석해 처리한다.
// - 입력(buf,size)은 신뢰할 수 없다(클라/타 서버가 임의 바이트를 보낼 수 있다).
//
// 요구사항:
// - 알 수 없는/허용되지 않은 type 은 안전하게 거부해야 한다.
// - payload 길이가 종류가 기대하는 크기와 맞는지 확인하고, 버퍼 밖을 읽으면 안 된다.
// - 잘못된 패킷 하나가 서버를 죽이거나 인접 메모리를 오독/누출하면 안 된다.
//
// 과제:
// 이 디스패치/파싱의 잠재적 문제를 모두 찾고, 악의적 type/길이로 무엇이 가능한지
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <cstddef>
enum class MsgType : uint16_t { Move = 0, Trade = 1, Chat = 2 };
#pragma pack(push, 1)
struct MoveMsg { float x, y, z; }; // 12 bytes
struct TradeMsg { int64_t targetId; int32_t gold; }; // 12 bytes
struct ChatHdr { uint16_t textLen; }; // 뒤에 textLen 바이트의 UTF-8
#pragma pack(pop)
class PacketRouter
{
using Handler = void (PacketRouter::*)(const uint8_t* p, uint16_t len);
Handler handlers_[3];
public:
PacketRouter()
{
handlers_[0] = &PacketRouter::HandleMove;
handlers_[1] = &PacketRouter::HandleTrade;
handlers_[2] = &PacketRouter::HandleChat;
}
// 봉투 한 개를 받아 처리
void Dispatch(const uint8_t* buf, size_t size)
{
uint16_t type = *reinterpret_cast<const uint16_t*>(buf); // (A)
uint16_t len = *reinterpret_cast<const uint16_t*>(buf + 2);
const uint8_t* p = buf + 4;
(this->*handlers_[type])(p, len); // (B) type 으로 핸들러 선택
}
void HandleMove(const uint8_t* p, uint16_t len)
{
const MoveMsg* m = reinterpret_cast<const MoveMsg*>(p); // (C)
ApplyMove(m->x, m->y, m->z);
}
void HandleTrade(const uint8_t* p, uint16_t len)
{
const TradeMsg* t = reinterpret_cast<const TradeMsg*>(p); // (D)
ApplyTrade(t->targetId, t->gold);
}
void HandleChat(const uint8_t* p, uint16_t len)
{
const ChatHdr* h = reinterpret_cast<const ChatHdr*>(p); // (E)
const char* text = reinterpret_cast<const char*>(p + 2);
ApplyChat(text, h->textLen);
}
private:
void ApplyMove(float, float, float) {}
void ApplyTrade(int64_t, int32_t) {}
void ApplyChat(const char*, uint16_t) {}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.