Skip to content

Commit e7ce2a4

Browse files
committed
Fixed #46: Integer overflow when reading RLE compressed TGA files
Also switched buffers to dynamic byte arrays and fixed some comments.
1 parent b9c7715 commit e7ce2a4

1 file changed

Lines changed: 46 additions & 40 deletions

File tree

Source/ImagingTarga.pas

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ implementation
5454
STargaSignature = 'TRUEVISION-XFILE';
5555

5656
type
57-
{ Targa file header.}
57+
{ Targa file header }
5858
TTargaHeader = packed record
5959
IDLength: Byte;
6060
ColorMapType: Byte;
@@ -70,7 +70,7 @@ implementation
7070
Desc: Byte;
7171
end;
7272

73-
{ Footer at the end of TGA file.}
73+
{ Footer at the end of TGA file }
7474
TTargaFooter = packed record
7575
ExtOff: UInt32; // Extension Area Offset
7676
DevDirOff: UInt32; // Developer Directory Offset
@@ -108,34 +108,40 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
108108

109109
procedure LoadRLE;
110110
var
111-
I, CPixel, Cnt: LongInt;
111+
I: Integer;
112+
CurrentPixel, CountPixels: NativeInt;
112113
Bpp, Rle: Byte;
113-
Buffer, Dest, Src: PByte;
114-
BufSize: LongInt;
114+
Dest, Src: PByte;
115+
Buffer: TDynByteArray;
116+
BufSize: NativeInt;
117+
BytesConsumedFromBuffer, SeekOffset: NativeInt;
115118
begin
116119
with GetIO, Images[0] do
117120
begin
118121
// Allocates buffer large enough to hold the worst case
119122
// RLE compressed data and reads then from input
120123
BufSize := Width * Height * FmtInfo.BytesPerPixel;
121124
BufSize := BufSize + BufSize div 2 + 1;
122-
GetMem(Buffer, BufSize);
123-
Src := Buffer;
124-
Dest := Bits;
125+
126+
SetLength(Buffer, BufSize);
125127
BufSize := Read(Handle, Buffer, BufSize);
126128

127-
Cnt := Width * Height;
129+
Src := @Buffer[0];
130+
Dest := Bits;
131+
132+
CountPixels := Width * Height;
128133
Bpp := FmtInfo.BytesPerPixel;
129-
CPixel := 0;
130-
while CPixel < Cnt do
134+
CurrentPixel := 0;
135+
136+
while CurrentPixel < CountPixels do
131137
begin
132138
Rle := Src^;
133139
Inc(Src);
134140
if Rle < 128 then
135141
begin
136142
// Process uncompressed pixel
137143
Rle := Rle + 1;
138-
CPixel := CPixel + Rle;
144+
CurrentPixel := CurrentPixel + Rle;
139145
for I := 0 to Rle - 1 do
140146
begin
141147
// Copy pixel from src to dest
@@ -153,7 +159,7 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
153159
begin
154160
// Process compressed pixels
155161
Rle := Rle - 127;
156-
CPixel := CPixel + Rle;
162+
CurrentPixel := CurrentPixel + Rle;
157163
// Copy one pixel from src to dest (many times there)
158164
for I := 0 to Rle - 1 do
159165
begin
@@ -168,10 +174,13 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
168174
Inc(Src, Bpp);
169175
end;
170176
end;
171-
// set position in source to real end of compressed data
172-
Seek(Handle, -(BufSize - (PtrUInt(Src) - PtrUInt(Buffer))),
173-
smFromCurrent);
174-
FreeMem(Buffer);
177+
178+
// Set position in source to the real end of compressed data
179+
BytesConsumedFromBuffer := PtrUInt(Src) - PtrUInt(Buffer);
180+
SeekOffset := BytesConsumedFromBuffer - BufSize;
181+
Assert(SeekOffset <= 0);
182+
183+
Seek(Handle, SeekOffset, smFromCurrent);
175184
end;
176185
end;
177186

@@ -196,7 +205,7 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
196205
3, 11: Format := ifGray8;
197206
end;
198207
// Format was not assigned by previous testing (it should be in
199-
// well formed targas), so formats which reflects bit dept are selected
208+
// well formed Targas), so format which reflect the bit depth is selected
200209
if Format = ifUnknown then
201210
case Hdr.PixelSize of
202211
8: Format := ifGray8;
@@ -268,8 +277,8 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
268277
Format := ifX1R5G5B5;
269278
end;
270279

