Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5800a18
Use managed signer for bundles
jtschuster Dec 4, 2024
0555c08
Fix tests and write out symtab
jtschuster Dec 5, 2024
416e029
Fix test failures
jtschuster Dec 5, 2024
81e5cf1
Flip comparison
jtschuster Dec 5, 2024
5dd384f
Add comment to TryAdjustHeadersForBundle, and throw if it returns false
jtschuster Dec 9, 2024
031fe60
Don't memory-map file if it's not a Mach-O, remove unused file.
jtschuster Dec 9, 2024
7ee6a45
Fix typo, restructure IsSigned check.
jtschuster Dec 9, 2024
ec5b478
PR Feedback: Fomatting and comments
jtschuster Dec 12, 2024
5c381c7
PR feedback:
jtschuster Dec 13, 2024
e6fec41
Create new file for macos signing, update LINKEDIT.VMSize to fit sign…
jtschuster Dec 17, 2024
11893bd
Merge branch 'main' into ManagedSignBundles
jtschuster Dec 29, 2024
c6d3c55
Merge branch 'main' into ManagedSignBundles
jtschuster Jan 3, 2025
163a8e1
PR feedback
jtschuster Mar 25, 2025
443b151
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster Mar 25, 2025
32c8bb0
Remove old file that's not needed
jtschuster Mar 25, 2025
d50db43
Merge branch 'ManagedSignBundles' of https://github.com/jtschuster/ru…
jtschuster Mar 26, 2025
2b2f820
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster Mar 26, 2025
ef1cb02
Use constant for alignments
jtschuster Mar 28, 2025
c249fe5
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster May 15, 2025
ae704e7
Wip
jtschuster May 17, 2025
90834fa
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster May 17, 2025
ca9532b
revert dotnet.sh
jtschuster May 19, 2025
82834c6
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster May 19, 2025
dbc3fe5
Wip
jtschuster May 20, 2025
2b6446a
Merge branch 'main' of https://github.com/dotnet/runtime into Managed…
jtschuster May 20, 2025
c79f117
Ensure entitlements are preserved
jtschuster May 20, 2025
a8d7f65
Update doc comments
jtschuster May 20, 2025
3908b00
Add entitlements to singlefilehost
jtschuster May 20, 2025
d4bcbbc
Check for identical code signatures in tests
jtschuster May 26, 2025
2ea2b12
undo test throw
jtschuster May 26, 2025
e331655
Remove CodeSignature class use EmbeddedSignatureBlob instead
jtschuster Jun 2, 2025
1d7c5a0
Merge branch 'main' of https://github.com/dotnet/runtime into Preserv…
jtschuster Jun 2, 2025
7733427
Remove CodeSignature class, unsign before enabling testonly behaviors
jtschuster Jun 3, 2025
7593313
Rename file to match class, don't set length of file while mapped
jtschuster Jun 4, 2025
9cc919d
Update bundler to memory map a file once, add test for inode change w…
jtschuster Jun 5, 2025
a427944
Don't abuse inheritance, add Stream file abstraction
jtschuster Jun 9, 2025
2fb3cb6
Update reading BigEndian values
jtschuster Jun 10, 2025
f37a9f7
Merge branch 'main' of https://github.com/dotnet/runtime into Preserv…
jtschuster Jun 10, 2025
6a39a82
Use composition + interfaces instead of inheritance for blobs
jtschuster Jun 11, 2025
01beca9
Merge branch 'main' into PreserveEntitlements
jtschuster Jun 12, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public sealed class AppHostMachOFormatException : AppHostUpdateException
{
public readonly MachOFormatError Error;

internal AppHostMachOFormatException(MachOFormatError error)
: base($"Failed to process MachO file: {error}")
internal AppHostMachOFormatException(MachOFormatError error, string message = "")
: base($"Failed to process MachO file: {error}. {message}")
{
Error = error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class BinaryUtils
{
internal static unsafe void SearchAndReplace(
MemoryMappedViewAccessor accessor,
byte[] searchPattern,
ReadOnlySpan<byte> searchPattern,
byte[] patternToReplace,
bool pad0s = true)
{
Expand Down Expand Up @@ -48,7 +48,7 @@ internal static unsafe void SearchAndReplace(
}
}

private static unsafe void Pad0(byte[] searchPattern, byte[] patternToReplace, byte* bytes, int offset)
private static unsafe void Pad0(ReadOnlySpan<byte> searchPattern, byte[] patternToReplace, byte* bytes, int offset)
{
if (patternToReplace.Length < searchPattern.Length)
{
Expand Down Expand Up @@ -92,7 +92,7 @@ public static unsafe int SearchInFile(string filePath, byte[] searchPattern)
}

// See: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
private static int[] ComputeKMPFailureFunction(byte[] pattern)
private static int[] ComputeKMPFailureFunction(ReadOnlySpan<byte> pattern)
{
int[] table = new int[pattern.Length];
if (pattern.Length >= 1)
Expand Down Expand Up @@ -128,7 +128,7 @@ private static int[] ComputeKMPFailureFunction(byte[] pattern)
}

// See: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
private static unsafe int KMPSearch(byte[] pattern, byte* bytes, long bytesLength)
private static unsafe int KMPSearch(ReadOnlySpan<byte> pattern, byte* bytes, long bytesLength)
{
int m = 0;
int i = 0;
Expand Down Expand Up @@ -162,18 +162,6 @@ private static unsafe int KMPSearch(byte[] pattern, byte* bytes, long bytesLengt
return -1;
}

public static void CopyFile(string sourcePath, string destinationPath)
{
var destinationDirectory = new FileInfo(destinationPath).Directory.FullName;
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}

// Copy file to destination path so it inherits the same attributes/permissions.
File.Copy(sourcePath, destinationPath, overwrite: true);
}

internal static void WriteToStream(MemoryMappedViewAccessor sourceViewAccessor, FileStream fileStream, long length)
{
int pos = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ void RewriteAppHost(MemoryMappedFile mappedFile, MemoryMappedViewAccessor access
{
bool isMachOImage;
// MacOS requires a new inode to be created when updating a signed file, so we'll delete the file and create a new one.
if (File.Exists(appHostDestinationFilePath))
if (enableMacOSCodeSign && File.Exists(appHostDestinationFilePath))
File.Delete(appHostDestinationFilePath);

using (FileStream appHostDestinationStream = new FileStream(appHostDestinationFilePath, FileMode.CreateNew, FileAccess.ReadWrite))
using (FileStream appHostDestinationStream = new FileStream(appHostDestinationFilePath, FileMode.Create, FileAccess.ReadWrite))
{
using (FileStream appHostSourceStream = new(appHostSourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1))
{
Expand All @@ -151,12 +151,13 @@ void RewriteAppHost(MemoryMappedFile mappedFile, MemoryMappedViewAccessor access
RewriteAppHost(memoryMappedFile, memoryMappedViewAccessor);
if (isMachOImage)
{
var file = new MemoryMappedMachOViewAccessor(memoryMappedViewAccessor);
MachObjectFile machObjectFile = MachObjectFile.Create(file);
if (enableMacOSCodeSign)
{
MachObjectFile machObjectFile = MachObjectFile.Create(memoryMappedViewAccessor);
appHostLength = machObjectFile.CreateAdHocSignature(memoryMappedViewAccessor, destinationFileName);
appHostLength = machObjectFile.AdHocSignFile(file, destinationFileName);
}
else if (MachObjectFile.RemoveCodeSignatureIfPresent(memoryMappedViewAccessor, out long? length))
else if (machObjectFile.RemoveCodeSignatureIfPresent(file, out long? length))
{
appHostLength = length.Value;
}
Expand Down Expand Up @@ -190,84 +191,6 @@ void RewriteAppHost(MemoryMappedFile mappedFile, MemoryMappedViewAccessor access
}
}

/// <summary>
/// Set the current AppHost as a single-file bundle.
/// </summary>
/// <param name="appHostPath">The path of Apphost template, which has the place holder</param>
/// <param name="bundleHeaderOffset">The offset to the location of bundle header</param>
/// <param name="macosCodesign">Whether to ad-hoc sign the bundle as a Mach-O executable</param>
public static void SetAsBundle(
string appHostPath,
long bundleHeaderOffset,
bool macosCodesign = false)
{
byte[] bundleHeaderPlaceholder = {
// 8 bytes represent the bundle header-offset
// Zero for non-bundle apphosts (default).
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 32 bytes represent the bundle signature: SHA-256 for ".net core bundle"
0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38,
0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18,
0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae
};

// Re-write the destination apphost with the proper contents.
RetryUtil.RetryOnIOError(() =>
{
string tmpFile = null;
try
{
// MacOS keeps a cache of file signatures. To avoid using the cached value,
// we need to create a new inode with the contents of the old file, sign it,
// and copy it the original file path.
tmpFile = Path.GetTempFileName();
using (FileStream newBundleStream = new FileStream(tmpFile, FileMode.Create, FileAccess.ReadWrite))
{
using (FileStream oldBundleStream = new FileStream(appHostPath, FileMode.Open, FileAccess.Read))
{
oldBundleStream.CopyTo(newBundleStream);
}

long bundleSize = newBundleStream.Length;
long mmapFileSize = macosCodesign
? bundleSize + MachObjectFile.GetSignatureSizeEstimate((uint)bundleSize, Path.GetFileName(appHostPath))
: bundleSize;
using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateFromFile(newBundleStream, null, mmapFileSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, leaveOpen: true))
using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite))
{
BinaryUtils.SearchAndReplace(accessor,
bundleHeaderPlaceholder,
BitConverter.GetBytes(bundleHeaderOffset),
pad0s: false);

if (MachObjectFile.IsMachOImage(accessor))
{
var machObjectFile = MachObjectFile.Create(accessor);
if (machObjectFile.HasSignature)
throw new AppHostMachOFormatException(MachOFormatError.SignNotRemoved);

bool wasBundled = machObjectFile.TryAdjustHeadersForBundle((ulong)bundleSize, accessor);
if (!wasBundled)
throw new InvalidOperationException("The single-file bundle was unable to be created. This is likely because the bundled content is too large.");

if (macosCodesign)
bundleSize = machObjectFile.CreateAdHocSignature(accessor, Path.GetFileName(appHostPath));
}
}
newBundleStream.SetLength(bundleSize);
}
File.Copy(tmpFile, appHostPath, overwrite: true);
Chmod755(appHostPath);
}
finally
{
if (tmpFile is not null)
File.Delete(tmpFile);
}
});
}

/// <summary>
/// Check if the an AppHost is a single-file bundle
/// </summary>
Expand Down Expand Up @@ -331,7 +254,7 @@ private static byte[] GetSearchOptionBytes(DotNetSearchOptions searchOptions)
return searchOptionsBytes;
}

private static void Chmod755(string pathName)
internal static void Chmod755(string pathName)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ public PlaceHolderNotFoundInAppHostException(byte[] pattern)
{
MissingPattern = pattern;
}
public PlaceHolderNotFoundInAppHostException(ReadOnlySpan<byte> pattern)
{
MissingPattern = pattern.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,14 @@
+ "398c454307e8e33b8426143daec9f596"
+ "836f97c8f74750e5975c64e2189f45de"
+ "f46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("HostActivation.Tests, PublicKey="
+ "00240000048000009400000006020000"
+ "00240000525341310004000001000100"
+ "b5fc90e7027f67871e773a8fde8938c8"
+ "1dd402ba65b9201d60593e96c492651e"
+ "889cc13f1415ebb53fac1131ae0bd333"
+ "c5ee6021672d9718ea31a8aebd0da007"
+ "2f25d87dba6fc90ffd598ed4da35e44c"
+ "398c454307e8e33b8426143daec9f596"
+ "836f97c8f74750e5975c64e2189f45de"
+ "f46b2a2b1247adc3652bf5c308055da9")]
Loading
Loading