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#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.