21. 패킹된 비트플래그/필드 언패킹
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 패킹된 비트플래그/필드 언패킹
// ----------------------------------------------------------------------------
// 시나리오 (프로토콜/패킷 · 서버-클라):
// - 액션 패킷의 헤더는 여러 필드를 하나의 16비트 정수에 비트로 패킹한다.
// 와이어 포맷(2바이트, 빅엔디안):
// bit 0..2 : actionType (0..7)
// bit 3..6 : slotIndex (0..15) ← 장비 슬롯 인덱스
// bit 7 : urgent (0/1)
// bit 8..15 : reserved (반드시 0)
// - 서버는 이 2바이트를 받아 각 필드를 언패킹하고, slotIndex 로 장비 배열에 접근한다.
//
// 요구사항:
// - 각 필드는 정의된 비트 폭/범위 안에서만 해석돼야 한다(slotIndex 는 0..15).
// - reserved 비트가 0이 아니면(=정의되지 않은 비트) 패킷을 거부해야 한다.
// - "서버가 신뢰한 요청"임을 나타내는 권한성 표식은 클라가 보낸 값으로 정해선 안 된다
// (서버 권위). 클라 패킷의 어떤 비트도 검증을 건너뛰게 만들 수 없어야 한다.
//
// 과제:
// 이 디코더/적용 코드의 잠재적 문제를 모두 찾고, 악의적/비정상 입력에서 어떤 일이
// 벌어지는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
public class ActionPacket
{
public int ActionType;
public int SlotIndex;
public bool Urgent;
public bool ServerTrusted; // 내부 신뢰 표식
}
public class ActionDecoder
{
private readonly string[] _equip = new string[16]; // 슬롯 0..15
public ActionPacket Decode(byte[] buf, int offset)
{
short packed = (short)((buf[offset] << 8) | buf[offset + 1]); // (A)
var p = new ActionPacket();
p.ActionType = packed & 0x7;
p.SlotIndex = (packed >> 3) & 0x1F; // (B)
p.Urgent = ((packed >> 7) & 1) != 0;
p.ServerTrusted = ((packed >> 8) & 1) != 0; // (C)
return p;
}
public void Apply(ActionPacket p)
{
if (!p.ServerTrusted)
{
if (p.SlotIndex < 0 || p.SlotIndex >= _equip.Length) return;
}
var name = _equip[p.SlotIndex]; // (D)
// ... actionType 에 따라 name 슬롯에 액션 적용
_ = name;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 패킹된 비트플래그/필드 언패킹
// ----------------------------------------------------------------------------
// 시나리오 (프로토콜/패킷 · 서버-클라):
// - 액션 패킷의 헤더는 여러 필드를 하나의 16비트 정수에 비트로 패킹한다.
// 와이어 포맷(2바이트, 빅엔디안):
// bit 0..2 : actionType (0..7)
// bit 3..6 : slotIndex (0..15) ← 장비 슬롯 인덱스
// bit 7 : urgent (0/1)
// bit 8..15 : reserved (반드시 0)
// - 서버는 수신 버퍼(const char*)에서 2바이트를 읽어 각 필드를 언패킹하고,
// slotIndex 로 장비 배열에 접근한다.
//
// 요구사항:
// - 각 필드는 정의된 비트 폭/범위 안에서만 해석돼야 한다(slotIndex 는 0..15).
// - reserved 비트가 0이 아니면 패킷을 거부해야 한다.
// - "서버가 신뢰한 요청" 표식은 클라 패킷의 비트로 정해선 안 된다(서버 권위).
//
// 과제:
// 이 디코더/적용 코드의 잠재적 문제를 모두 찾고(특히 입력 바이트의 부호/시프트 동작),
// 악의적/비정상 입력에서 무엇이 벌어지는지 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <string>
struct ActionPacket
{
int actionType;
int slotIndex;
bool urgent;
bool serverTrusted;
};
class ActionDecoder
{
std::string equip_[16]; // 슬롯 0..15
public:
ActionPacket Decode(const char* buf, std::size_t offset)
{
int16_t packed = (int16_t)((buf[offset] << 8) | buf[offset + 1]); // (A)
ActionPacket p{};
p.actionType = packed & 0x7;
p.slotIndex = (packed >> 3) & 0x1F; // (B)
p.urgent = ((packed >> 7) & 1) != 0;
p.serverTrusted = ((packed >> 8) & 1) != 0; // (C)
return p;
}
void Apply(const ActionPacket& p)
{
if (!p.serverTrusted)
{
if (p.slotIndex < 0 || p.slotIndex >= 16) return;
}
const std::string& name = equip_[p.slotIndex]; // (D)
(void)name;
}
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.