48. 프래그먼트 재조립의 offset/length 경계·겹침·구멍 미검증 · C#

난이도 중 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 시나리오: UDP 기반 신뢰성 전송의 대용량 메시지 프래그먼트 재조립 (C#)
//
// 큰 메시지(예: 인벤토리 스냅샷, 맵 데이터)는 여러 UDP 프래그먼트로 쪼개져 전송된다.
// 각 프래그먼트 헤더: msgId, totalSize(전체 바이트), offset(이 조각의 시작 위치),
//                     payload(이 조각의 데이터). moreFlag 대신 조각 payload 길이로 판단.
// 수신 측은 msgId 별로 재조립 버퍼를 만들고 offset 위치에 payload를 채워 넣는다.
// 모든 바이트가 채워지면(received == totalSize) 완성으로 보고 메시지를 디스패치한다.
//
// 클라이언트는 신뢰할 수 없다(변조된/악의적 프래그먼트가 올 수 있다).
//
// 과제:
//  1) (A)(B) 지점을 중심으로, 조작된 프래그먼트가 서버에 어떤 영향을 주는지 설명하라.
//  2) 구체적 공격/오작동 시나리오를 최소 두 가지 제시하라.
//  3) 견고한 재조립을 위한 수정안을 제시하라.

using System;
using System.Collections.Generic;

public struct Fragment
{
    public int MsgId;
    public int TotalSize;
    public int Offset;
    public byte[] Payload;
}

public class Reassembler
{
    private class Pending
    {
        public byte[] Buffer = Array.Empty<byte>();
        public int Received;
    }

    private readonly Dictionary<int, Pending> _pending = new Dictionary<int, Pending>();

    // 완성된 메시지를 반환하거나, 아직 미완성이면 null.
    public byte[] OnFragment(in Fragment f)
    {
        if (!_pending.TryGetValue(f.MsgId, out var p))
        {
            p = new Pending();
            // (A) 첫 조각이 알려준 totalSize 만큼 버퍼 확보
            p.Buffer = new byte[f.TotalSize];
            _pending[f.MsgId] = p;
        }

        // (B) 이 조각을 offset 위치에 채운다
        Array.Copy(f.Payload, 0, p.Buffer, f.Offset, f.Payload.Length);
        p.Received += f.Payload.Length;

        if (p.Received >= p.Buffer.Length)
        {
            _pending.Remove(f.MsgId);
            return p.Buffer;   // 완성
        }
        return null;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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