authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-06-23 16:08:11-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:13-04:00
log521988299d3e87c00388207319b09eb4ebd5d443
treed94bc08d4bbfa953defbf1e869ddff16a4c64623
parenta47212c72e9d156e06cb278ac681e51e11e140b7

add more safety checks when searching for eh_frame entries using findEntry


1 files changed, 41 insertions(+), 10 deletions(-)

lib/std/dwarf.zig+41-10
...@@ -1580,7 +1580,7 @@ pub const DwarfInfo = struct {...@@ -1580,7 +1580,7 @@ pub const DwarfInfo = struct {
1580 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;1580 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
1581 if (context.pc == 0) return;1581 if (context.pc == 0) return;
15821582
1583 // TODO: Handle signal frame (ie. use_prev_instr in libunwind)1583 // TODO: Handle unwinding from a signal frame (ie. use_prev_instr in libunwind)
15841584
1585 // Find the FDE and CIE1585 // Find the FDE and CIE
1586 var cie: CommonInformationEntry = undefined;1586 var cie: CommonInformationEntry = undefined;
...@@ -1594,7 +1594,14 @@ pub const DwarfInfo = struct {...@@ -1594,7 +1594,14 @@ pub const DwarfInfo = struct {
15941594
1595 if (di.eh_frame_hdr) |header| {1595 if (di.eh_frame_hdr) |header| {
1596 mapped_pc = context.pc;1596 mapped_pc = context.pc;
1597 try header.findEntry(context.isValidMemory, @intFromPtr(di.section(.eh_frame_hdr).?.ptr), mapped_pc, &cie, &fde);1597 try header.findEntry(
1598 context.isValidMemory,
1599 null, // TODO: Check di for this
1600 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1601 mapped_pc,
1602 &cie,
1603 &fde,
1604 );
1598 } else {1605 } else {
1599 mapped_pc = context.pc - module_base_address;1606 mapped_pc = context.pc - module_base_address;
1600 const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {1607 const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {
...@@ -1821,9 +1828,28 @@ pub const ExceptionFrameHeader = struct {...@@ -1821,9 +1828,28 @@ pub const ExceptionFrameHeader = struct {
1821 };1828 };
1822 }1829 }
18231830
1831 fn isValidPtr(
1832 self: ExceptionFrameHeader,
1833 ptr: usize,
1834 isValidMemory: *const fn (address: usize) bool,
1835 eh_frame_len: ?usize,
1836 ) bool {
1837 if (eh_frame_len) |len| {
1838 return ptr >= self.eh_frame_ptr and ptr < self.eh_frame_ptr + len;
1839 } else {
1840 return isValidMemory(ptr);
1841 }
1842 }
1843
1844 /// Find an entry by binary searching the eh_frame_hdr section.
1845 ///
1846 /// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller,
1847 /// `isValidMemory` will be called before accessing any memory referenced by
1848 /// the header entries. If `eh_frame_len` is provided, then these checks can be skipped.
1824 pub fn findEntry(1849 pub fn findEntry(
1825 self: ExceptionFrameHeader,1850 self: ExceptionFrameHeader,
1826 isValidMemory: *const fn (address: usize) bool,1851 isValidMemory: *const fn (address: usize) bool,
1852 eh_frame_len: ?usize,
1827 eh_frame_hdr_ptr: usize,1853 eh_frame_hdr_ptr: usize,
1828 pc: usize,1854 pc: usize,
1829 cie: *CommonInformationEntry,1855 cie: *CommonInformationEntry,
...@@ -1855,7 +1881,7 @@ pub const ExceptionFrameHeader = struct {...@@ -1855,7 +1881,7 @@ pub const ExceptionFrameHeader = struct {
18551881
1856 try stream.seekTo(left * entry_size);1882 try stream.seekTo(left * entry_size);
18571883
1858 // Read past pc_begin1884 // Read past the pc_begin field of the entry
1859 _ = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{1885 _ = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{
1860 .pc_rel_base = @intFromPtr(&self.entries[stream.pos]),1886 .pc_rel_base = @intFromPtr(&self.entries[stream.pos]),
1861 .follow_indirect = true,1887 .follow_indirect = true,
...@@ -1868,24 +1894,29 @@ pub const ExceptionFrameHeader = struct {...@@ -1868,24 +1894,29 @@ pub const ExceptionFrameHeader = struct {
1868 .data_rel_base = eh_frame_hdr_ptr,1894 .data_rel_base = eh_frame_hdr_ptr,
1869 }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf();1895 }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf();
18701896
1871 // TODO: Should this also do isValidMemory(fde_ptr) + 11 (worst case header size)?1897 // Verify the length fields of the FDE header are readable
1898 if (!self.isValidPtr(fde_ptr, isValidMemory, eh_frame_len) or fde_ptr < self.eh_frame_ptr) return badDwarf();
18721899
1873 // The length of the .eh_frame section is unknown at this point, since .eh_frame_hdr only provides the start1900 var fde_entry_header_len: usize = 4;
1874 if (!isValidMemory(fde_ptr) or fde_ptr < self.eh_frame_ptr) return badDwarf();1901 if (!self.isValidPtr(fde_ptr + 3, isValidMemory, eh_frame_len)) return badDwarf();
1875 const eh_frame = @ptrFromInt([*]const u8, self.eh_frame_ptr)[0..math.maxInt(usize)];1902 if (self.isValidPtr(fde_ptr + 11, isValidMemory, eh_frame_len)) fde_entry_header_len = 12;
1876 const fde_offset = fde_ptr - self.eh_frame_ptr;
18771903
1904 // Even if eh_frame_len is not specified, all ranges accssed are checked by isValidPtr
1905 const eh_frame = @ptrFromInt([*]const u8, self.eh_frame_ptr)[0..eh_frame_len orelse math.maxInt(u32)];
1906
1907 const fde_offset = fde_ptr - self.eh_frame_ptr;
1878 var eh_frame_stream = io.fixedBufferStream(eh_frame);1908 var eh_frame_stream = io.fixedBufferStream(eh_frame);
1879 try eh_frame_stream.seekTo(fde_offset);1909 try eh_frame_stream.seekTo(fde_offset);
18801910
1881 const fde_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian());1911 const fde_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian());
1882 if (!isValidMemory(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]))) return badDwarf();1912 if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf();
1883 if (fde_entry_header.type != .fde) return badDwarf();1913 if (fde_entry_header.type != .fde) return badDwarf();
18841914
1915 // CIEs always come before FDEs (the offset is a subtration), so we can assume this memory is readable
1885 const cie_offset = fde_entry_header.type.fde;1916 const cie_offset = fde_entry_header.type.fde;
1886 try eh_frame_stream.seekTo(cie_offset);1917 try eh_frame_stream.seekTo(cie_offset);
1887 const cie_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian());1918 const cie_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian());
1888 if (!isValidMemory(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]))) return badDwarf();1919 if (!self.isValidPtr(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf();
1889 if (cie_entry_header.type != .cie) return badDwarf();1920 if (cie_entry_header.type != .cie) return badDwarf();
18901921
1891 cie.* = try CommonInformationEntry.parse(1922 cie.* = try CommonInformationEntry.parse(