13. 가변길이(VarInt) 정수 디코딩과 악성 길이 방어 — C#
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 가변길이(VarInt) 정수 디코딩과 악성 길이 방어
// ----------------------------------------------------------------------------
// 시나리오:
// - 클라이언트-서버 패킷의 여러 필드(아이템 개수, 문자열 길이 등)를 공간 절약을
// 위해 VarInt(LEB128 유사) 로 인코딩한다.
// · 각 바이트의 하위 7비트가 값, 최상위 비트(0x80)가 "다음 바이트 계속" 표시.
// - 서버는 수신 버퍼(고정 크기, 신뢰 불가)에서 VarInt 를 읽어 개수/길이를 얻고,
// 그 개수만큼 아이템을 파싱한다.
// - 입력은 전적으로 신뢰할 수 없다(치터/퍼저/변조 패킷이 임의 바이트열을 보낸다).
//
// 요구사항:
// - 손상/악의적 입력에도 서버가 크래시·무한루프·과대 할당(메모리 고갈) 없이
// 안전하게 거부해야 한다.
// - 디코딩된 개수/길이는 남은 버퍼로 실제 수용 가능한지 검증해야 한다.
// - VarInt 는 64비트 범위를 넘기지 않아야 하고, 정수 오버플로가 없어야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 어떤 입력이 무엇을 깨뜨리는지(예외/무한루프/DoS)
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Text;
public struct Reader
{
public byte[] Buf;
public int Pos;
}
public class Item
{
public uint Id;
public string Name;
}
public static class VarIntParser
{
// 하나의 VarInt 를 읽어 값을 반환한다.
public static ulong ReadVarInt(ref Reader r)
{
ulong result = 0;
int shift = 0;
while (true)
{
// (A) 다음 바이트를 읽는다
byte b = r.Buf[r.Pos++];
// (B) 7비트씩 누적
result |= (ulong)(b & 0x7F) << shift;
if ((b & 0x80) == 0)
break; // 계속 비트가 꺼지면 끝
shift += 7;
}
return result;
}
// 길이프리픽스로 추려진 한 메시지 본문(buf)에서 아이템 목록을 파싱한다.
// 형식: [VarInt count][ item: VarInt id, VarInt nameLen, nameLen bytes ] * count
public static bool ParseItems(byte[] buf, out List<Item> outItems)
{
var r = new Reader { Buf = buf, Pos = 0 };
outItems = new List<Item>();
ulong count = ReadVarInt(ref r);
// (C) count 개수만큼 미리 공간 확보
outItems.Capacity = (int)count;
for (ulong i = 0; i < count; ++i)
{
var it = new Item();
it.Id = (uint)ReadVarInt(ref r);
ulong nameLen = ReadVarInt(ref r);
// (D) 이름 바이트를 그대로 복사
it.Name = Encoding.UTF8.GetString(r.Buf, r.Pos, (int)nameLen);
r.Pos += (int)nameLen;
outItems.Add(it);
}
return true;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 가변길이(VarInt) 정수 디코딩과 악성 길이 방어
// ----------------------------------------------------------------------------
// 시나리오:
// - 클라이언트-서버 패킷의 여러 필드(아이템 개수, 문자열 길이 등)를 공간 절약을
// 위해 VarInt(LEB128 유사) 로 인코딩한다.
// · 각 바이트의 하위 7비트가 값, 최상위 비트(0x80)가 "다음 바이트 계속" 표시.
// - 서버는 수신 버퍼(고정 크기, 신뢰 불가)에서 VarInt 를 읽어 개수/길이를 얻고,
// 그 개수만큼 아이템을 파싱한다.
// - 입력은 전적으로 신뢰할 수 없다(치터/퍼저/변조 패킷이 임의 바이트열을 보낸다).
//
// 요구사항:
// - 손상/악의적 입력에도 서버가 크래시(OOB read)·무한루프·과대 할당(메모리 고갈)
// 없이 안전하게 거부해야 한다.
// - 디코딩된 개수/길이는 남은 버퍼로 실제 수용 가능한지 검증해야 한다.
// - VarInt 는 64비트 범위를 넘기지 않아야 하고, 정수 오버플로가 없어야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 어떤 입력이 무엇을 깨뜨리는지(OOB/UB/DoS)
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <cstddef>
#include <vector>
#include <string>
struct Reader {
const uint8_t* buf;
size_t len;
size_t pos = 0;
};
struct Item {
uint32_t id;
std::string name;
};
// 하나의 VarInt 를 읽어 값을 반환한다.
static uint64_t ReadVarInt(Reader& r)
{
uint64_t result = 0;
int shift = 0;
while (true) {
// (A) 다음 바이트를 읽는다
uint8_t b = r.buf[r.pos++];
// (B) 7비트씩 누적
result |= (uint64_t)(b & 0x7F) << shift;
if ((b & 0x80) == 0)
break; // 계속 비트가 꺼지면 끝
shift += 7;
}
return result;
}
// 길이프리픽스로 추려진 한 메시지 본문(buf,len)에서 아이템 목록을 파싱한다.
// 형식: [VarInt count][ item: VarInt id, VarInt nameLen, nameLen bytes ] * count
bool ParseItems(const uint8_t* buf, size_t len, std::vector<Item>& out)
{
Reader r{buf, len, 0};
uint64_t count = ReadVarInt(r);
// (C) count 개수만큼 미리 공간 확보
out.reserve(count);
for (uint64_t i = 0; i < count; ++i) {
Item it;
it.id = (uint32_t)ReadVarInt(r);
uint64_t nameLen = ReadVarInt(r);
// (D) 이름 바이트를 그대로 복사
it.name.assign((const char*)(r.buf + r.pos), (size_t)nameLen);
r.pos += nameLen;
out.push_back(std::move(it));
}
return true;
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.