up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -313,11 +314,6 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
|
||||
|
||||
private static void EmitDependencies(ReachabilityGraphBuilder builder, BinaryInfo info)
|
||||
{
|
||||
if (info.Dependencies.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var dep in info.Dependencies)
|
||||
{
|
||||
var depSymbolId = BuildDependencySymbolId(info.Format, dep.Name);
|
||||
@@ -391,7 +387,7 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
|
||||
confidence: EdgeConfidence.Medium,
|
||||
origin: "static",
|
||||
provenance: "symbol-undef",
|
||||
evidence: $"file:{info.RelativePath}:undef");
|
||||
evidence: $"file:{info.RelativePath}:{unknown.ReasonCode}:{unknown.SymbolName}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,11 +493,295 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
|
||||
|
||||
private static IReadOnlyList<BinaryUnknown> CollectUnknowns(byte[] data, string format, CancellationToken cancellationToken)
|
||||
{
|
||||
// For now, return empty list - full implementation would parse symbol tables
|
||||
// for undefined symbols (STT_NOTYPE with SHN_UNDEF in ELF, etc.)
|
||||
// This is a placeholder for the baseline; full implementation would require
|
||||
// parsing the symbol tables of ELF/PE/Mach-O files
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
return format switch
|
||||
{
|
||||
"elf" => CollectElfUndefinedSymbols(data, cancellationToken),
|
||||
_ => Array.Empty<BinaryUnknown>()
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<BinaryUnknown> CollectElfUndefinedSymbols(byte[] data, CancellationToken cancellationToken)
|
||||
{
|
||||
ReadOnlySpan<byte> span = data;
|
||||
if (span.Length < 64 || !IsElf(span))
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var is64Bit = span[4] == 2;
|
||||
var isBigEndian = span[5] == 2;
|
||||
|
||||
if (!TryReadElfSectionHeaderTable(span, is64Bit, isBigEndian, out var shoff, out var shentsize, out var shnum, out var shstrndx))
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
if (shoff == 0 || shentsize == 0 || shnum == 0 || shstrndx < 0 || shstrndx >= shnum)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
if (shentsize < (is64Bit ? 64 : 40))
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
if (shoff > (ulong)span.Length || shoff + (ulong)shentsize * (ulong)shnum > (ulong)span.Length)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var sections = new ElfSectionHeader[shnum];
|
||||
for (var i = 0; i < shnum; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var entryOffset = (int)shoff + i * shentsize;
|
||||
var entry = span.Slice(entryOffset, shentsize);
|
||||
|
||||
if (is64Bit)
|
||||
{
|
||||
sections[i] = new ElfSectionHeader(
|
||||
NameOffset: ReadUInt32(entry, 0, isBigEndian),
|
||||
Type: ReadUInt32(entry, 4, isBigEndian),
|
||||
Offset: ReadUInt64(entry, 24, isBigEndian),
|
||||
Size: ReadUInt64(entry, 32, isBigEndian),
|
||||
Link: (int)ReadUInt32(entry, 40, isBigEndian),
|
||||
EntrySize: ReadUInt64(entry, 56, isBigEndian));
|
||||
}
|
||||
else
|
||||
{
|
||||
sections[i] = new ElfSectionHeader(
|
||||
NameOffset: ReadUInt32(entry, 0, isBigEndian),
|
||||
Type: ReadUInt32(entry, 4, isBigEndian),
|
||||
Offset: ReadUInt32(entry, 16, isBigEndian),
|
||||
Size: ReadUInt32(entry, 20, isBigEndian),
|
||||
Link: (int)ReadUInt32(entry, 24, isBigEndian),
|
||||
EntrySize: ReadUInt32(entry, 36, isBigEndian));
|
||||
}
|
||||
}
|
||||
|
||||
var shStrTab = sections[shstrndx];
|
||||
if (shStrTab.Offset == 0 || shStrTab.Size == 0 || shStrTab.Offset + shStrTab.Size > (ulong)span.Length)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var shStrTabSpan = span.Slice((int)shStrTab.Offset, (int)shStrTab.Size);
|
||||
|
||||
var dynsymIndex = FindSectionIndex(sections, shStrTabSpan, ".dynsym");
|
||||
if (dynsymIndex < 0)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var dynsym = sections[dynsymIndex];
|
||||
if (dynsym.Offset == 0 || dynsym.Size == 0 || dynsym.Offset + dynsym.Size > (ulong)span.Length)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
if (dynsym.Link < 0 || dynsym.Link >= sections.Length)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var dynstr = sections[dynsym.Link];
|
||||
if (dynstr.Offset == 0 || dynstr.Size == 0 || dynstr.Offset + dynstr.Size > (ulong)span.Length)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var dynstrSpan = span.Slice((int)dynstr.Offset, (int)dynstr.Size);
|
||||
|
||||
var entrySize = dynsym.EntrySize > 0 ? (int)Math.Min(dynsym.EntrySize, int.MaxValue) : (is64Bit ? 24 : 16);
|
||||
if (entrySize <= 0)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
var symbolCount = (int)Math.Min(dynsym.Size / (ulong)entrySize, 4096);
|
||||
if (symbolCount <= 0)
|
||||
{
|
||||
return Array.Empty<BinaryUnknown>();
|
||||
}
|
||||
|
||||
const int maxUnknowns = 200;
|
||||
var unknowns = new List<BinaryUnknown>(Math.Min(symbolCount, maxUnknowns));
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
for (var i = 0; i < symbolCount; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var symOffset = (long)dynsym.Offset + i * (long)entrySize;
|
||||
if (symOffset < 0 || symOffset + entrySize > span.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var sym = span.Slice((int)symOffset, entrySize);
|
||||
|
||||
uint nameOffset;
|
||||
ushort sectionIndex;
|
||||
if (is64Bit)
|
||||
{
|
||||
if (sym.Length < 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nameOffset = ReadUInt32(sym, 0, isBigEndian);
|
||||
sectionIndex = ReadUInt16(sym, 6, isBigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sym.Length < 16)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nameOffset = ReadUInt32(sym, 0, isBigEndian);
|
||||
sectionIndex = ReadUInt16(sym, 14, isBigEndian);
|
||||
}
|
||||
|
||||
if (sectionIndex != 0 || nameOffset == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = ReadNullTerminatedString(dynstrSpan, nameOffset, maxBytes: 256);
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!seen.Add(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unknowns.Add(new BinaryUnknown(name, "elf-dynsym-undef"));
|
||||
if (unknowns.Count >= maxUnknowns)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return unknowns
|
||||
.OrderBy(u => u.SymbolName, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static bool IsElf(ReadOnlySpan<byte> span)
|
||||
=> span.Length >= 4
|
||||
&& span[0] == 0x7F
|
||||
&& span[1] == (byte)'E'
|
||||
&& span[2] == (byte)'L'
|
||||
&& span[3] == (byte)'F';
|
||||
|
||||
private static bool TryReadElfSectionHeaderTable(
|
||||
ReadOnlySpan<byte> span,
|
||||
bool is64Bit,
|
||||
bool isBigEndian,
|
||||
out ulong shoff,
|
||||
out int shentsize,
|
||||
out int shnum,
|
||||
out int shstrndx)
|
||||
{
|
||||
shoff = 0;
|
||||
shentsize = 0;
|
||||
shnum = 0;
|
||||
shstrndx = 0;
|
||||
|
||||
if (span.Length < (is64Bit ? 64 : 52))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is64Bit)
|
||||
{
|
||||
shoff = ReadUInt64(span, 40, isBigEndian);
|
||||
shentsize = ReadUInt16(span, 58, isBigEndian);
|
||||
shnum = ReadUInt16(span, 60, isBigEndian);
|
||||
shstrndx = ReadUInt16(span, 62, isBigEndian);
|
||||
return true;
|
||||
}
|
||||
|
||||
shoff = ReadUInt32(span, 32, isBigEndian);
|
||||
shentsize = ReadUInt16(span, 46, isBigEndian);
|
||||
shnum = ReadUInt16(span, 48, isBigEndian);
|
||||
shstrndx = ReadUInt16(span, 50, isBigEndian);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int FindSectionIndex(ElfSectionHeader[] sections, ReadOnlySpan<byte> shStrTab, string sectionName)
|
||||
{
|
||||
for (var i = 0; i < sections.Length; i++)
|
||||
{
|
||||
var name = ReadNullTerminatedString(shStrTab, sections[i].NameOffset, maxBytes: 256);
|
||||
if (string.Equals(name, sectionName, StringComparison.Ordinal))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static ushort ReadUInt16(ReadOnlySpan<byte> span, int offset, bool bigEndian)
|
||||
{
|
||||
if ((uint)offset + 2 > (uint)span.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bigEndian
|
||||
? BinaryPrimitives.ReadUInt16BigEndian(span.Slice(offset, 2))
|
||||
: BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(offset, 2));
|
||||
}
|
||||
|
||||
private static uint ReadUInt32(ReadOnlySpan<byte> span, int offset, bool bigEndian)
|
||||
{
|
||||
if ((uint)offset + 4 > (uint)span.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bigEndian
|
||||
? BinaryPrimitives.ReadUInt32BigEndian(span.Slice(offset, 4))
|
||||
: BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(offset, 4));
|
||||
}
|
||||
|
||||
private static ulong ReadUInt64(ReadOnlySpan<byte> span, int offset, bool bigEndian)
|
||||
{
|
||||
if ((uint)offset + 8 > (uint)span.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bigEndian
|
||||
? BinaryPrimitives.ReadUInt64BigEndian(span.Slice(offset, 8))
|
||||
: BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(offset, 8));
|
||||
}
|
||||
|
||||
private static string? ReadNullTerminatedString(ReadOnlySpan<byte> table, uint offset, int maxBytes)
|
||||
{
|
||||
if (offset >= table.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var slice = table[(int)offset..];
|
||||
var terminator = slice.IndexOf((byte)0);
|
||||
var length = terminator >= 0 ? terminator : Math.Min(slice.Length, maxBytes);
|
||||
|
||||
if (length <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(slice[..length]);
|
||||
}
|
||||
|
||||
private static string? InferPurl(string fileName, string format)
|
||||
@@ -607,6 +887,14 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
|
||||
string Name,
|
||||
string Address);
|
||||
|
||||
private sealed record ElfSectionHeader(
|
||||
uint NameOffset,
|
||||
uint Type,
|
||||
ulong Offset,
|
||||
ulong Size,
|
||||
int Link,
|
||||
ulong EntrySize);
|
||||
|
||||
private sealed record BinaryUnknown(
|
||||
string SymbolName,
|
||||
string ReasonCode);
|
||||
|
||||
Reference in New Issue
Block a user