Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions src/StandaloneModuleGraph.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,26 @@ pub const StandaloneModuleGraph = struct {
};

const Macho = struct {
pub extern "C" fn Bun__getStandaloneModuleGraphMachoLength() ?*align(1) u32;
pub extern "C" fn Bun__getStandaloneModuleGraphMachoLength() ?*align(1) u64;

pub fn getData() ?[]const u8 {
if (Bun__getStandaloneModuleGraphMachoLength()) |length| {
if (length.* < 8) {
return null;
}

// BlobHeader has 8 bytes size (u64), so data starts at offset 8.
const data_offset = @sizeOf(u64);
const slice_ptr: [*]const u8 = @ptrCast(length);
return slice_ptr[4..][0..length.*];
return slice_ptr[data_offset..][0..length.*];
}

return null;
}
};

const PE = struct {
pub extern "C" fn Bun__getStandaloneModuleGraphPELength() u32;
pub extern "C" fn Bun__getStandaloneModuleGraphPELength() u64;
pub extern "C" fn Bun__getStandaloneModuleGraphPEData() ?[*]u8;

pub fn getData() ?[]const u8 {
Expand Down
13 changes: 7 additions & 6 deletions src/bun.js/bindings/c-bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,14 +910,14 @@ extern "C" void Bun__signpost_emit(os_log_t log, os_signpost_type_t type, os_sig

extern "C" {
struct BlobHeader {
uint32_t size;
uint64_t size; // 64-bit to ensure data[] starts at 8-byte aligned offset (required for bytecode cache)
uint8_t data[];
} __attribute__((aligned(BLOB_HEADER_ALIGNMENT)));
}

extern "C" BlobHeader __attribute__((section("__BUN,__bun"))) BUN_COMPILED = { 0, 0 };

extern "C" uint32_t* Bun__getStandaloneModuleGraphMachoLength()
extern "C" uint64_t* Bun__getStandaloneModuleGraphMachoLength()
{
return &BUN_COMPILED.size;
}
Expand All @@ -927,7 +927,7 @@ extern "C" uint32_t* Bun__getStandaloneModuleGraphMachoLength()
#include <windows.h>
#include <winnt.h>

static uint32_t* pe_section_size = nullptr;
static uint64_t* pe_section_size = nullptr;
static uint8_t* pe_section_data = nullptr;

// Helper function to find and map the .bun section
Expand All @@ -949,9 +949,10 @@ static bool initializePESection()
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) {
if (strncmp((char*)sectionHeader->Name, ".bun", 4) == 0) {
// Found the .bun section
// Section format: 8 bytes size (uint64_t) + data
BYTE* sectionData = (BYTE*)hModule + sectionHeader->VirtualAddress;
pe_section_size = (uint32_t*)sectionData;
pe_section_data = sectionData + sizeof(uint32_t);
pe_section_size = (uint64_t*)sectionData;
pe_section_data = sectionData + sizeof(uint64_t); // Skip size (8)
return true;
}
sectionHeader++;
Expand All @@ -960,7 +961,7 @@ static bool initializePESection()
return false;
}

extern "C" uint32_t Bun__getStandaloneModuleGraphPELength()
extern "C" uint64_t Bun__getStandaloneModuleGraphPELength()
{
if (!initializePESection()) return 0;
return pe_section_size ? *pe_section_size : 0;
Expand Down
10 changes: 5 additions & 5 deletions src/macho.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub const MachoFile = struct {
const PAGE_SIZE: u64 = 1 << 12;
const HASH_SIZE: usize = 32; // SHA256 = 32 bytes

const header_size = @sizeOf(u32);
const header_size = @sizeOf(u64);
const total_size = header_size + data.len;
const aligned_size = alignSize(total_size, blob_alignment);

Expand Down Expand Up @@ -164,14 +164,14 @@ pub const MachoFile = struct {
const prev_after_bun_slice = prev_data_slice[original_segsize..];
bun.memmove(after_bun_slice, prev_after_bun_slice);

// Now we copy the u32 size header
std.mem.writeInt(u32, self.data.items[original_fileoff..][0..4], @intCast(data.len), .little);
// Now we copy the u64 size header (8 bytes for alignment)
std.mem.writeInt(u64, self.data.items[original_fileoff..][0..8], @intCast(data.len), .little);

// Now we copy the data itself
@memcpy(self.data.items[original_fileoff + 4 ..][0..data.len], data);
@memcpy(self.data.items[original_fileoff + 8 ..][0..data.len], data);

// Lastly, we zero any of the padding that was added
const padding_bytes = self.data.items[original_fileoff..][data.len + 4 .. aligned_size];
const padding_bytes = self.data.items[original_fileoff..][data.len + 8 .. aligned_size];
@memset(padding_bytes, 0);

if (code_sign_cmd) |cs| {
Expand Down
30 changes: 16 additions & 14 deletions src/pe.zig
Original file line number Diff line number Diff line change
Expand Up @@ -500,11 +500,11 @@ pub const PEFile = struct {
}
}

// Check for overflow before adding 4
if (data_to_embed.len > std.math.maxInt(u32) - 4) {
// Check for overflow before adding 8
if (data_to_embed.len > std.math.maxInt(u32) - 8) {
return error.Overflow;
}
const payload_len = @as(u32, @intCast(data_to_embed.len + 4)); // 4 for LE length prefix
const payload_len = @as(u32, @intCast(data_to_embed.len + 8)); // 8 for LE length prefix
const raw_size = try alignUpU32(payload_len, opt.file_alignment);
const new_va = try alignUpU32(last_va_end, opt.section_alignment);
const new_raw = try alignUpU32(last_file_end, opt.file_alignment);
Expand Down Expand Up @@ -536,9 +536,9 @@ pub const PEFile = struct {
std.mem.copyForwards(u8, self.data.items[new_sh_off .. new_sh_off + @sizeOf(SectionHeader)], std.mem.asBytes(&sh));

// 8. Write payload
// At data[new_raw ..]: write LE length prefix then data
std.mem.writeInt(u32, self.data.items[new_raw..][0..4], @intCast(data_to_embed.len), .little);
@memcpy(self.data.items[new_raw + 4 ..][0..data_to_embed.len], data_to_embed);
// At data[new_raw ..]: write u64 LE length prefix, then data
std.mem.writeInt(u64, self.data.items[new_raw..][0..8], @intCast(data_to_embed.len), .little);
@memcpy(self.data.items[new_raw + 8 ..][0..data_to_embed.len], data_to_embed);

// 9. Update headers
// Get fresh pointers after resize
Expand Down Expand Up @@ -573,7 +573,8 @@ pub const PEFile = struct {
const section_headers = try self.getSectionHeaders();
for (section_headers) |section| {
if (std.mem.eql(u8, section.name[0..8], &BUN_SECTION_NAME)) {
if (section.size_of_raw_data < @sizeOf(u32)) {
// Header: 8 bytes size (u64)
if (section.size_of_raw_data < @sizeOf(u64)) {
return error.InvalidBunSection;
}

Expand All @@ -585,36 +586,37 @@ pub const PEFile = struct {
}

const section_data = self.data.items[section.pointer_to_raw_data..][0..section.size_of_raw_data];
const data_size = std.mem.readInt(u32, section_data[0..4], .little);
const data_size = std.mem.readInt(u64, section_data[0..8], .little);

if (data_size + @sizeOf(u32) > section.size_of_raw_data) {
if (data_size + @sizeOf(u64) > section.size_of_raw_data) {
return error.InvalidBunSection;
}

return section_data[4..][0..data_size];
// Data starts at offset 8 (after u64 size)
return section_data[8..][0..data_size];
}
}
return error.BunSectionNotFound;
}

/// Get the length of the Bun section data
pub fn getBunSectionLength(self: *const PEFile) !u32 {
pub fn getBunSectionLength(self: *const PEFile) !u64 {
const section_headers = try self.getSectionHeaders();
for (section_headers) |section| {
if (std.mem.eql(u8, section.name[0..8], &BUN_SECTION_NAME)) {
if (section.size_of_raw_data < @sizeOf(u32)) {
if (section.size_of_raw_data < @sizeOf(u64)) {
return error.InvalidBunSection;
}

// Bounds check
if (section.pointer_to_raw_data >= self.data.items.len or
section.pointer_to_raw_data + @sizeOf(u32) > self.data.items.len)
section.pointer_to_raw_data + @sizeOf(u64) > self.data.items.len)
{
return error.InvalidBunSection;
}

const section_data = self.data.items[section.pointer_to_raw_data..];
return std.mem.readInt(u32, section_data[0..4], .little);
return std.mem.readInt(u64, section_data[0..8], .little);
}
}
return error.BunSectionNotFound;
Expand Down
8 changes: 4 additions & 4 deletions test/regression/issue/pe-codesigning-integrity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ describe.if(isWindows)("PE codesigning integrity", () => {
// Read the .bun section data
const sectionData = new Uint8Array(this.buffer, bunSection.pointerToRawData, bunSection.sizeOfRawData);

// First 4 bytes should be the data size
const dataSize = new DataView(sectionData.buffer, bunSection.pointerToRawData).getUint32(0, true);
// First 8 bytes should be the data size (u64 for 8-byte alignment)
const dataSize = Number(new DataView(sectionData.buffer, bunSection.pointerToRawData).getBigUint64(0, true));

// Validate the size is reasonable - it should match or be close to virtual size
if (dataSize > bunSection.sizeOfRawData || dataSize === 0) {
Expand All @@ -133,8 +133,8 @@ describe.if(isWindows)("PE codesigning integrity", () => {
throw new Error(`Invalid .bun section: data size ${dataSize} exceeds virtual size ${bunSection.virtualSize}`);
}

// Extract the actual embedded data (skip the 4-byte size header)
const embeddedData = sectionData.slice(4, 4 + dataSize);
// Extract the actual embedded data (skip the 8-byte size header)
const embeddedData = sectionData.slice(8, 8 + dataSize);

return {
section: bunSection,
Expand Down