Skip to content

Commit

Permalink
Split the code scanner into smaller modules where easy to do so, and …
Browse files Browse the repository at this point in the history
…fix small bug with the server view model not clearing old Downloads
  • Loading branch information
CorruptComputer committed Nov 26, 2023
1 parent d2bd0ca commit 10d3eb4
Show file tree
Hide file tree
Showing 20 changed files with 1,118 additions and 924 deletions.
415 changes: 20 additions & 395 deletions UnitystationLauncher/ContentScanning/AssemblyTypeCheckerHelpers.cs

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions UnitystationLauncher/ContentScanning/Scanners/IlScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Threading.Tasks;
using ILVerify;
using UnitystationLauncher.Constants;
using UnitystationLauncher.Models.ConfigFile;

namespace UnitystationLauncher.ContentScanning.Scanners;

internal static class IlScanner
{
private static readonly bool _parallelIlScanning = false;

Check notice on line 16 in UnitystationLauncher/ContentScanning/Scanners/IlScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/IlScanner.cs#L16

Remove this initialization to '_parallelIlScanning', the compiler will do that for you.

internal static bool DoVerifyIl(string name, IResolver resolver, PEReader peReader,
MetadataReader reader, Action<string> info, Action<string> logErrors, SandboxConfig loadedCfg)
{
info.Invoke($"{name}: Verifying IL...");
Stopwatch sw = Stopwatch.StartNew();
ConcurrentBag<VerificationResult> bag = new();

// TODO: We should probably just pick one of these and remove the other
if (_parallelIlScanning)
{
IlScanner.ParallelIlScanning(reader, resolver, peReader, bag);
}
else
{
IlScanner.NonParallelIlScanning(reader, resolver, peReader, bag);
}

bool verifyErrors = false;
foreach (VerificationResult res in bag)
{
bool error = AssemblyTypeCheckerHelpers.CheckVerificationResult(loadedCfg, res, name, reader, logErrors);
if (error)
{
verifyErrors = true;
}
}

info.Invoke($"{name}: Verified IL in {sw.Elapsed.TotalMilliseconds}ms");

if (verifyErrors)
{
return false;
}

return true;
}

private static void ParallelIlScanning(MetadataReader reader, IResolver resolver, PEReader peReader, ConcurrentBag<VerificationResult> bag)
{
OrderablePartitioner<TypeDefinitionHandle> partitioner = Partitioner.Create(reader.TypeDefinitions);
Parallel.ForEach(partitioner.GetPartitions(Environment.ProcessorCount), handle =>
{
Verifier ver = new(resolver);
ver.SetSystemModuleName(new(AssemblyNames.SystemAssemblyName));
while (handle.MoveNext())
{
foreach (VerificationResult? result in ver.Verify(peReader, handle.Current, verifyMethods: true))
{
bag.Add(result);
}
}
});
}

private static void NonParallelIlScanning(MetadataReader reader, IResolver resolver, PEReader peReader, ConcurrentBag<VerificationResult> bag)
{
Verifier ver = new(resolver);
//mscorlib
ver.SetSystemModuleName(new(AssemblyNames.SystemAssemblyName));
foreach (TypeDefinitionHandle definition in reader.TypeDefinitions)
{
IEnumerable<VerificationResult> errors = ver.Verify(peReader, definition, verifyMethods: true);
foreach (VerificationResult error in errors)
{
bag.Add(error);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using UnitystationLauncher.Models.ConfigFile;
using UnitystationLauncher.Models.ContentScanning;
using UnitystationLauncher.Models.ContentScanning.ScanningTypes;
using UnitystationLauncher.Models.Enums;

namespace UnitystationLauncher.ContentScanning.Scanners;

internal static class InheritanceScanner
{
internal static void CheckInheritance(
SandboxConfig sandboxConfig,
List<(MType type, MType parent, ArraySegment<MType> interfaceImpls)> inherited,
ConcurrentBag<SandboxError> errors)
{
// This inheritance whitelisting primarily serves to avoid content doing funny stuff
// by e.g. inheriting Type.
foreach ((MType _, MType baseType, ArraySegment<MType> interfaces) in inherited)
{
if (CanInherit(baseType) == false)
{
errors.Add(new($"Inheriting of type not allowed: {baseType}"));
}

foreach (MType @interface in interfaces)
{
if (CanInherit(@interface) == false)
{
errors.Add(new($"Implementing of interface not allowed: {@interface}"));
}
}

bool CanInherit(MType inheritType)
{
MTypeReferenced realBaseType = inheritType switch
{
MTypeGeneric generic => (MTypeReferenced)generic.GenericType,
MTypeReferenced referenced => referenced,
_ => throw new InvalidOperationException() // Can't happen.
};

if (realBaseType.IsTypeAccessAllowed(sandboxConfig, out TypeConfig? cfg) == false)
{
return false;
}

return cfg.Inherit != InheritMode.Block && (cfg.Inherit == InheritMode.Allow || cfg.All);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnitystationLauncher.Models.ConfigFile;
using UnitystationLauncher.Models.ContentScanning;
using UnitystationLauncher.Models.ContentScanning.ScanningTypes;

namespace UnitystationLauncher.ContentScanning.Scanners;

internal static class MemberReferenceScanner

Check warning on line 11 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L11

Types should not have members with visibility set higher than the type's visibility
{
private static readonly bool _parallelMemberReferencesScanning = true;

public static void CheckMemberReferences(SandboxConfig sandboxConfig, List<MMemberRef> members, ConcurrentBag<SandboxError> errors)
{
// TODO: We should probably just pick one of these and remove the other
if (_parallelMemberReferencesScanning)
{
ParallelCheckMemberReferences(sandboxConfig, members, errors);
}
else
{
NonParallelCheckMemberReferences(sandboxConfig, members, errors);
}
}


private static void ParallelCheckMemberReferences(SandboxConfig sandboxConfig, List<MMemberRef> members, ConcurrentBag<SandboxError> errors)

Check failure on line 29 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L29

The Cyclomatic Complexity of this method is 17 which is greater than 10 authorized.

Check warning on line 29 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L29

This method 'ParallelCheckMemberReferences' has 82 lines, which is greater than the 80 lines authorized. Split it into smaller methods.
{
Parallel.ForEach(members, memberRef =>
{
MType baseType = memberRef.ParentType;
while (!(baseType is MTypeReferenced))
{
switch (baseType)
{
case MTypeGeneric generic:
{
baseType = generic.GenericType;

break;
}
case MTypeWackyArray:
{
// Members on arrays (not to be confused with vectors) are all fine.
// See II.14.2 in ECMA-335.
return;
}
case MTypeDefined:
{
// Valid for this to show up, safe to ignore.
return;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
}

MTypeReferenced baseTypeReferenced = (MTypeReferenced)baseType;

if (baseTypeReferenced.IsTypeAccessAllowed(sandboxConfig, out TypeConfig? typeCfg) == false)
{
// Technically this error isn't necessary since we have an earlier pass
// checking all referenced types. That should have caught this
// We still need the typeCfg so that's why we're checking. Might as well.
errors.Add(new($"Access to type not allowed: {baseTypeReferenced}"));
return;
}

if (typeCfg.All)
{
// Fully whitelisted for the type, we good.
return;
}

switch (memberRef)
{
case MMemberRefField mMemberRefField:
{
foreach (WhitelistFieldDefine field in typeCfg.FieldsParsed)
{
if (field.Name == mMemberRefField.Name &&
mMemberRefField.FieldType.WhitelistEquals(field.FieldType))
{
return; // Found
}
}

errors.Add(new($"Access to field not allowed: {mMemberRefField}"));
break;
}
case MMemberRefMethod mMemberRefMethod:

Check warning on line 95 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L95

Reduce this switch section number of statements from 13 to at most 8, for example by extracting code into a method.
foreach (WhitelistMethodDefine parsed in typeCfg.MethodsParsed)
{
bool notParamMismatch = true;

if (parsed.Name == mMemberRefMethod.Name &&
mMemberRefMethod.ReturnType.WhitelistEquals(parsed.ReturnType) &&
mMemberRefMethod.ParameterTypes.Length == parsed.ParameterTypes.Count &&
mMemberRefMethod.GenericParameterCount == parsed.GenericParameterCount)
{
for (int i = 0; i < mMemberRefMethod.ParameterTypes.Length; i++)

Check failure on line 105 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L105

Refactor this code to not nest more than 3 control flow statements.
{
MType a = mMemberRefMethod.ParameterTypes[i];
MType b = parsed.ParameterTypes[i];

if (a.WhitelistEquals(b) == false)
{
notParamMismatch = false;
break;
}
}

if (notParamMismatch)

Check failure on line 117 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L117

Refactor this code to not nest more than 3 control flow statements.
{
return; // Found
}
}
}

errors.Add(new($"Access to method not allowed: {mMemberRefMethod}"));
break;
default:
throw new ArgumentOutOfRangeException(nameof(memberRef));
}
});
}

private static void NonParallelCheckMemberReferences(SandboxConfig sandboxConfig, List<MMemberRef> members, ConcurrentBag<SandboxError> errors)

Check failure on line 132 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L132

The Cyclomatic Complexity of this method is 19 which is greater than 10 authorized.

Check warning on line 132 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L132

This method 'NonParallelCheckMemberReferences' has 87 lines, which is greater than the 80 lines authorized. Split it into smaller methods.
{
foreach (MMemberRef memberRef in members)
{
MType baseType = memberRef.ParentType;
while (!(baseType is MTypeReferenced))
{
switch (baseType)
{
case MTypeGeneric generic:
{
baseType = generic.GenericType;

break;
}
case MTypeWackyArray:
{
// Members on arrays (not to be confused with vectors) are all fine.
// See II.14.2 in ECMA-335.
continue;
}
case MTypeDefined:
{
// Valid for this to show up, safe to ignore.
continue;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
}

MTypeReferenced baseTypeReferenced = (MTypeReferenced)baseType;

if (baseTypeReferenced.IsTypeAccessAllowed(sandboxConfig, out TypeConfig? typeCfg) == false)
{
// Technically this error isn't necessary since we have an earlier pass
// checking all referenced types. That should have caught this
// We still need the typeCfg so that's why we're checking. Might as well.
errors.Add(new($"Access to type not allowed: {baseTypeReferenced}"));
continue;
}

if (typeCfg.All)
{
// Fully whitelisted for the type, we good.
continue;
}

switch (memberRef)
{
case MMemberRefField mMemberRefField:
{
foreach (WhitelistFieldDefine field in typeCfg.FieldsParsed)
{
if (field.Name == mMemberRefField.Name &&

Check failure on line 188 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L188

Refactor this code to not nest more than 3 control flow statements.
mMemberRefField.FieldType.WhitelistEquals(field.FieldType))
{
continue; // Found
}
}

errors.Add(new($"Access to field not allowed: {mMemberRefField}"));
break;
}
case MMemberRefMethod mMemberRefMethod:
bool notParamMismatch = true;
foreach (WhitelistMethodDefine parsed in typeCfg.MethodsParsed)
{
if (parsed.Name == mMemberRefMethod.Name &&

Check failure on line 202 in UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

UnitystationLauncher/ContentScanning/Scanners/MemberReferenceScanner.cs#L202

Refactor this code to not nest more than 3 control flow statements.
mMemberRefMethod.ReturnType.WhitelistEquals(parsed.ReturnType) &&
mMemberRefMethod.ParameterTypes.Length == parsed.ParameterTypes.Count &&
mMemberRefMethod.GenericParameterCount == parsed.GenericParameterCount)
{
for (int i = 0; i < mMemberRefMethod.ParameterTypes.Length; i++)
{
MType a = mMemberRefMethod.ParameterTypes[i];
MType b = parsed.ParameterTypes[i];

if (!a.WhitelistEquals(b))
{
notParamMismatch = false;
break;

}
}

if (notParamMismatch)
{
break; // Found
}
break;
}
}

if (notParamMismatch == false)
{
continue;
}

errors.Add(new($"Access to method not allowed: {mMemberRefMethod}"));
break;
default:
throw new ArgumentOutOfRangeException(nameof(memberRef));
}
}
}
}
Loading

0 comments on commit 10d3eb4

Please sign in to comment.