** Description changed:

- Two more bugs from the same porting effort:
+ Three more bugs from the same porting effort:
  
  4. XADSkipHandle: broken insertion loop (heap overflow on out-of-order skips)
  `XADSkipHandle.m:156-160`
  
  ```objc
-       for(int i=numregions-1;i>index;i++)
-       {
-               regions[i+1].actual=regions[i].actual;
-               regions[i+1].skip=regions[i].skip-end+start;
-       }
+  for(int i=numregions-1;i>index;i++)
+  {
+   regions[i+1].actual=regions[i].actual;
+   regions[i+1].skip=regions[i].skip-end+start;
+  }
  ```
  
  It is meant to shift regions up to make room for a new skip (the loop
  must run *downwards*: `i--`). With `i++` it runs *upwards* instead:
  whenever the new skip is not appended after all existing regions (`index
  < numregions-1`), the loop keeps writing `regions[i+1]` past the
  allocation — a heap buffer overflow — and never performs the intended
  shift, corrupting the region table.
  
  **Why it went unnoticed:** all current call sites (ALZip multi-volume,
  split-file handling) append skips in increasing offset order, so `index`
  always equals the last region and the loop body never executes. The bug
  is latent, waiting for the first caller that adds an out-of-order skip.
  
  ** Potential fix:** `for(int i=numregions-1;i>index;i--)`.
  
  5. NowCompress: reads uninitialized memory on degenerate headers
  `XADNowCompressHandle.m:195-200`
  
  ```objc
-       if(nextblock>=numblocks)
-       {
-               if(![self readNextFileHeader]) return 0;
-       }
+  if(nextblock>=numblocks)
+  {
+   if(![self readNextFileHeader]) return 0;
+  }
  
-       uint32_t offset=blocks[nextblock].offset;   // may still be out of range
+  uint32_t offset=blocks[nextblock].offset;   // may still be out of range
  ```
  
  `readNextFileHeader` can succeed while leaving `numblocks == 0`
  (degenerate header), after which `blocks[nextblock]` reads uninitialized
  malloc memory.
  
  **Potential fix:** `if(nextblock>=(int)blocks.size()) return 0;`.
+ 
+ 6. XAD7ZipParser: truncation of values to signed 32-bit
+ 
+ 64-bit integer values returned by `ReadNumber()` are cast directly to
+ signed `(int)`. Malformed archives can cause signed integer truncation
+ or negative lengths/counts.
+ 
+ ** Potential fix:** Preserving uint64_t through all parsing and bounds-
+ checking before narrowing conversions prevents overflow attacks. Diff is
+ like this:
+ 
+ ```
+ --- a/XAD7ZipParser.m
+ +++ b/XAD7ZipParser.m
+ @@ -551,3 +551,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+  {
+ -     int numcoders=(int)ReadNumber(handle);
+ +     uint64_t numcoders_val=ReadNumber(handle);
+ +     if(numcoders_val>1024) [XADException raiseIllegalDataException];
+ +     int numcoders=(int)numcoders_val;
+       NSMutableArray *instreams=[NSMutableArray array];
+ @@ -564,4 +566,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+               if(flags&0x10)
+               {
+ -                     numinstreams=(int)ReadNumber(handle);
+ -                     numoutstreams=(int)ReadNumber(handle);
+ +                     uint64_t numin=ReadNumber(handle);
+ +                     uint64_t numout=ReadNumber(handle);
+ +                     if(numin>1024||numout>1024) [XADException 
raiseIllegalDataException];
+ +                     numinstreams=(int)numin;
+ +                     numoutstreams=(int)numout;
+               }
+ @@ -570,3 +575,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+               NSData *props=nil;
+ -             if(flags&0x20) props=[handle 
readDataOfLength:(int)ReadNumber(handle)];
+ +             if(flags&0x20)
+ +             {
+ +                     uint64_t propslen=ReadNumber(handle);
+ +                     if(propslen>65536) [XADException 
raiseIllegalDataException];
+ +                     props=[handle readDataOfLength:(int)propslen];
+ +             }
+  
+ @@ -605,2 +614,4 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ +     if(totaloutstreams==0) [XADException raiseIllegalDataException];
+ +
+       // Load binding pairs
+       int numbindpairs=totaloutstreams-1;
+       for(int i=0;i<numbindpairs;i++)
+ @@ -608,4 +619,6 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+               uint64_t inindex=ReadNumber(handle);
+               uint64_t outindex=ReadNumber(handle);
+ +             
if(inindex>=(uint64_t)totalinstreams||outindex>=(uint64_t)totaloutstreams)
+ +                     [XADException raiseIllegalDataException];
+               
SetNumberEntryInArray(instreams,(int)inindex,outindex,@"SourceIndex");
+               
SetNumberEntryInArray(outstreams,(int)outindex,inindex,@"DestinationIndex");
+ @@ -615,2 +628,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+       int numpackedstreams=totalinstreams-numbindpairs;
+ +     
if(numpackedstreams<0||*packedstreamindex+numpackedstreams>(int)[packedstreams 
count])
+ +             [XADException raiseIllegalDataException];
+ +
+       if(numpackedstreams==1)
+ @@ -628,3 +644,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+               for(int i=0;i<numpackedstreams;i++)
+ -             
SetObjectEntryInArray(instreams,(int)ReadNumber(handle),[packedstreams 
objectAtIndex:*packedstreamindex+i],@"PackedStream");
+ +             {
+ +                     uint64_t sindex=ReadNumber(handle);
+ +                     if(sindex>=(uint64_t)totalinstreams) [XADException 
raiseIllegalDataException];
+ +                     
SetObjectEntryInArray(instreams,(int)sindex,[packedstreams 
objectAtIndex:*packedstreamindex+i],@"PackedStream");
+ +             }
+       }
+ @@ -655,3 +675,5 @@ -(void)parseSubStreamsInfoForHandle:(CSHandle *)handle 
folders:(NSArray *)folder
+                               for(int i=0;i<numfolders;i++)
+                               {
+ -                                     int 
numsubstreams=(int)ReadNumber(handle);
+ +                                     uint64_t numsub=ReadNumber(handle);
+ +                                     if(numsub>1000000) [XADException 
raiseIllegalDataException];
+ +                                     int numsubstreams=(int)numsub;
+                                       if(numsubstreams!=1) // Re-use default 
substream when there is only one
+ ```

