I have a server that has a function to return the registration date of a user. Any user can see any non-hidden user (in this example only user2 is hidden). The server gets a dateRequest
from a client and uses id
from that to find the corresponding user's date in the user file:
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
As you can see, it works correctly to receive the dates of the users, but if the request has extra bytes on the username, part of the user's password gets returned instead. Not only that, but if you keep adding more bytes, it returns data from user2, which is supposed to be hidden. Why?
When executing main
's third line of code, user
and publicSection
in getData
are "admin�2014-01-0140�adminpassword" and "admin�2014-01-0140". But then it returns "dminpasswo". How is slicing publicSection
("admin�2014-01-0140") returning "dminpasswo"? It looks like a buffer overrun problem, but that shouldn't happen because Go is memory-safe. I even tried reading past the buffer of publicSection
by printing publicSection[len(publicSection)]
, but it panics as expected.
I also tried replacing all the []byte
with string
, and that fixes the issue for some reason.
Slice expressions check the bounds of the upper index against the slice capacity, not just the length of the slice. In effect, you can slice past the length of the slice. You cannot however slice outside the bounds of the underlying array to access uninitialized memory.
http://play.golang.org/p/oIxXLG-YEV
If you really want to prevent slicing past the length of an array, in go1.3 or later you can set the capacity as the third argument when slicing.