什么叫内容泄露
内容泄露指的是:使用buffer值的一方通过某种非标准的方式,得到了本不该得到的内容
存在的场景和导致的原因
场景
- 通过调用Buffer值的某个用于读取内容的方法,得到一部分未读内容 A
- 当Buffer值又有一些新的内容之后,可通过上一步得到的结果值 A 直接获取新的内容
原因
- 通过切片可以直接访问和操纵它的底层数据
- 不论切片是基于某个数组得到的,还是通过另一个切片操作来获得的,上一结论都成立
bytes 例子
代码
package mainimport ("bytes""fmt")func main() {contents := "ab"buffer1 := bytes.NewBufferString(contents)fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap()) // 8fmt.Println()unreadBytes := buffer1.Bytes()fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // [97, 98]fmt.Println()contents = "cdefg"fmt.Printf("Write contents %q ...\n", contents)buffer1.WriteString(contents)fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())fmt.Println()unreadBytes = unreadBytes[:cap(unreadBytes)]fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // [97 98 99 100 101 102 103 0]fmt.Println()value := byte('X')fmt.Printf("Set a byte in the unread bytes to %v ...\n", value)unreadBytes[len(unreadBytes) - 2] = valuefmt.Printf("The unread bytes of the buffer: %v\n", buffer1.Bytes()) // 发生内容泄露 [97 98 99 100 101 102 88]fmt.Println()contents = "hijklmn"fmt.Printf("Write contents %q ..\n", contents)buffer1.WriteString(contents)fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap()) // 23fmt.Println()unreadBytes = unreadBytes[:cap(unreadBytes)]// 扩容代码就只能再创建一个新的内容容器,并把原有容器中的未读内容拷贝进去,最后再用新的容器替换掉原有的容器fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // The unread bytes of the buffer: [97 98 99 100 101 102 88 0]fmt.Println()}
解析
上述代码在写入 “hijklmn” 后,获取的unreadBytes还是与写入前一样?
先要了解 bytes.Buffer 的扩容策略:
- Buffer 即可以手动扩容,也可以自动扩容
- 如果当前容量的一半,仍然大于或等于其现有长度再加上另需字节数的和,将未读内容拷贝到它的头部位置(即原未读内容和新内容将已读内容覆盖掉)
- 如果当前内容容器的容量小于新长度的二倍,扩容策略会建立一个新的内容容器,并把原有容器中的未读内容拷贝进去,最后用新的容器代替原有的容器
根据bytes.Buffer的扩容策略,上述切片访问和操纵它的底层数据仍然是原容器的内容。
Bufio 示例
代码
package mainimport ("bufio""fmt""strings")func main() {comment := "Package bufio implements buffered I/O. " +"It wraps an io.Reader or io.Writer object, " +"creating another object (Reader or Writer) that " +"also implements the interface but provides buffering and " +"some help for textual I/O."basicReader := strings.NewReader(comment)fmt.Printf("The size of basic reader: %d\n", basicReader.Size()) // 213size := len(comment)fmt.Printf("New a buffered reader with size %d ...\n", size) // 213reader4 := bufio.NewReaderSize(basicReader, size)fmt.Println()peekNum := 7fmt.Printf("Peek %d bytes ...\n", peekNum) // 7bytes, err := reader4.Peek(peekNum)if err != nil {fmt.Printf("error: %v\n", err)}fmt.Printf("Peeked contents(%d): %q\n", len(bytes), bytes) // Peeked contents(7): "Package"fmt.Println()bytes = bytes[:cap(bytes)] // 存在内容泄露fmt.Printf("The all of the contents in the buffer:\n%q\n", bytes) // 获取到所有的值fmt.Println()blank := byte(' ') // 存在内容泄露,通过bytes值而修改到原bufio值fmt.Printf("Set blanks into the contents in the buffer ...")for _, i := range []int{55, 65, 57, 58, 66, 67, 68} {bytes[i] = blank}fmt.Println()peekNum = sizefmt.Printf("Peek %d bytes ...\n", peekNum)bytes, err = reader4.Peek(peekNum)if err != nil {fmt.Printf("error: %v\n", err)}fmt.Printf("Peeked contents(%d):\n%q\n", len(bytes), bytes) //"Package bufio implements buffered I/O. It wraps an io.R a r or i iter object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O."fmt.Println()}
解析
- 通过bufio包的peek函数读取到的是切片, 切片的底层是bufio的值
- 通过切片操作底层bufio的值
- bufio 类型中的Peek方法、ReadSlice方法和ReadLine方法都存在类似的问题
