项目一览
这个项目是将 PNG、JPEG 和 GIF 编码的图形文件转换为 ZPL 兼容的 ^GF 元素,然后作者给出了实例代码:
package main
import (
"simonwaldherr.de/go/zplgfa"
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"log"
"os"
)
func main() {
// open file
file, err := os.Open("label.png")
if err != nil {
log.Printf("Warning: could not open the file: %s\n", err)
return
}
defer file.Close()
// load image head information
config, format, err := image.DecodeConfig(file)
if err != nil {
log.Printf("Warning: image not compatible, format: %s, config: %v, error: %s\n", format, config, err)
}
// reset file pointer to the beginning of the file
file.Seek(0, 0)
// load and decode image
img, _, err := image.Decode(file)
if err != nil {
log.Printf("Warning: could not decode the file, %s\n", err)
return
}
// flatten image
flat := zplgfa.FlattenImage(img)
// convert image to zpl compatible type
gfimg := zplgfa.ConvertToZPL(flat, zplgfa.CompressedASCII)
// output zpl with graphic field data to stdout
fmt.Println(gfimg)
}
整体跑一遍基本就能明白他在干什么。然后把代码改成 fuzz 的格式:
package test
import (
"bytes"
"fmt"
"image/gif"
"testing"
"simonwaldherr.de/go/zplgfa"
)
func FuzzReverse(f *testing.F) {
f.Fuzz(func(a *testing.T, data []byte) { //接收参数
// load and decode image
img, err := gif.Decode(bytes.NewReader(data))
if err != nil {
return
}
// flatten image
flat := zplgfa.FlattenImage(img)
// wid := flat.Bounds().Size().X / 8
// panic(wid)
// convert image to zpl compatible type
gfimg := zplgfa.ConvertToZPL(flat, zplgfa.CompressedASCII)
// output zpl with graphic field data to stdout
fmt.Println(gfimg)
})
}
直接跑一遍就有 bug 了:
❯ go test -fuzz=Fuzz
fuzz: elapsed: 2s, gathering baseline coverage: 0/1541 completed
fuzz: minimizing 220-byte failing input file
fuzz: elapsed: 2s, gathering baseline coverage: 434/1541 completed
--- FAIL: FuzzReverse (1.93s)
--- FAIL: FuzzReverse (0.00s)
testing.go:1485: panic: runtime error: index out of range [0] with length 0
goroutine 1254 [running]:
runtime/debug.Stack()
/usr/local/go/src/runtime/debug/stack.go:24 +0xbc
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1485 +0x264
panic({0x102b9e940, 0x14000016318})
/usr/local/go/src/runtime/panic.go:884 +0x204
simonwaldherr.de/go/zplgfa.ConvertToGraphicField({0x102bb0de8, 0x140000a8a00}, 0x2)
/Users/*/go/pkg/mod/simonwaldherr.de/go/zplgfa@v1.1.1/zplgfa.go:160 +0xb44
simonwaldherr.de/go/zplgfa.ConvertToZPL({0x102bb0de8?, 0x140000a8a00?}, 0x140000d9748?)
/Users/*/go/pkg/mod/simonwaldherr.de/go/zplgfa@v1.1.1/zplgfa.go:31 +0x50
test.FuzzReverse.func1(0x140000d9718?, {0x14000026700, 0x1f, 0x80})
/Users/*/Desktop/src/cve/m_test.go:27 +0x130
reflect.Value.call({0x102b7c560?, 0x102baeca0?, 0x14000544e38?}, {0x102b27556, 0x4}, {0x1400009b8f0, 0x2, 0x0?})
/usr/local/go/src/reflect/value.go:586 +0x87c
reflect.Value.Call({0x102b7c560?, 0x102baeca0?, 0x0?}, {0x1400009b8f0?, 0x102bade20?, 0x102c781f8?})
/usr/local/go/src/reflect/value.go:370 +0x90
testing.(*F).Fuzz.func1.1(0x0?)
/usr/local/go/src/testing/fuzz.go:335 +0x360
testing.tRunner(0x1400010b040, 0x1400007ad80)
/usr/local/go/src/testing/testing.go:1576 +0x10c
created by testing.(*F).Fuzz.func1
/usr/local/go/src/testing/fuzz.go:322 +0x4c4
Failing input written to testdata/fuzz/FuzzReverse/3e80f54c43de28a6
To re-run:
go test -run=FuzzReverse/3e80f54c43de28a6
FAIL
exit status 1
FAIL test 2.559s
进代码里看看:发现在 ConvertToGraphicField
函数中,使用 image
库的 Bounds().Size()
获取了图片的长宽信息,但是事实上,这里是可以伪造的
可以看到这里的宽度就是0,之后顺着代码往下读,发现在下边的for循环中,line是根据宽度make出来的数组,而之后求currentByte时候更是直接求了索引为0的byte,这显然是个索引越界
func ConvertToGraphicField(source image.Image, graphicType GraphicType) string {
var gfType string
var lastLine string
size := source.Bounds().Size()
width := size.X / 8
height := size.Y
if size.Y%8 != 0 {
width = width + 1
}
var GraphicFieldData string
for y := 0; y < size.Y; y++ {
line := make([]uint8, width)
lineIndex := 0
index := uint8(0)
currentByte := line[lineIndex]//line[0]!
修复
我的修复思路就是越早处理越好,顺着调用链找,看看哪里返回是最合适的:其实这里就可以直接返回了(我认为
func ConvertToZPL(img image.Image, graphicType GraphicType) string {
wid := img.Bounds().Size().X / 8
if wid == 0 {
return ""
}
return fmt.Sprintf("^XA,^FS\n^FO0,0\n%s^FS,^XZ\n", ConvertToGraphicField(img, graphicType))
}
因为宽度为零的话事实上已经可以直接把空字符串返回了,之所以没有返回一个 err 是因为这个师傅写的代码,整体都没用到 err,变参数对整体影响太大了,而且返回""
也是合理的,因为图片本身宽度就是 0,0 确实可以代表没有图片信息。
师傅果断进行了合并: