47. 섹션 오프셋 테이블의 offset/length 무검증 (임의 접근 경계 밖 읽기)

난이도 중 해설 보기 →

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

결함 코드 · C#
// 시나리오: 가변 섹션 "오프셋 테이블" 패킷 역직렬화 — C# 서버
// - 한 패킷에 여러 가변 길이 섹션(인벤토리/스킬/버프 등)을 담기 위해, 헤더에 각 섹션의
//   (offset, length) 디렉터리를 두고 본문에서 해당 위치를 임의 접근(random access)으로 읽는다.
// - 수신 버퍼는 세션마다 재사용되는 풀 버퍼(byte[])이며, 이번 패킷의 유효 길이
//   (payloadLen)는 프레이밍 헤더로 별도 전달된다. offset/length 값은 클라이언트가 채운다.
//
// 요구사항:
//   1) 각 섹션은 payload 범위 안에서만 읽어야 한다(offset 은 본문 안, offset+length <= payloadLen).
//   2) 조작된 offset/length(음수, 범위 밖, 과대 length)는 거부해야 한다.
//
// 과제: 조작된 디렉터리로 payload 밖 영역을 읽거나 엉뚱한 데이터를 읽어들이는 경우가 있다.
//       (A),(B) 지점을 근거로 원인과 수정안을 설명하라.

using System;

public struct SectionDir { public int Offset; public int Length; }

public sealed class SectionReader
{
    private readonly byte[] _buf;      // 재사용 풀 버퍼(용량 >= payloadLen)
    private readonly int _payloadLen;  // 이번 패킷의 유효 길이
    private readonly int _headerEnd;   // 디렉터리(헤더) 끝 = 본문 시작 오프셋

    public SectionReader(byte[] buf, int payloadLen, int headerEnd)
    {
        _buf = buf; _payloadLen = payloadLen; _headerEnd = headerEnd;
    }

    public byte[] ReadSection(SectionDir dir)
    {
        var section = new byte[dir.Length];                        // (A)
        Array.Copy(_buf, dir.Offset, section, 0, dir.Length);      // (B)
        return section;
    }
}

public static class ProfileCodec
{
    public static void Parse(byte[] pooledBuf, int payloadLen, int headerEnd, SectionDir[] dirs)
    {
        var r = new SectionReader(pooledBuf, payloadLen, headerEnd);
        foreach (var d in dirs)
        {
            var bytes = r.ReadSection(d);
            Console.WriteLine($"section off={d.Offset} len={d.Length} -> {bytes.Length} bytes");
        }
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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