** Description changed:

  Three more bugs from the same porting effort:
  
  4. XADSkipHandle: broken insertion loop (heap overflow on out-of-order skips)
  `XADSkipHandle.m:156-160`
  
  ```objc
   for(int i=numregions-1;i>index;i++)
   {
    regions[i+1].actual=regions[i].actual;
    regions[i+1].skip=regions[i].skip-end+start;
   }
  ```
  
  It is meant to shift regions up to make room for a new skip (the loop
  must run *downwards*: `i--`). With `i++` it runs *upwards* instead:
  whenever the new skip is not appended after all existing regions (`index
  < numregions-1`), the loop keeps writing `regions[i+1]` past the
  allocation — a heap buffer overflow — and never performs the intended
  shift, corrupting the region table.
  
  **Why it went unnoticed:** all current call sites (ALZip multi-volume,
  split-file handling) append skips in increasing offset order, so `index`
  always equals the last region and the loop body never executes. The bug
  is latent, waiting for the first caller that adds an out-of-order skip.
  
  ** Potential fix:** `for(int i=numregions-1;i>index;i--)`.
  
  5. NowCompress: reads uninitialized memory on degenerate headers
  `XADNowCompressHandle.m:195-200`
  
  ```objc
   if(nextblock>=numblocks)
   {
    if(![self readNextFileHeader]) return 0;
   }
  
   uint32_t offset=blocks[nextblock].offset;   // may still be out of range
  ```
  
  `readNextFileHeader` can succeed while leaving `numblocks == 0`
  (degenerate header), after which `blocks[nextblock]` reads uninitialized
  malloc memory.
  
  **Potential fix:** `if(nextblock>=(int)blocks.size()) return 0;`.
  
  6. XAD7ZipParser: truncation of values to signed 32-bit
  
  64-bit integer values returned by `ReadNumber()` are cast directly to
  signed `(int)`. Malformed archives can cause signed integer truncation
  or negative lengths/counts.
  
  ** Potential fix:** Preserving uint64_t through all parsing and bounds-
  checking before narrowing conversions prevents overflow attacks. Diff is
  like this:
  
  ```
- --- a/XAD7ZipParser.m
- +++ b/XAD7ZipParser.m
- @@ -551,3 +551,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ --- a/XADMaster/XAD7ZipParser.m
+ +++ b/XADMaster/XAD7ZipParser.m
+ @@ -534,3 +534,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
   {
  -     int numcoders=(int)ReadNumber(handle);
  +     uint64_t numcoders_val=ReadNumber(handle);
  +     if(numcoders_val>1024) [XADException raiseIllegalDataException];
  +     int numcoders=(int)numcoders_val;
        NSMutableArray *instreams=[NSMutableArray array];
- @@ -564,4 +566,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -547,4 +549,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
                if(flags&0x10)
                {
  -                     numinstreams=(int)ReadNumber(handle);
  -                     numoutstreams=(int)ReadNumber(handle);
  +                     uint64_t numin=ReadNumber(handle);
  +                     uint64_t numout=ReadNumber(handle);
  +                     if(numin>1024||numout>1024) [XADException 
raiseIllegalDataException];
  +                     numinstreams=(int)numin;
  +                     numoutstreams=(int)numout;
                }
- @@ -570,3 +575,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -553,3 +558,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
                NSData *props=nil;
  -             if(flags&0x20) props=[handle 
readDataOfLength:(int)ReadNumber(handle)];
  +             if(flags&0x20)
  +             {
  +                     uint64_t propslen=ReadNumber(handle);
  +                     if(propslen>65536) [XADException 
raiseIllegalDataException];
  +                     props=[handle readDataOfLength:(int)propslen];
  +             }
   
- @@ -605,2 +614,4 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -588,2 +597,4 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
  +     if(totaloutstreams==0) [XADException raiseIllegalDataException];
  +
        // Load binding pairs
        int numbindpairs=totaloutstreams-1;
        for(int i=0;i<numbindpairs;i++)
- @@ -608,4 +619,6 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -591,4 +602,6 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
                uint64_t inindex=ReadNumber(handle);
                uint64_t outindex=ReadNumber(handle);
  +             
if(inindex>=(uint64_t)totalinstreams||outindex>=(uint64_t)totaloutstreams)
  +                     [XADException raiseIllegalDataException];
                
SetNumberEntryInArray(instreams,(int)inindex,outindex,@"SourceIndex");
                
SetNumberEntryInArray(outstreams,(int)outindex,inindex,@"DestinationIndex");
- @@ -615,2 +628,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -598,2 +611,5 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
        int numpackedstreams=totalinstreams-numbindpairs;
  +     
if(numpackedstreams<0||*packedstreamindex+numpackedstreams>(int)[packedstreams 
count])
  +             [XADException raiseIllegalDataException];
  +
        if(numpackedstreams==1)
- @@ -628,3 +644,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
+ @@ -611,3 +627,7 @@ -(void)parseFolderForHandle:(CSHandle *)handle 
dictionary:(NSMutableDictionary *
                for(int i=0;i<numpackedstreams;i++)
  -             
SetObjectEntryInArray(instreams,(int)ReadNumber(handle),[packedstreams 
objectAtIndex:*packedstreamindex+i],@"PackedStream");
  +             {
  +                     uint64_t sindex=ReadNumber(handle);
  +                     if(sindex>=(uint64_t)totalinstreams) [XADException 
raiseIllegalDataException];
  +                     
SetObjectEntryInArray(instreams,(int)sindex,[packedstreams 
objectAtIndex:*packedstreamindex+i],@"PackedStream");
  +             }
        }
- @@ -655,3 +675,5 @@ -(void)parseSubStreamsInfoForHandle:(CSHandle *)handle 
folders:(NSArray *)folder
+ @@ -638,3 +658,5 @@ -(void)parseSubStreamsInfoForHandle:(CSHandle *)handle 
folders:(NSArray *)folder
                                for(int i=0;i<numfolders;i++)
                                {
  -                                     int 
numsubstreams=(int)ReadNumber(handle);
  +                                     uint64_t numsub=ReadNumber(handle);
  +                                     if(numsub>1000000) [XADException 
raiseIllegalDataException];
  +                                     int numsubstreams=(int)numsub;
                                        if(numsubstreams!=1) // Re-use default 
substream when there is only one
+ 
  ```

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2168659

Title:
  Potential memory corruption

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/unar/+bug/2168659/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to