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
+100 -13
View File
@@ -17,6 +17,8 @@ import (
"github.com/redis/go-redis/v9/internal/pool"
"github.com/redis/go-redis/v9/internal/rand"
"github.com/redis/go-redis/v9/internal/util"
"github.com/redis/go-redis/v9/maintnotifications"
"github.com/redis/go-redis/v9/push"
)
//------------------------------------------------------------------------------
@@ -62,6 +64,8 @@ type FailoverOptions struct {
Protocol int
Username string
Password string
// Push notifications are always enabled for RESP3 connections
// CredentialsProvider allows the username and password to be updated
// before reconnecting. It should return the current username and password.
CredentialsProvider func() (username string, password string)
@@ -136,6 +140,15 @@ type FailoverOptions struct {
FailingTimeoutSeconds int
UnstableResp3 bool
// MaintNotificationsConfig is not supported for FailoverClients at the moment
// MaintNotificationsConfig provides custom configuration for maintnotifications upgrades.
// When MaintNotificationsConfig.Mode is not "disabled", the client will handle
// upgrade notifications gracefully and manage connection/pool state transitions
// seamlessly. Requires Protocol: 3 (RESP3) for push notifications.
// If nil, maintnotifications upgrades are disabled.
// (however if Mode is nil, it defaults to "auto" - enable if server supports it)
//MaintNotificationsConfig *maintnotifications.Config
}
func (opt *FailoverOptions) clientOptions() *Options {
@@ -182,6 +195,10 @@ func (opt *FailoverOptions) clientOptions() *Options {
IdentitySuffix: opt.IdentitySuffix,
UnstableResp3: opt.UnstableResp3,
MaintNotificationsConfig: &maintnotifications.Config{
Mode: maintnotifications.ModeDisabled,
},
}
}
@@ -226,6 +243,10 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
IdentitySuffix: opt.IdentitySuffix,
UnstableResp3: opt.UnstableResp3,
MaintNotificationsConfig: &maintnotifications.Config{
Mode: maintnotifications.ModeDisabled,
},
}
}
@@ -275,6 +296,10 @@ func (opt *FailoverOptions) clusterOptions() *ClusterOptions {
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
FailingTimeoutSeconds: opt.FailingTimeoutSeconds,
MaintNotificationsConfig: &maintnotifications.Config{
Mode: maintnotifications.ModeDisabled,
},
}
}
@@ -454,8 +479,6 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
opt.Dialer = masterReplicaDialer(failover)
opt.init()
var connPool *pool.ConnPool
rdb := &Client{
baseClient: &baseClient{
opt: opt,
@@ -463,15 +486,29 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
}
rdb.init()
connPool = newConnPool(opt, rdb.dialHook)
rdb.connPool = connPool
// Initialize push notification processor using shared helper
// Use void processor by default for RESP2 connections
rdb.pushProcessor = initializePushProcessor(opt)
var err error
rdb.connPool, err = newConnPool(opt, rdb.dialHook)
if err != nil {
panic(fmt.Errorf("redis: failed to create connection pool: %w", err))
}
rdb.pubSubPool, err = newPubSubPool(opt, rdb.dialHook)
if err != nil {
panic(fmt.Errorf("redis: failed to create pubsub pool: %w", err))
}
rdb.onClose = rdb.wrappedOnClose(failover.Close)
failover.mu.Lock()
failover.onFailover = func(ctx context.Context, addr string) {
_ = connPool.Filter(func(cn *pool.Conn) bool {
return cn.RemoteAddr().String() != addr
})
if connPool, ok := rdb.connPool.(*pool.ConnPool); ok {
_ = connPool.Filter(func(cn *pool.Conn) bool {
return cn.RemoteAddr().String() != addr
})
}
}
failover.mu.Unlock()
@@ -529,15 +566,40 @@ func NewSentinelClient(opt *Options) *SentinelClient {
},
}
// Initialize push notification processor using shared helper
// Use void processor for Sentinel clients
c.pushProcessor = NewVoidPushNotificationProcessor()
c.initHooks(hooks{
dial: c.baseClient.dial,
process: c.baseClient.process,
})
c.connPool = newConnPool(opt, c.dialHook)
var err error
c.connPool, err = newConnPool(opt, c.dialHook)
if err != nil {
panic(fmt.Errorf("redis: failed to create connection pool: %w", err))
}
c.pubSubPool, err = newPubSubPool(opt, c.dialHook)
if err != nil {
panic(fmt.Errorf("redis: failed to create pubsub pool: %w", err))
}
return c
}
// GetPushNotificationHandler returns the handler for a specific push notification name.
// Returns nil if no handler is registered for the given name.
func (c *SentinelClient) GetPushNotificationHandler(pushNotificationName string) push.NotificationHandler {
return c.pushProcessor.GetHandler(pushNotificationName)
}
// RegisterPushNotificationHandler registers a handler for a specific push notification name.
// Returns an error if a handler is already registered for this push notification name.
// If protected is true, the handler cannot be unregistered.
func (c *SentinelClient) RegisterPushNotificationHandler(pushNotificationName string, handler push.NotificationHandler, protected bool) error {
return c.pushProcessor.RegisterHandler(pushNotificationName, handler, protected)
}
func (c *SentinelClient) Process(ctx context.Context, cmd Cmder) error {
err := c.processHook(ctx, cmd)
cmd.SetErr(err)
@@ -547,13 +609,31 @@ func (c *SentinelClient) Process(ctx context.Context, cmd Cmder) error {
func (c *SentinelClient) pubSub() *PubSub {
pubsub := &PubSub{
opt: c.opt,
newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) {
return c.newConn(ctx)
newConn: func(ctx context.Context, addr string, channels []string) (*pool.Conn, error) {
cn, err := c.pubSubPool.NewConn(ctx, c.opt.Network, addr, channels)
if err != nil {
return nil, err
}
// will return nil if already initialized
err = c.initConn(ctx, cn)
if err != nil {
_ = cn.Close()
return nil, err
}
// Track connection in PubSubPool
c.pubSubPool.TrackConn(cn)
return cn, nil
},
closeConn: c.connPool.CloseConn,
closeConn: func(cn *pool.Conn) error {
// Untrack connection from PubSubPool
c.pubSubPool.UntrackConn(cn)
_ = cn.Close()
return nil
},
pushProcessor: c.pushProcessor,
}
pubsub.init()
return pubsub
}
@@ -776,6 +856,11 @@ func (c *sentinelFailover) MasterAddr(ctx context.Context) (string, error) {
}
}
// short circuit if no sentinels configured
if len(c.sentinelAddrs) == 0 {
return "", errors.New("redis: no sentinels configured")
}
var (
masterAddr string
wg sync.WaitGroup
@@ -823,10 +908,12 @@ func (c *sentinelFailover) MasterAddr(ctx context.Context) (string, error) {
}
func joinErrors(errs []error) string {
if len(errs) == 0 {
return ""
}
if len(errs) == 1 {
return errs[0].Error()
}
b := []byte(errs[0].Error())
for _, err := range errs[1:] {
b = append(b, '\n')