Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions XADMaster.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1415,13 +1415,14 @@
734C13B921C79C8500917EAA /* unpack_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 734C13B621C79C8400917EAA /* unpack_utils.c */; };
734C13BA21C79C8500917EAA /* unpack_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 734C13B621C79C8400917EAA /* unpack_utils.c */; };
7378D98B21CCD04000A4B11F /* CRCCalculationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7378D98A21CCD04000A4B11F /* CRCCalculationTests.m */; };
73B1C2012B4A100100ABCDEF /* XADRegexTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73B1C2002B4A100100ABCDEF /* XADRegexTests.m */; };
73F0AA6402D14A5300A1B2C3 /* EndianConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F0AA6302D14A5300A1B2C3 /* EndianConversionTests.m */; };
7398C7AE2059248700D7B977 /* UniversalDetector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B06D3770DDA5C2600D9C000 /* UniversalDetector.framework */; };
73A91A542ADC64DE0059C423 /* StuffitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73A91A4F2ADC64DE0059C423 /* StuffitTests.m */; };
73A91A562ADC64EB0059C423 /* StuffitFixtures in Resources */ = {isa = PBXBuildFile; fileRef = 73A91A552ADC64EB0059C423 /* StuffitFixtures */; };
73B1C2012B4A100100ABCDEF /* XADRegexTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73B1C2002B4A100100ABCDEF /* XADRegexTests.m */; };
73DA3468206B6BF1006ADB42 /* XADPlatformOSXTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73DA3467206B6BF1006ADB42 /* XADPlatformOSXTests.m */; };
73DA346A206B6BF1006ADB42 /* XADMaster.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* XADMaster.framework */; };
73F0AA6402D14A5300A1B2C3 /* EndianConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F0AA6302D14A5300A1B2C3 /* EndianConversionTests.m */; };
BA5204602FCDDC450041EA37 /* XADStuffItXIronHandleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA52045F2FCDDC450041EA37 /* XADStuffItXIronHandleTests.m */; };
C4C3241827AC25E9007919DB /* XADZippedBzip2LeakTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C3241727AC25E9007919DB /* XADZippedBzip2LeakTests.m */; };
C4C3241A27AC2655007919DB /* eicar.bz in Resources */ = {isa = PBXBuildFile; fileRef = C4C3241927AC2655007919DB /* eicar.bz */; };
DF05DCF92806F104001E9C52 /* XADMaster.h in Headers */ = {isa = PBXBuildFile; fileRef = DF05DCF52806EF40001E9C52 /* XADMaster.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -2146,14 +2147,15 @@
734C13B121C79B1A00917EAA /* unpack_seek.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack_seek.c; sourceTree = "<group>"; };
734C13B621C79C8400917EAA /* unpack_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack_utils.c; sourceTree = "<group>"; };
7378D98A21CCD04000A4B11F /* CRCCalculationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CRCCalculationTests.m; sourceTree = "<group>"; };
73B1C2002B4A100100ABCDEF /* XADRegexTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XADRegexTests.m; sourceTree = "<group>"; };
73F0AA6302D14A5300A1B2C3 /* EndianConversionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EndianConversionTests.m; sourceTree = "<group>"; };
73A91A4F2ADC64DE0059C423 /* StuffitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StuffitTests.m; sourceTree = "<group>"; };
73A91A552ADC64EB0059C423 /* StuffitFixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = StuffitFixtures; sourceTree = "<group>"; };
73B1C2002B4A100100ABCDEF /* XADRegexTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XADRegexTests.m; sourceTree = "<group>"; };
73DA3465206B6BF1006ADB42 /* XADMasterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XADMasterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
73DA3467206B6BF1006ADB42 /* XADPlatformOSXTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XADPlatformOSXTests.m; sourceTree = "<group>"; };
73DA3469206B6BF1006ADB42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
73F0AA6302D14A5300A1B2C3 /* EndianConversionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EndianConversionTests.m; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* XADMaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = XADMaster.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BA52045F2FCDDC450041EA37 /* XADStuffItXIronHandleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XADStuffItXIronHandleTests.m; sourceTree = "<group>"; };
C4C3241727AC25E9007919DB /* XADZippedBzip2LeakTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XADZippedBzip2LeakTests.m; sourceTree = "<group>"; };
C4C3241927AC2655007919DB /* eicar.bz */ = {isa = PBXFileReference; lastKnownFileType = file; path = eicar.bz; sourceTree = "<group>"; };
DF05DCF52806EF40001E9C52 /* XADMaster.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XADMaster.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3267,6 +3269,7 @@
children = (
73A91A552ADC64EB0059C423 /* StuffitFixtures */,
73A91A4F2ADC64DE0059C423 /* StuffitTests.m */,
BA52045F2FCDDC450041EA37 /* XADStuffItXIronHandleTests.m */,
);
path = Stuffit;
sourceTree = "<group>";
Expand Down Expand Up @@ -4782,6 +4785,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BA5204602FCDDC450041EA37 /* XADStuffItXIronHandleTests.m in Sources */,
7378D98B21CCD04000A4B11F /* CRCCalculationTests.m in Sources */,
73F0AA6402D14A5300A1B2C3 /* EndianConversionTests.m in Sources */,
73B1C2012B4A100100ABCDEF /* XADRegexTests.m in Sources */,
Expand Down
103 changes: 103 additions & 0 deletions XADMasterTests/Stuffit/XADStuffItXIronHandleTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* XADStuffItXIronHandleTests.m
*
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/