271-
// We must find true end of file and set input' position to it
272-
// paint programs appends extra info at the end of Targas
280+
// We must find true end of file and set input's position to it.
281+
// Some paint programs appends extra info at the end of Targas,
273282
// some of them multiple times (PSP Pro 8)
274283
repeat
275284
ExtFound := False;
@@ -296,7 +305,7 @@ function TTargaFileFormat.LoadData(Handle: TImagingHandle;
296305
end;
297306
until (not ExtFound) and (not FooterFound);
298307

299-
// Some editors save targas flipped
308+
// Some editors save Targas flipped
300309
if Hdr.Desc < 31 then
301310
FlipImage(Images[0]);
302311

@@ -316,7 +325,7 @@ function TTargaFileFormat.SaveData(Handle: TImagingHandle;
316325

317326
procedure SaveRLE;
318327
var
319-
Dest: PByte;
328+
DestBuffer: TDynByteArray;
320329
WidthBytes, Written, I, Total, DestSize: LongInt;
321330

322331
function CountDiff(Data: PByte; Bpp, PixelCount: Longint): LongInt;
@@ -407,10 +416,12 @@ function TTargaFileFormat.SaveData(Handle: TImagingHandle;
407416
begin
408417
DiffCount := CountDiff(Data, Bpp, PixelCount);
409418
SameCount := CountSame(Data, Bpp, PixelCount);
419+
410420
if (DiffCount > MaxRun) then
411421
DiffCount := MaxRun;
412422
if (SameCount > MaxRun) then
413423
SameCount := MaxRun;
424+
414425
if (DiffCount > 0) then
415426
begin
416427
Dest^ := Byte(DiffCount - 1);
@@ -449,19 +460,16 @@ function TTargaFileFormat.SaveData(Handle: TImagingHandle;
449460
WidthBytes := Width * FmtInfo.BytesPerPixel;
450461
DestSize := WidthBytes * Height;
451462
DestSize := DestSize + DestSize div 2 + 1;
452-
GetMem(Dest, DestSize);
463+
SetLength(DestBuffer, DestSize);
453464
Total := 0;
454-
try
455-
for I := 0 to Height - 1 do
456-
begin
457-
RleCompressLine(@PByteArray(Bits)[I * WidthBytes], Width,
458-
FmtInfo.BytesPerPixel, @PByteArray(Dest)[Total], Written);
459-
Total := Total + Written;
460-
end;
461-
GetIO.Write(Handle, Dest, Total);
462-
finally
463-
FreeMem(Dest);
465+
466+
for I := 0 to Height - 1 do
467+
begin
468+
RleCompressLine(@PByteArray(Bits)[I * WidthBytes], Width,
469+
FmtInfo.BytesPerPixel, @DestBuffer[Total], Written);
470+
Total := Total + Written;
464471
end;
472+
GetIO.Write(Handle, DestBuffer, Total);
465473
end;
466474
end;
467475

@@ -493,11 +501,10 @@ function TTargaFileFormat.SaveData(Handle: TImagingHandle;
493501
// Choose image type
494502
if FmtInfo.IsIndexed then
495503
Hdr.ImageType := Iff(FUseRLE, 9, 1)
504+
else if FmtInfo.HasGrayChannel then
505+
Hdr.ImageType := Iff(FUseRLE, 11, 3)
496506
else
497-
if FmtInfo.HasGrayChannel then
498-
Hdr.ImageType := Iff(FUseRLE, 11, 3)
499-
else
500-
Hdr.ImageType := Iff(FUseRLE, 10, 2);
507+
Hdr.ImageType := Iff(FUseRLE, 10, 2);
501508

502509
Write(Handle, @Hdr, SizeOf(Hdr));
503510

@@ -520,7 +527,7 @@ function TTargaFileFormat.SaveData(Handle: TImagingHandle;
520527
end;
521528

522529
if FUseRLE then
523-
// Save rle compressed mode images
530+
// Save RLE compressed mode images
524531
SaveRLE
525532
else
526533
// Save uncompressed mode images
@@ -580,8 +587,7 @@ initialization
580587
{
581588
File Notes:
582589
583-
-- TODOS ----------------------------------------------------
584-
- nothing now
590+
* More recent changes are in VCS history *
585591
586592
-- 0.21 Changes/Bug Fixes -----------------------------------
587593
- MakeCompatible method moved to base class, put ConvertToSupported here.

0 commit comments

Comments
 (0)