Skip to content

Commit e032d5e

Browse files
committed
fix: ChatSegment GetAudioSpan 메모리 경계 검사 보안 강화
- AudioMemoryOwner.Memory.Span.Slice에서 AudioDataSize가 실제 메모리 크기 초과 시 예외 방지 - Math.Min을 활용한 안전한 크기 검증으로 IndexOutOfRangeException 방지 - 메모리 풀링 안전성 테스트 케이스 추가 및 검증 완료 - MemoryPool.Rent()의 실제 할당 크기 고려한 테스트 로직 개선 CodeRabbit 보안 검토 결과 반영으로 메모리 접근 안전성 확보
1 parent e92f564 commit e032d5e

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

ProjectVG.Application/Models/Chat/ChatSegment.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public ReadOnlySpan<byte> GetAudioSpan()
3737
{
3838
if (AudioMemoryOwner != null && AudioDataSize > 0)
3939
{
40-
return AudioMemoryOwner.Memory.Span.Slice(0, AudioDataSize);
40+
var memory = AudioMemoryOwner.Memory;
41+
var safeSize = Math.Min(AudioDataSize, memory.Length);
42+
return memory.Span.Slice(0, safeSize);
4143
}
4244
if (AudioData != null)
4345
{

ProjectVG.Tests/Infrastructure/Integrations/MemoryPoolingPerformanceTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,46 @@ public void ChatSegment_MemoryOwner_vs_ByteArray_Test()
8585
_output.WriteLine($"최적화 방식 - HasAudio: {segment2.HasAudio}, 데이터 크기: {segment2.AudioDataSize}");
8686
}
8787

88+
[Fact]
89+
public void ChatSegment_GetAudioSpan_SafetyBoundaryTest()
90+
{
91+
// 경계 조건 테스트: AudioDataSize가 실제 메모리보다 큰 경우
92+
var testData = GenerateTestAudioData(1000);
93+
using var memoryOwner = MemoryPool<byte>.Shared.Rent(500); // 더 작은 메모리 할당
94+
var actualMemorySize = memoryOwner.Memory.Length; // 실제 할당된 메모리 크기
95+
var copySize = Math.Min(500, actualMemorySize);
96+
testData.AsSpan(0, copySize).CopyTo(memoryOwner.Memory.Span);
97+
98+
// AudioDataSize를 실제 메모리보다 크게 설정 (위험한 상황 시뮬레이션)
99+
var oversizedRequest = actualMemorySize + 100;
100+
var segment = ChatSegment.CreateText("Test content")
101+
.WithAudioMemory(memoryOwner, oversizedRequest, "audio/wav", 5.0f); // oversizedRequest > actualMemorySize
102+
103+
// GetAudioSpan이 예외 없이 안전하게 처리되어야 함
104+
var span = segment.GetAudioSpan();
105+
106+
// 실제 메모리 크기만큼만 반환되어야 함 (Math.Min 적용됨)
107+
Assert.Equal(actualMemorySize, span.Length);
108+
_output.WriteLine($"요청 크기: {oversizedRequest}, 실제 메모리: {actualMemorySize}, 반환된 span 크기: {span.Length}");
109+
}
110+
111+
[Fact]
112+
public void ChatSegment_GetAudioSpan_EmptyAndNullSafetyTest()
113+
{
114+
// null AudioMemoryOwner 테스트
115+
var segment1 = ChatSegment.CreateText("Test").WithAudioMemory(null!, 100, "audio/wav", 1.0f);
116+
var span1 = segment1.GetAudioSpan();
117+
Assert.True(span1.IsEmpty);
118+
119+
// AudioDataSize가 0인 경우
120+
using var memoryOwner = MemoryPool<byte>.Shared.Rent(100);
121+
var segment2 = ChatSegment.CreateText("Test").WithAudioMemory(memoryOwner, 0, "audio/wav", 1.0f);
122+
var span2 = segment2.GetAudioSpan();
123+
Assert.True(span2.IsEmpty);
124+
125+
_output.WriteLine("빈 케이스들이 모두 안전하게 처리됨");
126+
}
127+
88128
private byte[] GenerateTestAudioData(int size)
89129
{
90130
var random = new Random(12345); // 고정 시드로 일관된 테스트

0 commit comments

Comments
 (0)