From 14b08e986b4fa7f6d90ae2f0a42c615895fb51d9 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Wed, 1 Jul 2026 16:28:40 -0600 Subject: [PATCH 1/2] Fix 32-bit integer overflow in AllOutData byte calculation The AllocAry routines in NWTC_IO.f90 computed the byte count for error messages using 32-bit integer arithmetic (AryDim1*AryDim2*BYTES_IN_RxKi), which overflowed for large arrays, producing misleading negative values in error messages. Changes: - NWTC_IO.f90: Cast all dimension arguments to B8Ki before multiplication in error message byte-count calculations for all multi-dimensional AllocAry routines (2D through 5D, integer, R4Ki, and R8Ki variants). - FAST_Subs.f90: Add pre-allocation safeguard in FAST_InitOutput that computes the AllOutData array size using B8Ki arithmetic and checks against a 2 GB limit before attempting allocation. Provides a clear error message with the required memory size, array dimensions, and actionable suggestions (reduce TMax, increase DT_Out, reduce output channels, or use text output format). Co-authored-by: Andy Platt Co-authored-by: Claude Opus 4 --- modules/nwtc-library/src/NWTC_IO.f90 | 26 +++++++++++----------- modules/openfast-library/src/FAST_Subs.f90 | 13 +++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index 1fbe24a6cc..511394ee16 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -490,7 +490,7 @@ SUBROUTINE AllIAry2 ( Ary, AryDim1, AryDim2, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*BYTES_IN_INT))//' bytes of memory for the '//TRIM( Descr )//' array.' + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*BYTES_IN_INT))//' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE ErrStat = ErrID_None @@ -521,7 +521,7 @@ SUBROUTINE AllIAry3 ( Ary, AryDim1, AryDim2, AryDim3, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*BYTES_IN_INT))//' bytes of memory for the '//TRIM( Descr )//' array.' + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*BYTES_IN_INT))//' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE ErrStat = ErrID_None @@ -681,7 +681,7 @@ SUBROUTINE AllRPAry2 ( Ary, AryDim1, AryDim2, Descr, ErrStat, ErrMsg ) ALLOCATE ( Ary(AryDim1,AryDim2) , STAT=ErrStat ) IF ( ErrStat /= 0 ) THEN ErrStat = ErrID_Fatal - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*BYTES_IN_REAL))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*BYTES_IN_REAL))//& ' bytes of memory for the '//TRIM( Descr )//' array.' ELSE ErrStat = ErrID_None @@ -714,7 +714,7 @@ SUBROUTINE AllR4PAry3 ( Ary, AryDim1, AryDim2, AryDim3, Descr, ErrStat, ErrMsg ALLOCATE ( Ary(AryDim1,AryDim2,AryDim3) , STAT=ErrStat ) IF ( ErrStat /= 0 ) THEN ErrStat = ErrID_Fatal - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*BYTES_IN_R4Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*BYTES_IN_R4Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' ELSE ErrStat = ErrID_None @@ -747,7 +747,7 @@ SUBROUTINE AllR8PAry3 ( Ary, AryDim1, AryDim2, AryDim3, Descr, ErrStat, ErrMsg ALLOCATE ( Ary(AryDim1,AryDim2,AryDim3) , STAT=ErrStat ) IF ( ErrStat /= 0 ) THEN ErrStat = ErrID_Fatal - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*BYTES_IN_R8Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*BYTES_IN_R8Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' ELSE ErrStat = ErrID_None @@ -926,7 +926,7 @@ SUBROUTINE AllR4Ary2 ( Ary, AryDim1, AryDim2, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*BYTES_IN_R4Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*BYTES_IN_R4Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -957,7 +957,7 @@ SUBROUTINE AllR8Ary2 ( Ary, AryDim1, AryDim2, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*BYTES_IN_R8Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*BYTES_IN_R8Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -989,7 +989,7 @@ SUBROUTINE AllR4Ary3 ( Ary, AryDim1, AryDim2, AryDim3, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*BYTES_IN_R4Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*BYTES_IN_R4Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -1021,7 +1021,7 @@ SUBROUTINE AllR8Ary3 ( Ary, AryDim1, AryDim2, AryDim3, Descr, ErrStat, ErrMsg ) IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*BYTES_IN_R8Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*BYTES_IN_R8Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -1054,7 +1054,7 @@ SUBROUTINE AllR4Ary4 ( Ary, AryDim1, AryDim2, AryDim3, AryDim4, Descr, ErrStat, IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*AryDim4*BYTES_IN_R4Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*int(AryDim4,B8Ki)*BYTES_IN_R4Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -1087,7 +1087,7 @@ SUBROUTINE AllR8Ary4 ( Ary, AryDim1, AryDim2, AryDim3, AryDim4, Descr, ErrStat, IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*AryDim4*BYTES_IN_R8Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*int(AryDim4,B8Ki)*BYTES_IN_R8Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -1121,7 +1121,7 @@ SUBROUTINE AllR4Ary5 ( Ary, AryDim1, AryDim2, AryDim3, AryDim4, AryDim5, Descr, IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*AryDim4*AryDim5*BYTES_IN_R4Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*int(AryDim4,B8Ki)*int(AryDim5,B8Ki)*BYTES_IN_R4Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE @@ -1155,7 +1155,7 @@ SUBROUTINE AllR8Ary5 ( Ary, AryDim1, AryDim2, AryDim3, AryDim4, AryDim5, Descr, IF ( ALLOCATED(Ary) ) THEN ! or Sttus=151 on IVF ErrMsg = 'Error allocating memory for the '//TRIM( Descr )//' array; array was already allocated.' ELSE - ErrMsg = 'Error allocating '//TRIM(Num2LStr(AryDim1*AryDim2*AryDim3*AryDim4*AryDim5*BYTES_IN_R8Ki))//& + ErrMsg = 'Error allocating '//TRIM(Num2LStr(int(AryDim1,B8Ki)*int(AryDim2,B8Ki)*int(AryDim3,B8Ki)*int(AryDim4,B8Ki)*int(AryDim5,B8Ki)*BYTES_IN_R8Ki))//& ' bytes of memory for the '//TRIM( Descr )//' array.' END IF ELSE diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 781968e4ac..fe8155ce07 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -2208,6 +2208,7 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) INTEGER(IntKi) :: iRot ! Rotor index for DO loops. INTEGER(IntKi) :: indxNext ! The index of the next value to be written to an array INTEGER(IntKi) :: NumOuts ! number of channels to be written to the output file(s) + INTEGER(B8Ki) :: AllOutData_bytes ! total bytes required for AllOutData array character(10) :: Prefix ! Output header prefix !...................................................... @@ -2659,6 +2660,18 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) !IF (p_FAST%CompAeroMaps) y_FAST%NOutSteps = p_FAST%NumTSR * p_FAST%NumPitch y_FAST%NOutSteps = CEILING ( (p_FAST%TMax - p_FAST%TStart) / p_FAST%DT_OUT ) + 1 + ! Check that the AllOutData array size will not exceed memory limits + AllOutData_bytes = int(NumOuts-1, B8Ki) * int(y_FAST%NOutSteps, B8Ki) * int(BYTES_IN_REAL, B8Ki) + IF ( AllOutData_bytes > 2147483647_B8Ki ) THEN ! 2 GB limit + ErrStat = ErrID_Fatal + ErrMsg = 'The AllOutData array would require '//TRIM(Num2LStr(AllOutData_bytes))// & + ' bytes of memory ('//TRIM(Num2LStr(AllOutData_bytes/(1024_B8Ki*1024_B8Ki)))//' MB).'// & + ' This exceeds the 2 GB limit for binary output stored in memory.'// & + ' Reduce TMax, increase DT_Out, reduce output channels, or switch to text output (OutFileFmt=1).'// & + ' (NumOuts='//TRIM(Num2LStr(NumOuts-1))//', NOutSteps='//TRIM(Num2LStr(y_FAST%NOutSteps))//')' + RETURN + END IF + CALL AllocAry( y_FAST%AllOutData, NumOuts-1, y_FAST%NOutSteps, 'AllOutData', ErrStat, ErrMsg ) ! this does not include the time channel (or case number for steady-state solve) IF ( ErrStat >= AbortErrLev ) RETURN y_FAST%AllOutData = 0.0_ReKi From bb615bc2df97576b4aa44c699548e749808a02fd Mon Sep 17 00:00:00 2001 From: Andy Platt Date: Wed, 1 Jul 2026 16:49:02 -0600 Subject: [PATCH 2/2] Update error message on `AllOutData` size for more clarity Co-authored-by: GitHub Copilot Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- modules/openfast-library/src/FAST_Subs.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index fe8155ce07..6ed98bcc8d 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -2668,7 +2668,7 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) ' bytes of memory ('//TRIM(Num2LStr(AllOutData_bytes/(1024_B8Ki*1024_B8Ki)))//' MB).'// & ' This exceeds the 2 GB limit for binary output stored in memory.'// & ' Reduce TMax, increase DT_Out, reduce output channels, or switch to text output (OutFileFmt=1).'// & - ' (NumOuts='//TRIM(Num2LStr(NumOuts-1))//', NOutSteps='//TRIM(Num2LStr(y_FAST%NOutSteps))//')' + ' (NumOutChans='//TRIM(Num2LStr(NumOuts-1))//', NOutSteps='//TRIM(Num2LStr(y_FAST%NOutSteps))//')' RETURN END IF