Skip to content

Commit

Permalink
Fix stream copier out of bounds when size is not dividable by 512. Fi…
Browse files Browse the repository at this point in the history
…x lzw test data eol
  • Loading branch information
henrikstengaard committed Aug 9, 2023
1 parent c117eae commit 354ac0d
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 18 deletions.
1 change: 1 addition & 0 deletions demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ hst imager - demo

- gui:
- download hst imager gui
- blank: 1gb image for testing and show .img vs .vhd
- info
- read from usb
- write to usb
Expand Down
75 changes: 64 additions & 11 deletions src/Hst.Imager.Core.Tests/GivenReadCommand.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Hst.Core.Extensions;

namespace Hst.Imager.Core.Tests
{
using System;
Expand All @@ -15,7 +17,7 @@ namespace Hst.Imager.Core.Tests
public class GivenReadCommand : CommandTestBase
{
[Fact]
public async Task WhenReadPhysicalDriveTToImgThenReadDataIsIdentical()
public async Task WhenReadPhysicalDriveToImgThenReadDataIsIdentical()
{
// arrange - paths and test command helper
var sourcePath = TestCommandHelper.PhysicalDrivePath;
Expand Down Expand Up @@ -204,7 +206,7 @@ public async Task WhenReadPhysicalDriveToVhdThenReadDataIsIdentical()
// get source bytes
var sourceBytes = await testCommandHelper.ReadMediaData(sourcePath);

// get destination bytes from vhd
// get destination bytes
var destinationBytes =
await ReadMediaBytes(testCommandHelper, destinationPath, ImageSize);
var destinationPathSize = new FileInfo(destinationPath).Length;
Expand All @@ -229,32 +231,32 @@ public async Task WhenReadPhysicalDriveToVhdWithSizeThenReadDataIsIdentical()
var sourcePath = TestCommandHelper.PhysicalDrivePath;
var destinationPath = $"{Guid.NewGuid()}.vhd";
var size = 16 * 512;
var fakeCommandHelper = new TestCommandHelper();
var testCommandHelper = new TestCommandHelper();

try
{
// arrange - create source physical drive
fakeCommandHelper.AddTestMedia(sourcePath, ImageSize);
testCommandHelper.AddTestMedia(sourcePath, ImageSize);

// arrange - read command
var cancellationTokenSource = new CancellationTokenSource();
var readCommand = new ReadCommand(new NullLogger<ReadCommand>(), fakeCommandHelper,
var readCommand = new ReadCommand(new NullLogger<ReadCommand>(), testCommandHelper,
Enumerable.Empty<IPhysicalDrive>(), sourcePath, destinationPath, new Size(size, Unit.Bytes), 0, false, false, 0);

// act - read source physical drive to destination vhd
var result = await readCommand.Execute(cancellationTokenSource.Token);
Assert.True(result.IsSuccess);

// get source bytes
var sourceBytes = (await fakeCommandHelper.ReadMediaData(sourcePath)).Take(size).ToArray();
var sourceBytes = (await testCommandHelper.ReadMediaData(sourcePath)).Take(size).ToArray();
Assert.Equal(size, sourceBytes.Length);

// get destination bytes from vhd
var destinationBytes = await fakeCommandHelper.ReadMediaData(destinationPath);
var destinationPathSize = new FileInfo(destinationPath).Length;
// get destination bytes
var destinationBytes = await testCommandHelper.ReadMediaData(destinationPath);

// assert length is not the same (vhd file format different than img) and bytes are the same
Assert.NotEqual(sourceBytes.Length, destinationPathSize);
// assert - source bytes are equal to destination bytes
destinationBytes = destinationBytes.Take(size).ToArray();
Assert.Equal(sourceBytes.Length, destinationBytes.Length);
Assert.Equal(sourceBytes, destinationBytes);
}
finally
Expand All @@ -265,5 +267,56 @@ public async Task WhenReadPhysicalDriveToVhdWithSizeThenReadDataIsIdentical()
}
}
}

[Fact]
public async Task WhenReadPhysicalDriveToVhdWithSizeNotDividableBy512ThenReadDataIsIdentical()
{
// arrange - paths, size to read and test command helper
var srcPath = TestCommandHelper.PhysicalDrivePath;
var destPath = $"{Guid.NewGuid()}.vhd";
var size = 1000000;
var testCommandHelper = new TestCommandHelper();

try
{
// arrange - create source physical drive
testCommandHelper.AddTestMedia(srcPath, 10.MB());

// arrange - read command
var cancellationTokenSource = new CancellationTokenSource();
var readCommand = new ReadCommand(new NullLogger<ReadCommand>(), testCommandHelper,
Enumerable.Empty<IPhysicalDrive>(), srcPath, destPath, new Size(size, Unit.Bytes), 0, false, false, 0);

// act - read source physical drive to destination vhd
var result = await readCommand.Execute(cancellationTokenSource.Token);
Assert.True(result.IsSuccess);

// get source bytes
var sourceBytes = (await testCommandHelper.ReadMediaData(srcPath)).Take(size).ToArray();
Assert.Equal(size, sourceBytes.Length);

// get destination bytes
var destinationBytes = await testCommandHelper.ReadMediaData(destPath);

// assert - destination bytes are larger than size and dividable by 512
Assert.True(destinationBytes.Length > size);
Assert.True(destinationBytes.Length % 512 == 0);

// assert - destination bytes are equal to size rounded to next sector
Assert.Equal(size + (512 - size % 512), destinationBytes.Length);

// assert - source bytes are equal to destination bytes
destinationBytes = destinationBytes.Take(size).ToArray();
Assert.Equal(sourceBytes.Length, destinationBytes.Length);
Assert.Equal(sourceBytes, destinationBytes);
}
finally
{
if (File.Exists(destPath))
{
File.Delete(destPath);
}
}
}
}
}
46 changes: 46 additions & 0 deletions src/Hst.Imager.Core.Tests/StreamCopierTests/GivenStreamCopier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,50 @@ public async Task WhenCopyAndDestinationOffsetIsLargerThanZeroSourceThenCopiedBy
Assert.Equal(expectedDestinationBytes.Length, destinationBytes.Length);
Assert.Equal(expectedDestinationBytes, destinationBytes);
}

