Size computation for allocation may overflow (#99)

* Size computation for allocation may overflow

Performing calculations involving the size of potentially large strings or slices can result in an overflow (for signed integer types) or a wraparound (for unsigned types). An overflow causes the result of the calculation to become negative, while a wraparound results in a small (positive) number.
This commit is contained in:
2025-12-08 11:22:28 +00:00
committed by GitHub
parent 56051779ee
commit a750c4f5b9
93 changed files with 10500 additions and 443 deletions
+89 -2
View File
@@ -50,7 +50,8 @@ func (e RedisError) Error() string { return string(e) }
func (RedisError) RedisError() {}
func ParseErrorReply(line []byte) error {
return RedisError(line[1:])
msg := string(line[1:])
return parseTypedRedisError(msg)
}
//------------------------------------------------------------------------------
@@ -99,6 +100,92 @@ func (r *Reader) PeekReplyType() (byte, error) {
return b[0], nil
}
func (r *Reader) PeekPushNotificationName() (string, error) {
// "prime" the buffer by peeking at the next byte
c, err := r.Peek(1)
if err != nil {
return "", err
}
if c[0] != RespPush {
return "", fmt.Errorf("redis: can't peek push notification name, next reply is not a push notification")
}
// peek 36 bytes at most, should be enough to read the push notification name
toPeek := 36
buffered := r.Buffered()
if buffered == 0 {
return "", fmt.Errorf("redis: can't peek push notification name, no data available")
}
if buffered < toPeek {
toPeek = buffered
}
buf, err := r.rd.Peek(toPeek)
if err != nil {
return "", err
}
if buf[0] != RespPush {
return "", fmt.Errorf("redis: can't parse push notification: %q", buf)
}
if len(buf) < 3 {
return "", fmt.Errorf("redis: can't parse push notification: %q", buf)
}
// remove push notification type
buf = buf[1:]
// remove first line - e.g. >2\r\n
for i := 0; i < len(buf)-1; i++ {
if buf[i] == '\r' && buf[i+1] == '\n' {
buf = buf[i+2:]
break
} else {
if buf[i] < '0' || buf[i] > '9' {
return "", fmt.Errorf("redis: can't parse push notification: %q", buf)
}
}
}
if len(buf) < 2 {
return "", fmt.Errorf("redis: can't parse push notification: %q", buf)
}
// next line should be $<length><string>\r\n or +<length><string>\r\n
// should have the type of the push notification name and it's length
if buf[0] != RespString && buf[0] != RespStatus {
return "", fmt.Errorf("redis: can't parse push notification name: %q", buf)
}
typeOfName := buf[0]
// remove the type of the push notification name
buf = buf[1:]
if typeOfName == RespString {
// remove the length of the string
if len(buf) < 2 {
return "", fmt.Errorf("redis: can't parse push notification name: %q", buf)
}
for i := 0; i < len(buf)-1; i++ {
if buf[i] == '\r' && buf[i+1] == '\n' {
buf = buf[i+2:]
break
} else {
if buf[i] < '0' || buf[i] > '9' {
return "", fmt.Errorf("redis: can't parse push notification name: %q", buf)
}
}
}
}
if len(buf) < 2 {
return "", fmt.Errorf("redis: can't parse push notification name: %q", buf)
}
// keep only the notification name
for i := 0; i < len(buf)-1; i++ {
if buf[i] == '\r' && buf[i+1] == '\n' {
buf = buf[:i]
break
}
}
return util.BytesToString(buf), nil
}
// ReadLine Return a valid reply, it will check the protocol or redis error,
// and discard the attribute type.
func (r *Reader) ReadLine() ([]byte, error) {
@@ -115,7 +202,7 @@ func (r *Reader) ReadLine() ([]byte, error) {
var blobErr string
blobErr, err = r.readStringReply(line)
if err == nil {
err = RedisError(blobErr)
err = parseTypedRedisError(blobErr)
}
return nil, err
case RespAttr: