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++
// 시나리오: 가변 섹션 "오프셋 테이블" 패킷 역직렬화 — C++ 서버
// - 헤더에 각 섹션의 (offset, length) 디렉터리를 두고 본문을 임의 접근으로 읽는다.
// - 수신 버퍼는 세션마다 재사용되는 버퍼(uint8_t*)이며, 이번 패킷 유효 길이(payload_len)는
// 프레이밍으로 별도 전달된다(버퍼 용량 >= payload_len). offset/length 는 클라가 채운다.
//
// 요구사항:
// 1) 각 섹션은 payload 범위 안에서만 읽어야 한다(offset >= header_end, offset+length <= payload_len).
// 2) 조작된 offset/length(범위 밖, 과대 length)는 거부해야 한다.
//
// 과제: 조작된 디렉터리로 payload 밖을 읽거나 엉뚱한 데이터를 읽어들이는 경우가 있다.
// (A),(B) 지점을 근거로 원인·수정안을 설명하라. (컴파일 가능해야 함)
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <vector>
struct SectionDir { uint32_t offset; uint32_t length; };
class SectionReader {
public:
SectionReader(const uint8_t* buf, size_t payload_len, size_t header_end)
: buf_(buf), payload_len_(payload_len), header_end_(header_end) {}
std::vector<uint8_t> ReadSection(const SectionDir& dir) {
std::vector<uint8_t> section(dir.length); // (A)
std::memcpy(section.data(), buf_ + dir.offset, dir.length); // (B)
return section;
}
private:
const uint8_t* buf_;
size_t payload_len_;
size_t header_end_;
};
void ParseProfile(const uint8_t* pooled, size_t payload_len, size_t header_end,
const std::vector<SectionDir>& dirs) {
SectionReader r(pooled, payload_len, header_end);
for (const auto& d : dirs) {
auto bytes = r.ReadSection(d);
std::printf("section off=%u len=%u -> %zu bytes\n", d.offset, d.length, bytes.size());
}
}
int main() {
uint8_t buf[16] = {0};
std::vector<SectionDir> dirs = { {4, 4}, {8, 4} }; // header_end=4, payload_len=12
ParseProfile(buf, 12, 4, dirs);
return 0;
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.