diff --git a/frontend/docker/plugin.go b/frontend/docker/plugin.go index f02daf80d..3dd5c9f81 100644 --- a/frontend/docker/plugin.go +++ b/frontend/docker/plugin.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "encoding/json" "fmt" + "math" "os" "os/exec" "path/filepath" @@ -289,6 +290,9 @@ func (p *Plugin) Create(request *volume.CreateRequest) error { sizeBytes, err := utils.GetVolumeSizeBytes(ctx, request.Options, "0") if err != nil { return fmt.Errorf("error creating volume: %v", err) + } else if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") } delete(request.Options, "size") diff --git a/storage_drivers/azure/azure_anf.go b/storage_drivers/azure/azure_anf.go index 6372f6c83..4eacaf9d8 100644 --- a/storage_drivers/azure/azure_anf.go +++ b/storage_drivers/azure/azure_anf.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "fmt" + "math" "net" "os" "reflect" @@ -200,10 +201,15 @@ func (d *NASStorageDriver) Initialize( volumeCreateTimeout := d.defaultCreateTimeout() if config.VolumeCreateTimeout != "" { - if i, parseErr := strconv.ParseUint(d.Config.VolumeCreateTimeout, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.VolumeCreateTimeout, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(parseErr).Error( "Invalid volume create timeout period.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(parseErr).Error( + "Unsupported volume create timeout period.") + return errors.UnsupportedError("unsupported volume create timeout period") } else { volumeCreateTimeout = time.Duration(i) * time.Second } @@ -559,10 +565,15 @@ func (d *NASStorageDriver) initializeAzureSDKClient( sdkTimeout := api.DefaultSDKTimeout if config.SDKTimeout != "" { - if i, parseErr := strconv.ParseUint(d.Config.SDKTimeout, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.SDKTimeout, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.SDKTimeout).WithError(parseErr).Error( "Invalid value for SDK timeout.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.SDKTimeout).WithError(parseErr).Error( + "Unsupported value for SDK timeout.") + return errors.UnsupportedError("unsupported SDK timeout") } else { sdkTimeout = time.Duration(i) * time.Second } @@ -570,10 +581,15 @@ func (d *NASStorageDriver) initializeAzureSDKClient( maxCacheAge := api.DefaultMaxCacheAge if config.MaxCacheAge != "" { - if i, parseErr := strconv.ParseUint(d.Config.MaxCacheAge, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.MaxCacheAge, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.MaxCacheAge).WithError(parseErr).Error( "Invalid value for max cache age.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.MaxCacheAge).WithError(parseErr).Error( + "Unsupported value for max cache age.") + return errors.UnsupportedError("unsupported max cache age") } else { maxCacheAge = time.Duration(i) * time.Second } @@ -1912,6 +1928,11 @@ func (d *NASStorageDriver) Resize(ctx context.Context, volConfig *storage.Volume Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> Resize") defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Resize") + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") + } + // Update resource cache as needed if err := d.SDK.RefreshAzureResources(ctx); err != nil { return fmt.Errorf("could not update ANF resource cache; %v", err) diff --git a/storage_drivers/azure/azure_anf_subvolume.go b/storage_drivers/azure/azure_anf_subvolume.go index cb92f59ac..08b3bea5e 100644 --- a/storage_drivers/azure/azure_anf_subvolume.go +++ b/storage_drivers/azure/azure_anf_subvolume.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "math" "os" "reflect" "regexp" @@ -290,10 +291,15 @@ func (d *NASBlockStorageDriver) Initialize( volumeCreateTimeout := d.defaultCreateTimeout() if config.VolumeCreateTimeout != "" { - if i, parseErr := strconv.ParseUint(d.Config.VolumeCreateTimeout, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.VolumeCreateTimeout, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(parseErr).Error( "Invalid volume create timeout period.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(parseErr).Error( + "Unsupported volume create timeout period.") + return errors.UnsupportedError("unsupported volume create timeout period") } else { volumeCreateTimeout = time.Duration(i) * time.Second } @@ -548,10 +554,15 @@ func (d *NASBlockStorageDriver) initializeAzureSDKClient( sdkTimeout := api.DefaultSDKTimeout if config.SDKTimeout != "" { - if i, parseErr := strconv.ParseUint(d.Config.SDKTimeout, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.SDKTimeout, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.SDKTimeout).WithError(parseErr).Error( "Invalid value for SDK timeout.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.SDKTimeout).WithError(parseErr).Error( + "Unsupported value for SDK timeout.") + return errors.UnsupportedError("unsupported SDK timeout") } else { sdkTimeout = time.Duration(i) * time.Second } @@ -559,10 +570,15 @@ func (d *NASBlockStorageDriver) initializeAzureSDKClient( maxCacheAge := api.DefaultMaxCacheAge if config.MaxCacheAge != "" { - if i, parseErr := strconv.ParseUint(d.Config.MaxCacheAge, 10, 64); parseErr != nil { + i, parseErr := strconv.ParseInt(d.Config.MaxCacheAge, 10, 64) + if parseErr != nil { Logc(ctx).WithField("interval", d.Config.MaxCacheAge).WithError(parseErr).Error( "Invalid value for max cache age.") return parseErr + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.MaxCacheAge).WithError(parseErr).Error( + "Unsupported value for max cache age.") + return errors.UnsupportedError("unsupported max cache age") } else { maxCacheAge = time.Duration(i) * time.Second } @@ -1686,6 +1702,11 @@ func (d *NASBlockStorageDriver) Resize(ctx context.Context, volConfig *storage.V Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> Resize") defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Resize") + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") + } + // Get the subvolume subvolumeWithMetadata, err := d.SDK.Subvolume(ctx, volConfig, true) if err != nil { diff --git a/storage_drivers/azure/azure_anf_subvolume_test.go b/storage_drivers/azure/azure_anf_subvolume_test.go index a36520aab..c5eef8bcb 100644 --- a/storage_drivers/azure/azure_anf_subvolume_test.go +++ b/storage_drivers/azure/azure_anf_subvolume_test.go @@ -556,6 +556,37 @@ func TestSubvolumeInitialize_InvalidSDKTimeout(t *testing.T) { assert.False(t, driver.Initialized(), "initialized") } +func TestSubvolumeInitialize_NegativeVolumeCreateTimeout(t *testing.T) { + commonConfig, filesystems := getStructsForSubvolumeInitialize() + + configJSON := ` + { + "version": 1, + "storageDriverName": "azure-netapp-files-subvolume", + "location": "fake-location", + "subscriptionID": "deadbeef-173f-4bf4-b5b8-f17f8d2fe43b", + "tenantID": "deadbeef-4746-4444-a919-3b34af5f0a3c", + "clientID": "deadbeef-784c-4b35-8329-460f52a3ad50", + "clientSecret": "myClientSecret", + "serviceLevel": "Premium", + "debugTraceFlags": {"method": true, "api": true, "discovery": true}, + "capacityPools": ["RG1/NA1/CP1", "RG1/NA1/CP2"], + "filePoolVolumes": ["RG1/NA1/CP1/VOL-1"], + "virtualNetwork": "VN1", + "subnet": "RG1/VN1/SN1", + "volumeCreateTimeout": "-600" + }` + + mockAPI, driver := newMockANFSubvolumeDriver(t) + mockAPI.EXPECT().ValidateFilePoolVolumes(ctx, gomock.Any()).Return(filesystems, nil).Times(1) + mockAPI.EXPECT().Init(ctx, gomock.Any()).Return(nil).Times(1) + result := driver.Initialize(ctx, tridentconfig.ContextCSI, configJSON, commonConfig, map[string]string{}, + BackendUUID) + + assert.Error(t, result, "initialized") + assert.False(t, driver.Initialized(), "initialized") +} + func TestSubvolumeInitialize_InvalidVolumeCreateTimeout(t *testing.T) { commonConfig, filesystems := getStructsForSubvolumeInitialize() diff --git a/storage_drivers/azure/azure_anf_test.go b/storage_drivers/azure/azure_anf_test.go index f1ef75018..61d767c69 100644 --- a/storage_drivers/azure/azure_anf_test.go +++ b/storage_drivers/azure/azure_anf_test.go @@ -757,6 +757,52 @@ func TestInitialize_InvalidStoragePrefix(t *testing.T) { assert.False(t, driver.Initialized(), "initialized") } +func TestInitialize_NegativeVolumeCreateTimeout(t *testing.T) { + commonConfig := &drivers.CommonStorageDriverConfig{ + Version: 1, + StorageDriverName: "azure-netapp-files", + BackendName: "myANFBackend", + DriverContext: tridentconfig.ContextCSI, + DebugTraceFlags: debugTraceFlags, + } + + configJSON := ` + { + "version": 1, + "storageDriverName": "azure-netapp-files", + "location": "fake-location", + "subscriptionID": "deadbeef-173f-4bf4-b5b8-f17f8d2fe43b", + "tenantID": "deadbeef-4746-4444-a919-3b34af5f0a3c", + "clientID": "deadbeef-784c-4b35-8329-460f52a3ad50", + "clientSecret": "myClientSecret", + "serviceLevel": "Premium", + "debugTraceFlags": {"method": true, "api": true, "discovery": true}, + "capacityPools": ["RG1/NA1/CP1", "RG1/NA1/CP2"], + "virtualNetwork": "VN1", + "subnet": "RG1/VN1/SN1", + "volumeCreateTimeout": "-600" + }` + + // Have to at least one CapacityPool for ANF backends. + pool := &api.CapacityPool{ + Name: "CP1", + Location: "fake-location", + NetAppAccount: "NA1", + ResourceGroup: "RG1", + } + + mockAPI, driver := newMockANFDriver(t) + + mockAPI.EXPECT().Init(ctx, gomock.Any()).Return(nil).Times(1) + mockAPI.EXPECT().CapacityPoolsForStoragePools(ctx).Return([]*api.CapacityPool{pool}).Times(1) + + result := driver.Initialize(ctx, tridentconfig.ContextCSI, configJSON, commonConfig, map[string]string{}, + BackendUUID) + + assert.Error(t, result, "initialize did not fail") + assert.False(t, driver.Initialized(), "initialized") +} + func TestInitialize_InvalidVolumeCreateTimeout(t *testing.T) { commonConfig := &drivers.CommonStorageDriverConfig{ Version: 1, @@ -780,7 +826,7 @@ func TestInitialize_InvalidVolumeCreateTimeout(t *testing.T) { "capacityPools": ["RG1/NA1/CP1", "RG1/NA1/CP2"], "virtualNetwork": "VN1", "subnet": "RG1/VN1/SN1", - "volumeCreateTimeout": "10m" + "volumeCreateTimeout": "600m" }` // Have to at least one CapacityPool for ANF backends. diff --git a/storage_drivers/gcp/gcp_cvs.go b/storage_drivers/gcp/gcp_cvs.go index 8242869b4..9b76f5443 100644 --- a/storage_drivers/gcp/gcp_cvs.go +++ b/storage_drivers/gcp/gcp_cvs.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "fmt" + "math" "net" "reflect" "regexp" @@ -312,11 +313,15 @@ func (d *NFSStorageDriver) populateConfigurationDefaults( // VolumeCreateTimeoutSeconds is the timeout value in seconds. volumeCreateTimeout := d.defaultCreateTimeout() if config.VolumeCreateTimeout != "" { - i, err := strconv.ParseUint(d.Config.VolumeCreateTimeout, 10, 64) + i, err := strconv.ParseInt(d.Config.VolumeCreateTimeout, 10, 64) if err != nil { - Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).Errorf( - "Invalid volume create timeout period. %v", err) + Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(err).Error( + "Invalid volume create timeout period.") return err + } else if i < 0 { + Logc(ctx).WithField("interval", d.Config.VolumeCreateTimeout).WithError(err).Error( + "Unsupported volume create timeout period.") + return errors.UnsupportedError("unsupported volume create timeout period") } volumeCreateTimeout = time.Duration(i) * time.Second } @@ -728,6 +733,11 @@ func (d *NFSStorageDriver) Create( sizeBytes = d.applyMinimumVolumeSizeHW(requestedSizeBytes) } + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") + } + if requestedSizeBytes < sizeBytes { Logc(ctx).WithFields(LogFields{ "name": name, @@ -1770,6 +1780,11 @@ func (d *NFSStorageDriver) Resize(ctx context.Context, volConfig *storage.Volume Logd(ctx, d.Config.StorageDriverName, d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> Resize") defer Logd(ctx, d.Config.StorageDriverName, d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Resize") + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") + } + // Get the volume creationToken := name diff --git a/storage_drivers/gcp/gcp_cvs_test.go b/storage_drivers/gcp/gcp_cvs_test.go index a0a513af4..2d04ffefb 100644 --- a/storage_drivers/gcp/gcp_cvs_test.go +++ b/storage_drivers/gcp/gcp_cvs_test.go @@ -909,7 +909,7 @@ func TestInitialize_InvalidVolumeCreateTimeout(t *testing.T) { d := newTestGCPDriver(nil) err := d.Initialize(ctx(), tridentconfig.ContextCSI, configJSON, commonConfig, map[string]string{}, "abcd") - assert.ErrorContains(t, err, "strconv.ParseUint", "Valid volume create timeout") + assert.Error(t, err, "Expected an error") assert.False(t, d.initialized, "Driver initialized") } diff --git a/storage_drivers/ontap/api/abstraction_rest.go b/storage_drivers/ontap/api/abstraction_rest.go index 6f7107451..5dffb6462 100644 --- a/storage_drivers/ontap/api/abstraction_rest.go +++ b/storage_drivers/ontap/api/abstraction_rest.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "math" "runtime/debug" "sort" "strconv" @@ -1942,6 +1943,11 @@ func (d OntapAPIREST) LunCreate(ctx context.Context, lun Lun) error { sizeBytesStr, _ := utils.ConvertSizeToBytes(lun.Size) sizeBytes, _ := strconv.ParseUint(sizeBytesStr, 10, 64) + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithField("sizeInBytes", sizeBytes).Error("Invalid volume size") + return errors.New("invalid volume size") + } + creationErr := d.api.LunCreate(ctx, lun.Name, int64(sizeBytes), lun.OsType, lun.Qos, *lun.SpaceReserved, *lun.SpaceAllocated) if creationErr != nil { diff --git a/storage_drivers/ontap/api/ontap_rest.go b/storage_drivers/ontap/api/ontap_rest.go index bc7d3676c..d08fc63d4 100644 --- a/storage_drivers/ontap/api/ontap_rest.go +++ b/storage_drivers/ontap/api/ontap_rest.go @@ -10,6 +10,7 @@ import ( "encoding/json" "fmt" "io" + "math" "net/http" "net/url" "os" @@ -611,6 +612,10 @@ func (c RestClient) setVolumeSizeByNameAndStyle(ctx context.Context, volumeName, sizeBytesStr, _ := utils.ConvertSizeToBytes(newSize) sizeBytes, _ := strconv.ParseUint(sizeBytesStr, 10, 64) + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithField("sizeInBytes", sizeBytes).Error("Invalid volume size") + return errors.New("invalid volume size") + } volumeInfo := &models.Volume{ Size: utils.Ptr(int64(sizeBytes)), @@ -2884,6 +2889,11 @@ func (c RestClient) LunSetSize( sizeBytesStr, _ := utils.ConvertSizeToBytes(newSize) sizeBytes, _ := strconv.ParseUint(sizeBytesStr, 10, 64) + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithField("sizeInBytes", sizeBytes).Error("Invalid volume size") + return 0, errors.New("invalid volume size") + } + spaceInfo := &models.LunInlineSpace{ Size: utils.Ptr(int64(sizeBytes)), } @@ -5647,6 +5657,10 @@ func (c RestClient) NVMeNamespaceCreate(ctx context.Context, ns NVMeNamespace) ( sizeBytesStr, _ := utils.ConvertSizeToBytes(ns.Size) sizeInBytes, _ := strconv.ParseUint(sizeBytesStr, 10, 64) + if sizeInBytes > math.MaxInt64 { + Logc(ctx).WithField("sizeInBytes", sizeInBytes).Error("Invalid volume size") + return "", errors.New("invalid volume size") + } nsInfo := &models.NvmeNamespace{ Name: &ns.Name, diff --git a/storage_drivers/ontap/ontap_nas_qtree.go b/storage_drivers/ontap/ontap_nas_qtree.go index 23926a5a2..5f291e6af 100644 --- a/storage_drivers/ontap/ontap_nas_qtree.go +++ b/storage_drivers/ontap/ontap_nas_qtree.go @@ -5,6 +5,7 @@ package ontap import ( "context" "fmt" + "math" "reflect" "regexp" "strconv" @@ -31,16 +32,16 @@ import ( var QtreeInternalIDRegex = regexp.MustCompile(`^/svm/(?P[^/]+)/flexvol/(?P[^/]+)/qtree/(?P[^/]+)$`) const ( - deletedQtreeNamePrefix = "deleted_" - maxQtreeNameLength = 64 - minQtreesPerFlexvol = 50 - defaultQtreesPerFlexvol = 200 - maxQtreesPerFlexvol = 300 - defaultPruneFlexvolsPeriodSecs = uint64(600) // default to 10 minutes - defaultResizeQuotasPeriodSecs = uint64(60) // default to 1 minute - defaultEmptyFlexvolDeferredDeletePeriodSecs = uint64(28800) // default to 8 hours - pruneTask = "prune" - resizeTask = "resize" + deletedQtreeNamePrefix = "deleted_" + maxQtreeNameLength = 64 + minQtreesPerFlexvol = 50 + defaultQtreesPerFlexvol = 200 + maxQtreesPerFlexvol = 300 + defaultPruneFlexvolsPeriod = 10 * time.Minute // default to 10 minutes + defaultResizeQuotasPeriod = 1 * time.Minute // default to 1 minute + defaultEmptyFlexvolDeferredDeletePeriod = 8 * time.Hour // default to 8 hours + pruneTask = "prune" + resizeTask = "resize" ) // NASQtreeStorageDriver is for NFS and SMB storage provisioning of qtrees @@ -1966,35 +1967,35 @@ func (t *HousekeepingTask) run(ctx context.Context, tick time.Time) { func NewPruneTask(ctx context.Context, d *NASQtreeStorageDriver, tasks []func(context.Context)) *HousekeepingTask { // Read background task timings from config file, use defaults if missing or invalid - pruneFlexvolsPeriodSecs := defaultPruneFlexvolsPeriodSecs + pruneFlexvolsPeriod := defaultPruneFlexvolsPeriod if d.Config.QtreePruneFlexvolsPeriod != "" { - i, err := strconv.ParseUint(d.Config.QtreePruneFlexvolsPeriod, 10, 64) + i, err := strconv.ParseInt(d.Config.QtreePruneFlexvolsPeriod, 10, 64) if err != nil { Logc(ctx).WithField("interval", d.Config.QtreePruneFlexvolsPeriod).Warnf( "Invalid Flexvol pruning interval. %v", err) } else { - pruneFlexvolsPeriodSecs = i + pruneFlexvolsPeriod = time.Duration(i) * time.Second } } - emptyFlexvolDeferredDeletePeriodSecs := defaultEmptyFlexvolDeferredDeletePeriodSecs + emptyFlexvolDeferredDeletePeriod := defaultEmptyFlexvolDeferredDeletePeriod if d.Config.EmptyFlexvolDeferredDeletePeriod != "" { i, err := strconv.ParseUint(d.Config.EmptyFlexvolDeferredDeletePeriod, 10, 64) if err != nil { Logc(ctx).WithField("interval", d.Config.EmptyFlexvolDeferredDeletePeriod).Warnf( "Invalid Flexvol deferred delete period. %v", err) } else { - emptyFlexvolDeferredDeletePeriodSecs = i + emptyFlexvolDeferredDeletePeriod = time.Duration(i) * time.Second } } - d.emptyFlexvolDeferredDeletePeriod = time.Duration(emptyFlexvolDeferredDeletePeriodSecs) * time.Second + d.emptyFlexvolDeferredDeletePeriod = emptyFlexvolDeferredDeletePeriod Logc(ctx).WithFields(LogFields{ - "IntervalSeconds": pruneFlexvolsPeriodSecs, - "EmptyFlexvolTTL": emptyFlexvolDeferredDeletePeriodSecs, + "IntervalSeconds": pruneFlexvolsPeriod, + "EmptyFlexvolTTL": emptyFlexvolDeferredDeletePeriod, }).Debug("Configured Flexvol pruning period.") task := &HousekeepingTask{ Name: pruneTask, - Ticker: time.NewTicker(time.Duration(pruneFlexvolsPeriodSecs) * time.Second), + Ticker: time.NewTicker(pruneFlexvolsPeriod), InitialDelay: HousekeepingStartupDelaySecs * time.Second, Done: make(chan struct{}), Tasks: tasks, @@ -2006,23 +2007,23 @@ func NewPruneTask(ctx context.Context, d *NASQtreeStorageDriver, tasks []func(co func NewResizeTask(ctx context.Context, d *NASQtreeStorageDriver, tasks []func(context.Context)) *HousekeepingTask { // Read background task timings from config file, use defaults if missing or invalid - resizeQuotasPeriodSecs := defaultResizeQuotasPeriodSecs + resizeQuotasPeriod := defaultResizeQuotasPeriod if d.Config.QtreeQuotaResizePeriod != "" { - i, err := strconv.ParseUint(d.Config.QtreeQuotaResizePeriod, 10, 64) + i, err := strconv.ParseInt(d.Config.QtreeQuotaResizePeriod, 10, 64) if err != nil { Logc(ctx).WithField("interval", d.Config.QtreeQuotaResizePeriod).Warnf( "Invalid quota resize interval. %v", err) } else { - resizeQuotasPeriodSecs = i + resizeQuotasPeriod = time.Duration(i) * time.Second } } Logc(ctx).WithFields(LogFields{ - "IntervalSeconds": resizeQuotasPeriodSecs, + "IntervalSeconds": resizeQuotasPeriod, }).Debug("Configured quota resize period.") task := &HousekeepingTask{ Name: resizeTask, - Ticker: time.NewTicker(time.Duration(resizeQuotasPeriodSecs) * time.Second), + Ticker: time.NewTicker(resizeQuotasPeriod), InitialDelay: HousekeepingStartupDelaySecs * time.Second, Done: make(chan struct{}), Tasks: tasks, @@ -2048,6 +2049,10 @@ func (d *NASQtreeStorageDriver) Resize(ctx context.Context, volConfig *storage.V return fmt.Errorf("resizing is not supported on a read-only volume") } + if sizeBytes > math.MaxInt64 { + return fmt.Errorf("invalid volume size") + } + // Ensure any Flexvol won't be pruned before resize is completed. utils.Lock(ctx, "resize", d.sharedLockID) defer utils.Unlock(ctx, "resize", d.sharedLockID) diff --git a/storage_drivers/ontap/ontap_nas_qtree_test.go b/storage_drivers/ontap/ontap_nas_qtree_test.go index 8a7d186ce..bcb7e7642 100644 --- a/storage_drivers/ontap/ontap_nas_qtree_test.go +++ b/storage_drivers/ontap/ontap_nas_qtree_test.go @@ -2586,8 +2586,7 @@ func TestNewPruneTask_WithDefaultValues(t *testing.T) { assert.Equal(t, tasks, result.Tasks) assert.Equal(t, driver, result.Driver) - expectedEmptyFlexvolDeferredDeletePeriod := time.Duration(defaultEmptyFlexvolDeferredDeletePeriodSecs) * time.Second - assert.Equal(t, expectedEmptyFlexvolDeferredDeletePeriod, driver.emptyFlexvolDeferredDeletePeriod, + assert.Equal(t, defaultEmptyFlexvolDeferredDeletePeriod, driver.emptyFlexvolDeferredDeletePeriod, "emptyFlexvolDeferredDeletePeriod does not match") } @@ -2626,8 +2625,7 @@ func TestNewPruneTask_WithInValidConfigValues(t *testing.T) { assert.Equal(t, tasks, result.Tasks) assert.Equal(t, driver, result.Driver) - expectedFlexvolDeferredDeletePeriod := time.Duration(defaultEmptyFlexvolDeferredDeletePeriodSecs) * time.Second - assert.Equal(t, expectedFlexvolDeferredDeletePeriod, driver.emptyFlexvolDeferredDeletePeriod, + assert.Equal(t, defaultEmptyFlexvolDeferredDeletePeriod, driver.emptyFlexvolDeferredDeletePeriod, "emptyFlexvolDeferredDeletePeriod does not match") } diff --git a/storage_drivers/ontap/ontap_san_nvme.go b/storage_drivers/ontap/ontap_san_nvme.go index 1cb160fbc..7616a7fd2 100644 --- a/storage_drivers/ontap/ontap_san_nvme.go +++ b/storage_drivers/ontap/ontap_san_nvme.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "math" "reflect" "regexp" "strconv" @@ -1237,6 +1238,11 @@ func (d *NVMeStorageDriver) Resize( defer Logd(ctx, d.Config.StorageDriverName, d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Resize") + if requestedSizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return fmt.Errorf("invalid volume size") + } + volExists, err := d.API.VolumeExists(ctx, name) if err != nil { Logc(ctx).WithFields(LogFields{ @@ -1264,12 +1270,14 @@ func (d *NVMeStorageDriver) Resize( return fmt.Errorf("error while checking namespace size, %v", err) } - nsSizeBytes, err := strconv.ParseUint(ns.Size, 10, 64) + nsSizeBytes, err := strconv.ParseInt(ns.Size, 10, 64) if err != nil { return fmt.Errorf("error while parsing namespace size, %v", err) + } else if nsSizeBytes < 0 { + return errors.UnsupportedCapacityRangeError(errors.New("unsupported namespace size")) } - if requestedSizeBytes < nsSizeBytes { + if int64(requestedSizeBytes) < nsSizeBytes { return fmt.Errorf("requested size %d is less than existing volume size %d", requestedSizeBytes, nsSizeBytes) } @@ -1281,7 +1289,7 @@ func (d *NVMeStorageDriver) Resize( newFlexVolSize := calculateFlexvolSizeBytes(ctx, name, requestedSizeBytes, snapshotReserveInt) newFlexVolSize = uint64(LUNMetadataBufferMultiplier * float64(newFlexVolSize)) - sameNamespaceSize := utils.VolumeSizeWithinTolerance(int64(requestedSizeBytes), int64(nsSizeBytes), + sameNamespaceSize := utils.VolumeSizeWithinTolerance(int64(requestedSizeBytes), nsSizeBytes, tridentconfig.SANResizeDelta) sameFlexVolSize := utils.VolumeSizeWithinTolerance(int64(newFlexVolSize), int64(currentFlexVolSize), @@ -1295,7 +1303,7 @@ func (d *NVMeStorageDriver) Resize( "delta": tridentconfig.SANResizeDelta, }).Info("Requested size and current namespace size are within the delta and therefore considered" + " the same size for SAN resize operations.") - volConfig.Size = strconv.FormatUint(nsSizeBytes, 10) + volConfig.Size = strconv.FormatInt(nsSizeBytes, 10) return nil } diff --git a/storage_drivers/ontap/ontap_san_nvme_test.go b/storage_drivers/ontap/ontap_san_nvme_test.go index 3f7874a52..e0ea73d8c 100644 --- a/storage_drivers/ontap/ontap_san_nvme_test.go +++ b/storage_drivers/ontap/ontap_san_nvme_test.go @@ -5,6 +5,7 @@ package ontap import ( "encoding/json" "fmt" + "math" "testing" "github.com/golang/mock/gomock" @@ -1135,6 +1136,14 @@ func TestCreatePrepare(t *testing.T) { assert.True(t, volConfig.AccessInfo.PublishEnforcement, "Publish enforcement not enabled.") } +func TestNVMeResize_InvalidRequestedSize(t *testing.T) { + d, _ := newNVMeDriverAndMockApi(t) + _, volConfig, _ := getNVMeCreateArgs(d) + + err := d.Resize(ctx, volConfig, math.MaxInt64+1) + assert.ErrorContains(t, err, "invalid volume size") +} + func TestNVMeResize_VolumeExistsErrors(t *testing.T) { d, mAPI := newNVMeDriverAndMockApi(t) _, volConfig, _ := getNVMeCreateArgs(d) @@ -1190,7 +1199,7 @@ func TestNVMeResize_ParseNamespaceSizeError(t *testing.T) { err := d.Resize(ctx, volConfig, 100) - assert.ErrorContains(t, err, "error while parsing namespace size") + assert.Error(t, err, "expected an error") } func TestNVMeResize_LessRequestedSizeError(t *testing.T) { diff --git a/storage_drivers/solidfire/solidfire_san.go b/storage_drivers/solidfire/solidfire_san.go index 2e3afda9a..3a348ff31 100644 --- a/storage_drivers/solidfire/solidfire_san.go +++ b/storage_drivers/solidfire/solidfire_san.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "fmt" + "math" "net/url" "reflect" "regexp" @@ -813,20 +814,20 @@ func (d *SANStorageDriver) Create( if err != nil { return fmt.Errorf("could not convert volume size %s: %v", volConfig.Size, err) } - sizeBytes, err := strconv.ParseUint(requestedSize, 10, 64) + sizeBytes, err := strconv.ParseInt(requestedSize, 10, 64) if err != nil { return fmt.Errorf("%v is an invalid volume size: %v", volConfig.Size, err) } if sizeBytes == 0 { defaultSize, _ := utils.ConvertSizeToBytes(pool.InternalAttributes()[Size]) - sizeBytes, _ = strconv.ParseUint(defaultSize, 10, 64) + sizeBytes, _ = strconv.ParseInt(defaultSize, 10, 64) } - if checkMinVolumeSizeError := drivers.CheckMinVolumeSize(sizeBytes, + if checkMinVolumeSizeError := drivers.CheckMinVolumeSize(uint64(sizeBytes), MinimumVolumeSizeBytes); checkMinVolumeSizeError != nil { return checkMinVolumeSizeError } if _, _, checkVolumeSizeLimitsError := drivers.CheckVolumeSizeLimits( - ctx, sizeBytes, d.Config.CommonStorageDriverConfig, + ctx, uint64(sizeBytes), d.Config.CommonStorageDriverConfig, ); checkVolumeSizeLimitsError != nil { return checkVolumeSizeLimitsError } @@ -899,12 +900,12 @@ func (d *SANStorageDriver) Create( } // Update config to reflect values used to create volume - volConfig.Size = strconv.FormatUint(sizeBytes, 10) + volConfig.Size = strconv.FormatInt(sizeBytes, 10) volConfig.FileSystem = fstype volConfig.QosType = opts["type"] req.Qos = qos - req.TotalSize = int64(sizeBytes) + req.TotalSize = sizeBytes req.AccountID = d.AccountID req.Name = MakeSolidFireName(name) req.Attributes = meta @@ -1935,6 +1936,11 @@ func (d *SANStorageDriver) Resize(ctx context.Context, volConfig *storage.Volume Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> Resize") defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Resize") + if sizeBytes > math.MaxInt64 { + Logc(ctx).WithFields(fields).Error("Invalid volume size") + return errors.New("invalid volume size") + } + volume, err := d.GetVolume(ctx, name) if err != nil { Logc(ctx).WithFields(LogFields{