mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 10:09:05 +02:00
Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
553 lines
18 KiB
Go
553 lines
18 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package sysinfoapi
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestFormatUUID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
raw []byte
|
|
smbios26Plus bool
|
|
expected string
|
|
}{
|
|
{
|
|
name: "SMBIOS 2.6+ UUID with LE byte swap",
|
|
// Stored as: 78 56 34 12 34 12 78 56 12 34 56 78 9a bc de f0
|
|
// Expected: 12345678-1234-5678-1234-56789abcdef0
|
|
raw: []byte{0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0},
|
|
smbios26Plus: true,
|
|
expected: "12345678-1234-5678-1234-56789abcdef0",
|
|
},
|
|
{
|
|
name: "pre-2.6 UUID in network byte order",
|
|
// All fields in big-endian (network) order.
|
|
raw: []byte{0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0},
|
|
smbios26Plus: false,
|
|
expected: "12345678-1234-5678-1234-56789abcdef0",
|
|
},
|
|
{
|
|
name: "all zeros - not settable",
|
|
raw: make([]byte, 16),
|
|
smbios26Plus: true,
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "all 0xFF - not present",
|
|
raw: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
smbios26Plus: true,
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "too short",
|
|
raw: []byte{0x01, 0x02},
|
|
smbios26Plus: true,
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
assert.Equal(t, tc.expected, formatUUID(tc.raw, tc.smbios26Plus))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractStrings(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := []byte("QEMU\x00Standard PC\x00v1.0\x00\x00")
|
|
result := extractStrings(data)
|
|
// The trailing double-NUL produces a final empty string entry;
|
|
// the first three entries correspond to the actual firmware strings.
|
|
require.GreaterOrEqual(t, len(result), 3)
|
|
assert.Equal(t, "QEMU", result[0])
|
|
assert.Equal(t, "Standard PC", result[1])
|
|
assert.Equal(t, "v1.0", result[2])
|
|
}
|
|
|
|
func TestExtractStringsEmpty(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Empty strings between NULs must be preserved by position.
|
|
data := []byte("first\x00\x00third\x00\x00")
|
|
result := extractStrings(data)
|
|
require.GreaterOrEqual(t, len(result), 3)
|
|
assert.Equal(t, "first", result[0])
|
|
assert.Empty(t, result[1])
|
|
assert.Equal(t, "third", result[2])
|
|
}
|
|
|
|
func TestGetStringByIndex(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
strs := []string{"first", "second", "third"}
|
|
assert.Empty(t, getStringByIndex(strs, 0))
|
|
assert.Equal(t, "first", getStringByIndex(strs, 1))
|
|
assert.Equal(t, "second", getStringByIndex(strs, 2))
|
|
assert.Equal(t, "third", getStringByIndex(strs, 3))
|
|
assert.Empty(t, getStringByIndex(strs, 4))
|
|
}
|
|
|
|
func TestParseSMBIOSSystemInfo(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Build a minimal RSMB blob with a Type 1 structure.
|
|
// Header: 8 bytes (rawSMBIOSData).
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x03, // SMBIOSMajorVersion
|
|
0x02, // SMBIOSMinorVersion
|
|
0x00, // DMARevision
|
|
0x00, 0x00, 0x00, 0x00, // Length (placeholder)
|
|
}
|
|
|
|
// Type 1 formatted area: 0x19 bytes
|
|
type1 := make([]byte, 0x19)
|
|
type1[0] = 1 // Type
|
|
type1[1] = 0x19 // Length
|
|
// Handle (2 bytes) - don't care
|
|
type1[4] = 1 // Manufacturer string index
|
|
type1[5] = 2 // Product Name string index
|
|
type1[6] = 3 // Version string index
|
|
type1[7] = 4 // Serial Number string index
|
|
|
|
// UUID at offset 0x08: 16 bytes
|
|
// Store 12345678-1234-5678-9abc-def012345678 in SMBIOS LE format
|
|
copy(type1[0x08:], []byte{0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78})
|
|
|
|
// Strings area
|
|
strings := []byte("QEMU\x00Standard PC (Q35 + ICH9, 2009)\x00pc-q35-9.2\x00SN-12345\x00\x00")
|
|
|
|
// Combine
|
|
tableData := slices.Concat(type1, strings)
|
|
tableLen := len(tableData)
|
|
|
|
// Fill in length in header
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
data := slices.Concat(header, tableData)
|
|
|
|
info, err := parseSMBIOSInfo(data)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "QEMU", info.System.Manufacturer)
|
|
assert.Equal(t, "Standard PC (Q35 + ICH9, 2009)", info.System.ProductName)
|
|
assert.Equal(t, "pc-q35-9.2", info.System.Version)
|
|
assert.Equal(t, "SN-12345", info.System.SerialNumber)
|
|
assert.Equal(t, "12345678-1234-5678-9abc-def012345678", info.System.UUID)
|
|
}
|
|
|
|
func TestParseSMBIOSSystemInfoPre26(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// SMBIOS 2.5 header: UUID bytes in network (big-endian) order.
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x02, // SMBIOSMajorVersion = 2
|
|
0x05, // SMBIOSMinorVersion = 5 (pre-2.6)
|
|
0x00, // DMARevision
|
|
0x00, 0x00, 0x00, 0x00, // Length (placeholder)
|
|
}
|
|
|
|
type1 := make([]byte, 0x19)
|
|
type1[0] = 1 // Type
|
|
type1[1] = 0x19 // Length
|
|
type1[4] = 1 // Manufacturer string index
|
|
type1[5] = 2 // Product Name string index
|
|
type1[6] = 3 // Version string index
|
|
type1[7] = 4 // Serial Number string index
|
|
|
|
// UUID in network byte order: 12345678-1234-5678-9abc-def012345678
|
|
copy(type1[0x08:], []byte{0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78})
|
|
|
|
strs := []byte("QEMU\x00Standard PC\x00v1.0\x00SN-001\x00\x00")
|
|
|
|
tableData := slices.Concat(type1, strs)
|
|
tableLen := len(tableData)
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
data := slices.Concat(header, tableData)
|
|
|
|
info, err := parseSMBIOSInfo(data)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "12345678-1234-5678-9abc-def012345678", info.System.UUID)
|
|
assert.Equal(t, "QEMU", info.System.Manufacturer)
|
|
}
|
|
|
|
func TestParseSMBIOSSystemInfoSMBIOS20(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// SMBIOS 2.0 Type 1 structure: only 8 bytes (no UUID field).
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x02, // SMBIOSMajorVersion = 2
|
|
0x00, // SMBIOSMinorVersion = 0
|
|
0x00, // DMARevision
|
|
0x00, 0x00, 0x00, 0x00, // Length (placeholder)
|
|
}
|
|
|
|
type1 := make([]byte, 0x08)
|
|
type1[0] = 1 // Type
|
|
type1[1] = 0x08 // Length (8 bytes, no UUID)
|
|
type1[4] = 1 // Manufacturer string index
|
|
type1[5] = 2 // Product Name string index
|
|
type1[6] = 3 // Version string index
|
|
type1[7] = 4 // Serial Number string index
|
|
|
|
strs := []byte("OldBIOS\x00Legacy PC\x00v0.1\x00SN-OLD\x00\x00")
|
|
|
|
tableData := slices.Concat(type1, strs)
|
|
tableLen := len(tableData)
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
data := slices.Concat(header, tableData)
|
|
|
|
info, err := parseSMBIOSInfo(data)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "OldBIOS", info.System.Manufacturer)
|
|
assert.Equal(t, "Legacy PC", info.System.ProductName)
|
|
assert.Equal(t, "v0.1", info.System.Version)
|
|
assert.Equal(t, "SN-OLD", info.System.SerialNumber)
|
|
assert.Empty(t, info.System.UUID, "UUID should be empty for 8-byte SMBIOS 2.0 Type 1 structures")
|
|
}
|
|
|
|
func TestParseSMBIOSSystemInfoWithType0Preceding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Real firmware tables almost always have Type 0 (BIOS Information) before Type 1.
|
|
// Verify the walker correctly skips Type 0 and finds Type 1.
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x03, // SMBIOSMajorVersion
|
|
0x02, // SMBIOSMinorVersion
|
|
0x00, // DMARevision
|
|
0x00, 0x00, 0x00, 0x00, // Length (placeholder)
|
|
}
|
|
|
|
// Type 0 (BIOS Information): 0x12 bytes formatted area.
|
|
type0 := make([]byte, 0x12)
|
|
type0[0] = 0 // Type 0
|
|
type0[1] = 0x12 // Length
|
|
type0[4] = 1 // Vendor string index
|
|
type0[5] = 2 // BIOS Version string index
|
|
|
|
type0Strs := []byte("BIOS Vendor\x00BIOS v1.0\x00\x00")
|
|
|
|
// Type 1 (System Information): 0x19 bytes formatted area.
|
|
type1 := make([]byte, 0x19)
|
|
type1[0] = 1 // Type 1
|
|
type1[1] = 0x19 // Length
|
|
type1[4] = 1 // Manufacturer string index
|
|
type1[5] = 2 // Product Name string index
|
|
type1[6] = 3 // Version string index
|
|
type1[7] = 4 // Serial Number string index
|
|
|
|
copy(type1[0x08:], []byte{0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78})
|
|
|
|
type1Strs := []byte("Dell Inc.\x00PowerEdge R740\x00v2.0\x00SN-ABCDE\x00\x00")
|
|
|
|
tableData := slices.Concat(type0, type0Strs, type1, type1Strs)
|
|
tableLen := len(tableData)
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
data := slices.Concat(header, tableData)
|
|
|
|
info, err := parseSMBIOSInfo(data)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Dell Inc.", info.System.Manufacturer)
|
|
assert.Equal(t, "PowerEdge R740", info.System.ProductName)
|
|
assert.Equal(t, "v2.0", info.System.Version)
|
|
assert.Equal(t, "SN-ABCDE", info.System.SerialNumber)
|
|
assert.Equal(t, "12345678-1234-5678-9abc-def012345678", info.System.UUID)
|
|
}
|
|
|
|
func TestParseSMBIOSSystemInfoUnterminatedStrings(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Build a Type 1 structure whose string area lacks the double-NUL terminator.
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x03, // SMBIOSMajorVersion
|
|
0x02, // SMBIOSMinorVersion
|
|
0x00, // DMARevision
|
|
0x00, 0x00, 0x00, 0x00, // Length (placeholder)
|
|
}
|
|
|
|
type1 := make([]byte, 0x19)
|
|
type1[0] = 1 // Type
|
|
type1[1] = 0x19 // Length
|
|
type1[4] = 1 // Manufacturer string index
|
|
|
|
// String area with only a single NUL (missing the terminating double-NUL).
|
|
strs := []byte("QEMU\x00")
|
|
|
|
tableData := slices.Concat(type1, strs)
|
|
tableLen := len(tableData)
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
data := slices.Concat(header, tableData)
|
|
|
|
_, err := parseSMBIOSInfo(data)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not properly terminated")
|
|
}
|
|
|
|
// buildRSMB wraps SMBIOS structures in a RawSMBIOSData header for SMBIOS 3.2.
|
|
func buildRSMB(structures ...[]byte) []byte {
|
|
tableData := slices.Concat(structures...)
|
|
tableLen := len(tableData)
|
|
|
|
header := []byte{
|
|
0x00, // Used20CallingMethod
|
|
0x03, // SMBIOSMajorVersion
|
|
0x02, // SMBIOSMinorVersion
|
|
0x00, // DMARevision
|
|
byte(tableLen), byte(tableLen >> 8), byte(tableLen >> 16), byte(tableLen >> 24),
|
|
}
|
|
|
|
return slices.Concat(header, tableData)
|
|
}
|
|
|
|
// smbiosStruct builds one SMBIOS structure with a formatted area of length
|
|
// bytes, the given fields set and the given strings.
|
|
func smbiosStruct(structType byte, length int, fields map[int]byte, strs ...string) []byte {
|
|
formatted := make([]byte, length)
|
|
formatted[0] = structType
|
|
formatted[1] = byte(length)
|
|
|
|
for offset, value := range fields {
|
|
formatted[offset] = value
|
|
}
|
|
|
|
var stringArea []byte
|
|
|
|
for _, str := range strs {
|
|
stringArea = append(stringArea, str...)
|
|
stringArea = append(stringArea, 0)
|
|
}
|
|
|
|
if len(stringArea) == 0 {
|
|
stringArea = []byte{0}
|
|
}
|
|
|
|
return slices.Concat(formatted, stringArea, []byte{0})
|
|
}
|
|
|
|
func TestParseSMBIOSInfoAllTypes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type0 := smbiosStruct(0, 0x1A,
|
|
map[int]byte{0x04: 1, 0x05: 2, 0x08: 3, 0x14: 2, 0x15: 17},
|
|
"American Megatrends Inc.", "1.4.2", "05/23/2024",
|
|
)
|
|
type1 := smbiosStruct(1, 0x1B,
|
|
map[int]byte{0x04: 1, 0x05: 2, 0x06: 3, 0x07: 4, 0x19: 5, 0x1A: 6},
|
|
"Dell Inc.", "PowerEdge R740", "v2.0", "SN-SYS", "SKU=0715", "PowerEdge",
|
|
)
|
|
type2 := smbiosStruct(2, 0x0F,
|
|
map[int]byte{0x04: 1, 0x05: 2, 0x06: 3, 0x07: 4, 0x08: 5},
|
|
"Dell Inc.", "0WXD1Y", "A05", "SN-BOARD", "BOARD-TAG",
|
|
)
|
|
// A second baseboard must not override the first.
|
|
type2b := smbiosStruct(2, 0x0F, map[int]byte{0x04: 1, 0x05: 2}, "Other", "Second")
|
|
type3 := smbiosStruct(3, 0x15,
|
|
map[int]byte{0x04: 1, 0x05: 0x17, 0x06: 2, 0x07: 3, 0x08: 4},
|
|
"Dell Inc.", "C1", "SN-CHASSIS", "CHASSIS-TAG",
|
|
)
|
|
end := smbiosStruct(127, 4, nil)
|
|
|
|
info, err := parseSMBIOSInfo(buildRSMB(type0, end, type1, type2, type2b, type3))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, SMBIOSBIOSInfo{
|
|
Vendor: "American Megatrends Inc.",
|
|
Version: "1.4.2",
|
|
ReleaseDate: "05/23/2024",
|
|
Release: "2.17",
|
|
}, info.BIOS)
|
|
assert.Equal(t, "SKU=0715", info.System.SKUNumber)
|
|
assert.Equal(t, "PowerEdge", info.System.Family)
|
|
assert.Equal(t, "PowerEdge R740", info.System.ProductName)
|
|
assert.Equal(t, SMBIOSBoardInfo{
|
|
Manufacturer: "Dell Inc.",
|
|
Product: "0WXD1Y",
|
|
Version: "A05",
|
|
SerialNumber: "SN-BOARD",
|
|
AssetTag: "BOARD-TAG",
|
|
}, info.Board)
|
|
assert.Equal(t, SMBIOSChassisInfo{
|
|
Manufacturer: "Dell Inc.",
|
|
Version: "C1",
|
|
SerialNumber: "SN-CHASSIS",
|
|
AssetTag: "CHASSIS-TAG",
|
|
}, info.Chassis)
|
|
}
|
|
|
|
func TestParseSMBIOSInfoShortStructures(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// SMBIOS 2.0-sized structures: no BIOS release, SKU, family or asset tags.
|
|
type0 := smbiosStruct(0, 0x12, map[int]byte{0x04: 1, 0x05: 2, 0x08: 3}, "Vendor", "1.0", "01/01/2000")
|
|
type1 := smbiosStruct(1, 0x08, map[int]byte{0x04: 1}, "Maker")
|
|
type2 := smbiosStruct(2, 0x08, map[int]byte{0x04: 1, 0x05: 2, 0x06: 3, 0x07: 4}, "BV", "BN", "BVer", "BS")
|
|
type3 := smbiosStruct(3, 0x08, map[int]byte{0x04: 1, 0x06: 2, 0x07: 3}, "CV", "CVer", "CS")
|
|
|
|
info, err := parseSMBIOSInfo(buildRSMB(type0, type1, type2, type3))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "Vendor", info.BIOS.Vendor)
|
|
assert.Equal(t, "01/01/2000", info.BIOS.ReleaseDate)
|
|
assert.Empty(t, info.BIOS.Release)
|
|
assert.Empty(t, info.System.SKUNumber)
|
|
assert.Empty(t, info.System.Family)
|
|
assert.Equal(t, "BS", info.Board.SerialNumber)
|
|
assert.Empty(t, info.Board.AssetTag)
|
|
assert.Equal(t, "CS", info.Chassis.SerialNumber)
|
|
assert.Empty(t, info.Chassis.AssetTag)
|
|
}
|
|
|
|
func TestParseSMBIOSInfoBIOSReleaseNotSupported(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type0 := smbiosStruct(0, 0x18, map[int]byte{0x04: 1, 0x14: 0xFF, 0x15: 0xFF}, "Vendor")
|
|
type1 := smbiosStruct(1, 0x08, map[int]byte{0x04: 1}, "Maker")
|
|
|
|
info, err := parseSMBIOSInfo(buildRSMB(type0, type1))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, info.BIOS.Release)
|
|
}
|
|
|
|
func TestParseSMBIOSInfoBIOSReleaseMinorMissing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Length 0x15 covers the major release byte but not the minor one.
|
|
type0 := smbiosStruct(0, 0x15, map[int]byte{0x04: 1, 0x14: 2}, "Vendor")
|
|
type1 := smbiosStruct(1, 0x08, map[int]byte{0x04: 1}, "Maker")
|
|
|
|
info, err := parseSMBIOSInfo(buildRSMB(type0, type1))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, info.BIOS.Release)
|
|
}
|
|
|
|
func TestParseSMBIOSInfoMalformedBeforeSystem(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type0 := smbiosStruct(0, 0x12, map[int]byte{0x04: 1}, "Vendor")
|
|
broken := slices.Concat(smbiosStruct(2, 0x08, nil)[:0x08], []byte("Board\x00"))
|
|
|
|
_, err := parseSMBIOSInfo(buildRSMB(type0, broken))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not properly terminated")
|
|
}
|
|
|
|
func TestParseSMBIOSInfoMalformedAfterSystem(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// A structure with an unterminated string area after Type 1 must not
|
|
// discard the System Information that was already parsed.
|
|
type1 := smbiosStruct(1, 0x08, map[int]byte{0x04: 1}, "Maker")
|
|
broken := slices.Concat(smbiosStruct(2, 0x08, map[int]byte{0x04: 1})[:0x08], []byte("Board\x00"))
|
|
|
|
info, err := parseSMBIOSInfo(buildRSMB(type1, broken))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Maker", info.System.Manufacturer)
|
|
assert.Empty(t, info.Board.Manufacturer)
|
|
}
|
|
|
|
func TestParseSMBIOSInfoMissingSystem(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type0 := smbiosStruct(0, 0x12, map[int]byte{0x04: 1}, "Vendor")
|
|
|
|
_, err := parseSMBIOSInfo(buildRSMB(type0))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Type 1")
|
|
}
|
|
|
|
func FuzzParseSMBIOSInfo(f *testing.F) {
|
|
// Seed with a valid RSMB blob.
|
|
header := []byte{
|
|
0x00, 0x03, 0x02, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
}
|
|
|
|
type1 := make([]byte, 0x19)
|
|
type1[0] = 1
|
|
type1[1] = 0x19
|
|
type1[4] = 1
|
|
type1[5] = 2
|
|
type1[6] = 3
|
|
type1[7] = 4
|
|
|
|
copy(type1[0x08:], []byte{0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78})
|
|
|
|
strs := []byte("QEMU\x00Standard PC\x00v1.0\x00SN-001\x00\x00")
|
|
tableData := slices.Concat(type1, strs)
|
|
tableLen := len(tableData)
|
|
header[4] = byte(tableLen)
|
|
header[5] = byte(tableLen >> 8)
|
|
header[6] = byte(tableLen >> 16)
|
|
header[7] = byte(tableLen >> 24)
|
|
|
|
f.Add(slices.Concat(header, tableData))
|
|
|
|
// Seed with every parsed structure type and a partial BIOS release.
|
|
f.Add(buildRSMB(
|
|
smbiosStruct(0, 0x1A, map[int]byte{0x04: 1, 0x05: 2, 0x08: 3, 0x14: 2, 0x15: 17}, "Vendor", "1.0", "01/01/2024"),
|
|
smbiosStruct(127, 4, nil),
|
|
smbiosStruct(1, 0x1B, map[int]byte{0x04: 1, 0x19: 2, 0x1A: 3}, "Maker", "SKU", "Family"),
|
|
smbiosStruct(2, 0x0F, map[int]byte{0x04: 1, 0x08: 2}, "Board", "Tag"),
|
|
smbiosStruct(3, 0x15, map[int]byte{0x04: 1, 0x05: 0x17}, "Chassis"),
|
|
))
|
|
f.Add(buildRSMB(
|
|
smbiosStruct(0, 0x15, map[int]byte{0x04: 1, 0x14: 2}, "Vendor"),
|
|
smbiosStruct(1, 0x08, map[int]byte{0x04: 1}, "Maker"),
|
|
))
|
|
|
|
// Also seed with an empty input and a truncated one.
|
|
f.Add([]byte{})
|
|
f.Add(header[:4])
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// Must not panic on any input.
|
|
_, _ = parseSMBIOSInfo(data)
|
|
})
|
|
}
|