我有一台服务器,它具有返回用户注册日期的功能。任何用户都可以看到任何非隐藏用户(在此示例中,只隐藏了user2)。服务器从客户端获取dateRequest
并使用id
来查找用户文件中相应用户的日期:
package main
import (
"bytes"
"fmt"
)
const datelen = 10
//format: `name 0xfe date age 0xfd password`; each user is seperated by 0xff
var userfile = []byte("admin\xfe2014-01-0140\xfdadminpassword\xffuser1\xfe2014-03-0423\xfduser1password\xffuser2\xfe2014-09-2736\xfduser2password")
func main() {
c := clientInfo{0, 0, 0}
fmt.Println(string(getDate(dateRequest{c, user{0, "admin"}})))
fmt.Println(string(getDate(dateRequest{c, user{0, "admin______________"}})))
fmt.Println(string(getDate(dateRequest{c, user{1, "user1"}})))
//fmt.Println(string(getDate(dateRequest{c,user{2,"user2"}}))) // panic
fmt.Println(string(getDate(dateRequest{c, user{1, "user1_________________________________________________"}})))
}
func getDate(r dateRequest) []byte {
if r.id == 2 {
panic("hidden user")
}
user := bytes.Split(userfile, []byte{0xff})[r.id]
publicSection := bytes.Split(user, []byte{0xfd})[0]
return publicSection[len(r.username)+1 : len(r.username)+1+datelen]
}
type dateRequest struct {
clientInfo
user
}
type clientInfo struct {
reqTime uint64
ip uint32
ver uint32
}
type user struct {
id int
username string
}
$ go run a.go
2014-01-01
dminpasswo
2014-03-04
r2password
正如您所看到的,它可以正常接收用户的日期,但如果请求在用户名上有额外的字节,则会返回部分用户密码。不仅如此,如果你继续添加更多字节,它将从user2返回数据,这应该是隐藏的。为什么呢?
执行main
的第三行代码时,user
中的publicSection
和getData
为"admin�2014-01-0140� ADMINPASSWORD"和"admin�2014-01-0140"。但随后又返回了#d;; dminpasswo"。如何切换publicSection
("admin�2014-01-0140")返回" dminpasswo"?它看起来像是一个缓冲区溢出问题,但这不应该发生,因为Go是内存安全的。我甚至尝试通过打印publicSection
读取publicSection[len(publicSection)]
的缓冲区,但它会按预期发生恐慌。
我还尝试将所有[]byte
替换为string
,并且由于某种原因解决了这个问题。
答案 0 :(得分:6)
切片表达式检查上部索引的边界与切片容量的关系,而不仅仅是切片的长度。实际上,您可以切片超过切片的长度。但是,您不能在基础数组的边界之外切片以访问未初始化的内存。
http://play.golang.org/p/oIxXLG-YEV
s := make([]int, 5, 10)
copy(s, []int{1, 2, 3, 4, 5})
fmt.Printf("len:%d cap:%d\n", len(s), cap(s))
// > len:5 cap:10
fmt.Printf("raw slice: %+v\n", s)
// > raw slice: [1 2 3 4 5]
fmt.Printf("sliced past length: %+v\n", s[:10])
// > sliced past length: [1 2 3 4 5 0 0 0 0 0]
// panics
_ = s[:11]
// > panic: runtime error: slice bounds out of range
如果你真的想要防止切片超过数组的长度,在go1.3或更高版本中,你可以在切片时将容量设置为第三个参数。
// set the capacity to 5
s := s[:5:5]
// now this will panic
_ = s[:6]