public byte[] CreateTestData(long size)
{
var data = new byte[size];

for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)(i % 256);
}

return data;
}

[Fact]
public async Task WhenCopy1MbFrom10MbSrcToDestWith1MbBufferThenDataIsEqual()
{
// arrange - test data
var data = CreateTestData(10.MB());

// arrange - source stream with test data
using var source = new MemoryStream();
await source.WriteBytes(data);

// arrange - destination stream
using var destination = new MemoryStream();

// arrange - stream copier
var streamCopier = new StreamCopier(1024 * 1024);

// act - copy from source to destination
var size = (int)1.MB();
var sourceOffset = 0;
var destinationOffset = 0;
var cancellationTokenSource = new CancellationTokenSource();
await streamCopier.Copy(cancellationTokenSource.Token, source, destination, size, sourceOffset, destinationOffset, true);

// get source bytes
var sourceBytes = data.Take(size).ToArray();

// get destination bytes
var destinationBytes = destination.ToArray();

// assert - source bytes are equal to destination bytes
Assert.Equal(sourceBytes.Length, destinationBytes.Length);
Assert.Equal(sourceBytes, destinationBytes);
}
}
5 changes: 5 additions & 0 deletions src/Hst.Imager.Core.Tests/TestCommandHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ public override Result<Media> GetWritableFileMedia(string path, long? size = nul
return new Result<Media>(new Media(testMedia.Path, testMedia.Name, testMedia.Data.Length,
Media.MediaType.Raw, false, new TestMediaStream(testMedia)));
}

if (size.HasValue)
{
size = GetVhdSize(size.Value);
}

if (create && size == null)
{
Expand Down
9 changes: 3 additions & 6 deletions src/Hst.Imager.Core/Commands/CommandHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using DiscUtils.Fat;
using DiscUtils.Ntfs;

namespace Hst.Imager.Core.Commands
namespace Hst.Imager.Core.Commands
{
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -267,8 +264,8 @@ public virtual Result<Media> GetWritableMedia(IEnumerable<IPhysicalDrive> physic

public virtual long GetVhdSize(long size)
{
// vhd size dividable by 512
return size % 512 != 0 ? size - (size % 512) : size;
// increase size to next sector size, if not dividable by 512
return size % 512 != 0 ? size + (512 - size % 512) : size;
}

public bool IsVhd(string path)
Expand Down
2 changes: 1 addition & 1 deletion src/Hst.Imager.Core/StreamCopier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public async Task<Result> Copy(CancellationToken token, Stream source, Stream de
foreach (var dataSector in dataSectors)
{
var sectorSize = bytesProcessed + dataSector.Start + dataSector.Size > size
? (int)(size - bytesProcessed + dataSector.Start)
? (int)(size - (bytesProcessed + dataSector.Start))
: dataSector.Size;

var sectorData = new byte[sectorSize];
Expand Down

0 comments on commit 354ac0d

Please sign in to comment.