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

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,4 +1,6 @@
using System.Buffers.Binary;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Scanner.Reachability;
@@ -132,6 +134,39 @@ public class BinaryReachabilityLifterTests
Assert.DoesNotContain(graph.Nodes, n => n.Kind == "entry_point");
}
[Fact]
public async Task EmitsUnknownsForElfUndefinedDynsymSymbols()
{
using var temp = new TempDir();
var binaryPath = System.IO.Path.Combine(temp.Path, "sample.so");
var bytes = CreateElfWithDynsymUndefinedSymbol("puts");
await System.IO.File.WriteAllBytesAsync(binaryPath, bytes);
var context = new ReachabilityLifterContext
{
RootPath = temp.Path,
AnalysisId = "analysis-unknowns"
};
var builder = new ReachabilityGraphBuilder();
var lifter = new BinaryReachabilityLifter();
await lifter.LiftAsync(context, builder, CancellationToken.None);
var graph = builder.ToUnionGraph(SymbolId.Lang.Binary);
var binaryNode = Assert.Single(graph.Nodes, n => n.Kind == "binary");
var unknownNode = Assert.Single(graph.Nodes, n => n.Kind == "unknown" && n.Display == "?puts");
Assert.NotNull(unknownNode.Attributes);
Assert.Equal("true", unknownNode.Attributes!["is_unknown"]);
Assert.Equal("elf-dynsym-undef", unknownNode.Attributes["reason"]);
Assert.Contains(graph.Edges, e =>
e.EdgeType == EdgeTypes.Call &&
e.From == binaryNode.SymbolId &&
e.To == unknownNode.SymbolId);
}
private static byte[] CreateMinimalElf()
{
var data = new byte[64];
@@ -165,4 +200,111 @@ public class BinaryReachabilityLifterTests
BitConverter.TryWriteBytes(data.AsSpan(24, 8), entryAddr);
return data;
}
private static byte[] CreateElfWithDynsymUndefinedSymbol(string symbolName)
{
var shstr = Encoding.ASCII.GetBytes("\0.shstrtab\0.dynstr\0.dynsym\0");
var dynstr = Encoding.ASCII.GetBytes("\0" + symbolName + "\0");
const int elfHeaderSize = 64;
const int shEntrySize = 64;
const int dynsymEntrySize = 24;
const int dynsymEntries = 2;
var offset = elfHeaderSize;
var shstrOffset = offset;
offset = Align(offset + shstr.Length, 8);
var dynstrOffset = offset;
offset = Align(offset + dynstr.Length, 8);
var dynsymOffset = offset;
var dynsymSize = dynsymEntrySize * dynsymEntries;
offset = Align(offset + dynsymSize, 8);
var shoff = offset;
const int shnum = 4;
var totalSize = shoff + shnum * shEntrySize;
var buffer = new byte[totalSize];
// ELF header (64-bit LE) with section headers.
buffer[0] = 0x7F;
buffer[1] = (byte)'E';
buffer[2] = (byte)'L';
buffer[3] = (byte)'F';
buffer[4] = 2; // 64-bit
buffer[5] = 1; // little endian
buffer[6] = 1; // version
buffer[7] = 0; // System V ABI
WriteU16LE(buffer, 16, 3); // e_type = ET_DYN
WriteU16LE(buffer, 18, 0x3E); // e_machine = EM_X86_64
WriteU32LE(buffer, 20, 1); // e_version
WriteU64LE(buffer, 24, 0); // e_entry
WriteU64LE(buffer, 32, 0); // e_phoff
WriteU64LE(buffer, 40, (ulong)shoff); // e_shoff
WriteU32LE(buffer, 48, 0); // e_flags
WriteU16LE(buffer, 52, elfHeaderSize); // e_ehsize
WriteU16LE(buffer, 54, 0); // e_phentsize
WriteU16LE(buffer, 56, 0); // e_phnum
WriteU16LE(buffer, 58, shEntrySize); // e_shentsize
WriteU16LE(buffer, 60, shnum); // e_shnum
WriteU16LE(buffer, 62, 1); // e_shstrndx
shstr.CopyTo(buffer, shstrOffset);
dynstr.CopyTo(buffer, dynstrOffset);
// .dynsym with one undefined global function symbol.
var sym1 = dynsymOffset + dynsymEntrySize;
WriteU32LE(buffer, sym1 + 0, 1u); // st_name (offset into dynstr)
buffer[sym1 + 4] = 0x12; // st_info = STB_GLOBAL(1) | STT_FUNC(2)
buffer[sym1 + 5] = 0x00; // st_other
WriteU16LE(buffer, sym1 + 6, 0); // st_shndx = SHN_UNDEF
// Section headers.
// Section 1: .shstrtab
var sh1 = shoff + shEntrySize;
WriteU32LE(buffer, sh1 + 0, 1u); // sh_name
WriteU32LE(buffer, sh1 + 4, 3u); // sh_type = SHT_STRTAB
WriteU64LE(buffer, sh1 + 24, (ulong)shstrOffset); // sh_offset
WriteU64LE(buffer, sh1 + 32, (ulong)shstr.Length); // sh_size
WriteU64LE(buffer, sh1 + 48, 1u); // sh_addralign
// Section 2: .dynstr
var sh2 = shoff + shEntrySize * 2;
WriteU32LE(buffer, sh2 + 0, 11u); // sh_name
WriteU32LE(buffer, sh2 + 4, 3u); // sh_type = SHT_STRTAB
WriteU64LE(buffer, sh2 + 24, (ulong)dynstrOffset); // sh_offset
WriteU64LE(buffer, sh2 + 32, (ulong)dynstr.Length); // sh_size
WriteU64LE(buffer, sh2 + 48, 1u); // sh_addralign
// Section 3: .dynsym
var sh3 = shoff + shEntrySize * 3;
WriteU32LE(buffer, sh3 + 0, 19u); // sh_name
WriteU32LE(buffer, sh3 + 4, 11u); // sh_type = SHT_DYNSYM
WriteU64LE(buffer, sh3 + 24, (ulong)dynsymOffset); // sh_offset
WriteU64LE(buffer, sh3 + 32, (ulong)dynsymSize); // sh_size
WriteU32LE(buffer, sh3 + 40, 2u); // sh_link = dynstr
WriteU32LE(buffer, sh3 + 44, 1u); // sh_info (one local symbol)
WriteU64LE(buffer, sh3 + 48, 8u); // sh_addralign
WriteU64LE(buffer, sh3 + 56, dynsymEntrySize); // sh_entsize
return buffer;
}
private static int Align(int value, int alignment)
=> (value + (alignment - 1)) / alignment * alignment;
private static void WriteU16LE(byte[] buffer, int offset, int value)
=> BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(offset, 2), (ushort)value);
private static void WriteU16LE(byte[] buffer, int offset, ushort value)
=> BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(offset, 2), value);
private static void WriteU32LE(byte[] buffer, int offset, uint value)
=> BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(offset, 4), value);
private static void WriteU64LE(byte[] buffer, int offset, ulong value)
=> BinaryPrimitives.WriteUInt64LittleEndian(buffer.AsSpan(offset, 8), value);
}