#import <XCTest/XCTest.h>
#import "../../CSMemoryHandle.h"
#import "../../XADException.h"
#import "../../XADStuffItXIronHandle.h"

@interface XADStuffItXIronHandleTests : XCTestCase
@end

@implementation XADStuffItXIronHandleTests

- (void)testOversizedBlockRaisesIllegalData
{
// Bit layout (LSB-first within each byte), encoding 2^31 via CSInputNextSitxP2:
// bit 0 = 0 stream bit — not end of stream
// bits 1-2 = 1,0 prefix: one leading 1-bit → n=2
// bit 3 = 1 value loop: value |= 1, n→1
// bits 4-33 = 0 30 zeros — bit counter advances to 2^31
// bit 34 = 1 value loop: value |= 2^31, n→0
// CSInputNextSitxP2 returns 2^31 = INT_MAX+1
uint8_t bytes[] = {0x0A, 0x00, 0x00, 0x00, 0x04};

NSException *caught =
[self caughtExceptionProducingBlockWithBytes:bytes
length:sizeof(bytes)];

XCTAssertNotNil(caught, @"Expected XADException to be thrown");
XCTAssertEqualObjects(caught.name, XADExceptionName);
XCTAssertEqual([caught.userInfo[@"XADError"] intValue], XADIllegalDataError);
}

// Demonstrates the crash path where blocksize*6 overflows unsigned int to a tiny
// allocation. blocksize=715827883 is below INT_MAX so the rawblocksize > INT_MAX
// guard does not fire. Without the fix, unsigned int arithmetic wraps
// 715827883*6 → 2, so malloc(2) succeeds and sorted lands ~680 MB beyond the
// 2-byte block, causing EXC_BAD_ACCESS — a hardware signal that @try/@catch
// cannot catch, so the test runner crashes before reaching any assertion.
// With the fix, size_t arithmetic gives the correct ~4 GB allocation size.
// malloc either returns NULL (→ XADOutOfMemoryException) or succeeds and
// decoding hits EOF on the tiny input (→ CSEndOfFileException). Either way
// the process survives, which is the only assertion we can reliably make here.
- (void)testBlockSizeWithMultiplicationOverflow
{
// blocksize=715827883: 715827883*6 mod 2^32 = 2 → malloc(2) without fix
// Bit layout: stream=0, prefix 14×1 + 0 (n=15), value loop = 0x2AAAAAAC bits,
// compressed=0, firstindex=0
uint8_t bytes[] = {
0xFE, 0x7F, // stream bit + prefix (14 ones → 0)
0xAC, 0xAA, 0xAA, 0x2A, 0x01, // blocksize value loop + compressed + firstindex
0x00, // skip byte in decodeBlockWithLength
0x00, 0x00, 0x00, 0x00, // InitializeRangeCoder
0x00, 0x00, 0x00, 0x00, // decode data (crash before any is consumed)
0x00, 0x00, 0x00, 0x00,
};

NSException *caught =
[self caughtExceptionProducingBlockWithBytes:bytes
length:sizeof(bytes)];

XCTAssertNotNil(caught, @"Expected an exception, not a hard crash");
}

#pragma mark - Helpers

- (NSException *)caughtExceptionProducingBlockWithBytes:(uint8_t *)bytes length:(size_t)length
{
CSMemoryHandle *handle =
[CSMemoryHandle memoryHandleForReadingBuffer:bytes
length:(unsigned int)length];
XADStuffItXIronHandle *ironHandle =
[[XADStuffItXIronHandle alloc] initWithHandle:handle
length:CSHandleMaxLength];
NSException *caught = nil;
@try {
[ironHandle produceBlockAtOffset:0];
} @catch (NSException *e) {
caught = e;
}
[ironHandle release];
return caught;
}

@end
13 changes: 11 additions & 2 deletions XADStuffItXIronHandle.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "StuffItXUtilities.h"
#import "CarrylessRangeCoder.h"
#import "BWT.h"
#import <limits.h>



Expand Down Expand Up @@ -93,12 +94,20 @@ -(int)produceBlockAtOffset:(off_t)pos

if(CSInputNextBitLE(input)==1) return -1;

unsigned int blocksize=(unsigned int)CSInputNextSitxP2(input);
uint64_t rawblocksize=CSInputNextSitxP2(input);
// Values above INT_MAX overflow when cast to int later (e.g. decodeBlockWithLength:).
if(rawblocksize>INT_MAX) [XADException raiseIllegalDataException];
unsigned int blocksize=(unsigned int)rawblocksize;
Comment thread
alek-prykhodko marked this conversation as resolved.
Comment thread
alek-prykhodko marked this conversation as resolved.

if(blocksize>currsize)
{
// blocksize*6 must not overflow size_t.
if((size_t)blocksize>SIZE_MAX/6)
{
[XADException raiseIllegalDataException];
}
free(block);
block=malloc(blocksize*6);
block=malloc((size_t)blocksize*6);
if(!block) [XADException raiseOutOfMemoryException];
sorted=block+blocksize;
table=(uint32_t *)(block+2*blocksize);
Expand Down
Loading