From e781237bb6d0b04cfb9d380bc36b552f5ee53af2 Mon Sep 17 00:00:00 2001 From: Mike Beaumont Date: Thu, 22 Aug 2024 19:47:12 +0200 Subject: [PATCH] fix: stop decoding without error if EOF encountered during header read Linux v6.6.39 stops decoding with EOF in this situation but maintains the already decoded structures. See /drivers/firmware/dmi_scan.c Fixes #16 Signed-off-by: Mike Beaumont Signed-off-by: Andrey Smirnov --- .../digitalocean/go-smbios/smbios/decoder.go | 19 +- .../go-smbios/smbios/decoder_test.go | 54 ++++- smbios/smbios_test.go | 1 + smbios/testdata/Beelink-EQ12.dmi | Bin 0 -> 4323 bytes smbios/testdata/Beelink-EQ12.json | 187 ++++++++++++++++++ 5 files changed, 252 insertions(+), 9 deletions(-) create mode 100644 smbios/testdata/Beelink-EQ12.dmi create mode 100644 smbios/testdata/Beelink-EQ12.json diff --git a/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder.go b/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder.go index deda0f9..6382f4b 100644 --- a/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder.go +++ b/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder.go @@ -18,6 +18,7 @@ import ( "bufio" "bytes" "encoding/binary" + "errors" "fmt" "io" ) @@ -99,11 +100,21 @@ func (d *Decoder) next() (*Structure, error) { return nil, err } + if h == nil { + // unexpected structure end, pretend we hit the end of the table + return &Structure{ + Header: Header{ + Type: typeEndOfTable, + }, + }, nil + } + // Length of formatted section is length specified by header, minus // the length of the header itself. l := int(h.Length) - headerLen - // Hyper-V SMBIOS tables seem to be broken, implement a workaround + // Some SMBIOS tables, for example Hyper-V, seem to be broken, implement a + // workaround if h.Length == 0 && h.Type == 0 { return &Structure{ Header: Header{ @@ -132,6 +143,12 @@ func (d *Decoder) next() (*Structure, error) { // parseHeader parses a Structure's Header from the stream. func (d *Decoder) parseHeader() (*Header, error) { if _, err := io.ReadFull(d.br, d.b[:headerLen]); err != nil { + // We do the same thing the kernel does in drivers/firmware/dmi_scan.c + // since v6.6.39 and return early if we don't have enough bytes for a header + if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) { + return nil, nil //nolint:nilnil + } + return nil, fmt.Errorf("error reading header: %w", err) } diff --git a/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder_test.go b/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder_test.go index d454cd8..1b64d57 100644 --- a/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder_test.go +++ b/smbios/internal/github.com/digitalocean/go-smbios/smbios/decoder_test.go @@ -33,6 +33,12 @@ func TestDecoder(t *testing.T) { { name: "short header", b: []byte{0x00}, + ss: []*smbios.Structure{{ + Header: smbios.Header{ + Type: 127, + }, + }}, + ok: true, }, { name: "length too short", @@ -49,14 +55,6 @@ func TestDecoder(t *testing.T) { 'a', 'b', 'c', 'd', }, }, - { - name: "no end of table", - b: []byte{ - 0x01, 0x04, 0x01, 0x00, - 0x00, - 0x00, - }, - }, { name: "bad second message", b: []byte{ @@ -67,6 +65,46 @@ func TestDecoder(t *testing.T) { 0xff, }, + ss: []*smbios.Structure{ + { + Header: smbios.Header{ + Type: 1, + Length: 12, + Handle: 2, + }, + Formatted: []uint8{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}, + Strings: []string{"deadbeef"}, + }, + { + Header: smbios.Header{ + Type: 127, + }, + }, + }, + ok: true, + }, + { + name: "no end of table", + b: []byte{ + 0x01, 0x04, 0x01, 0x00, + 0x00, + 0x00, + }, + ss: []*smbios.Structure{ + { + Header: smbios.Header{ + Type: 1, + Length: 4, + Handle: 1, + }, + }, + { + Header: smbios.Header{ + Type: 127, + }, + }, + }, + ok: true, }, { name: "OK, one, no format, no strings", diff --git a/smbios/smbios_test.go b/smbios/smbios_test.go index e8b3302..686e499 100644 --- a/smbios/smbios_test.go +++ b/smbios/smbios_test.go @@ -23,6 +23,7 @@ func TestDecode(t *testing.T) { "SuperMicro-Dual-Xeon", "SuperMicro-Quad-Opteron", "HyperV", + "Beelink-EQ12", } { t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/smbios/testdata/Beelink-EQ12.dmi b/smbios/testdata/Beelink-EQ12.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a1886b9ffe4173fe21f80328ea415e32fd6b8733 GIT binary patch literal 4323 zcmcgw-EY%Y6hDrgv}s!U0ferl^g_3_1==QQ(+~F5#7WzTrpe;8z%&Ut$t8_&ZR9vW z5f2#%P3$k2v=_vrX?xq=_O|^am3ZGCkoGc(jqF^zani(vR!F;6eC_)?U-#U5&pFOb zDLH$yILNz!gJ1jr1X|h<0Ox|2e69NJt9u-Re2;smRs)FaP`#9wRg$Dd+0dz4D3gS0 zP+gUclBUYaWwO35PQ%7hcq{U_lxocmw(zmNLO=3?`&)%;May~~u2#S53cAK=PxEFw&u`4AV1<2hUl zfM+-T|mfn9tjz?1LxnVLGG>ACe0la--sibD0kl(;Qp z=29Y4u|zT%fM{gnDhYt7SMnu1e_5K}Ry9Q{?m~QVCAzW_55dyn!hCpcHWWm%Y;b-x zJTK18OX2C{;%bl(0%-cAX}YDBrT6BEH5r4ZAGbA34<2HAlX-A*^rESthBeT5d)NoK zG1908-0G;OQYceUDPLj}cfRV)40Zxrww#7}rMZm(Uz3p{RvHfKFe?>}@ zXi1gzU9v^>GFDmudqdNJIkOv{23R!!a01IJG_Y37&|4HsGt6kBR-j~xi0MtJn9p_Y z?DZ^(h)Hs{A#LWMG_3iAz7RYR+N34pkc>y~+q}uRy{&EQ(7ojf)`~>Bx{0RF1ORuN zWh!slC@@n2i$JsUScZ34vu)Fu7S{lOQ(`6&Ba(p`s0^`ES zmg!y?!&(*#PqX=~uG_-e<|9Q=y|i04=njb#F^oeSNirjXNtPB9)HY6vIQyVJ-r|D# zaJvV#STuq}idd;c&Xtssu}k7AdXSQpYqrMp+6@w|l$3%!lHIPT1*+$;gqg@DC!2|z zr}S3I&@mhWuF6%hbRc+vAaqpGZ|t`oB%++(rs(D2Sw@6sjOE`#^f`fPDpCBs>aX&U-TkNNZ zxe@!x0s9Owb`XDdz?Ok~%2fW^z@$hv!qi2OjUC*V4aim(MU3s5zZ@_TF)w0&J76*3 zzGmuPH84rTGs5)6k&nfMe;SyrZv`