| ... | @@ -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; |
| 1582 | | 1582 | |
| 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) |
| 1584 | | 1584 | |
| 1585 | // Find the FDE and CIE | 1585 | // 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 { |
| 1594 | | 1594 | |
| 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 | } |
| 1823 | | 1830 | |
| | 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 { |
| 1855 | | 1881 | |
| 1856 | try stream.seekTo(left * entry_size); | 1882 | try stream.seekTo(left * entry_size); |
| 1857 | | 1883 | |
| 1858 | // Read past pc_begin | 1884 | // 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(); |
| 1870 | | 1896 | |
| 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(); |
| 1872 | | 1899 | |
| 1873 | // The length of the .eh_frame section is unknown at this point, since .eh_frame_hdr only provides the start | 1900 | 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; | | |
| 1877 | | 1903 | |
| | 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); |
| 1880 | | 1910 | |
| 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(); |
| 1884 | | 1914 | |
| | 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(); |
| 1890 | | 1921 | |
| 1891 | cie.* = try CommonInformationEntry.parse( | 1922 | cie.* = try CommonInformationEntry.parse( |