From c4f92f3032fbf9f134a0f71ca32e041f1abdc39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Creuse?= Date: Mon, 9 Sep 2024 17:25:20 +0200 Subject: [PATCH 1/3] TGen.Types.Translation: Fix constraint error in error handling mechanism --- src/tgen/tgen-types-translation.adb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tgen/tgen-types-translation.adb b/src/tgen/tgen-types-translation.adb index fcadcc19..3c5daf25 100644 --- a/src/tgen/tgen-types-translation.adb +++ b/src/tgen/tgen-types-translation.adb @@ -2963,7 +2963,7 @@ package body TGen.Types.Translation is -- First part of the declaration. Used to determine whether the type we -- are translating is private or not. - Specialized_Res : Translation_Result (Success => True); + Specialized_Res : Translation_Result; begin Verbose_Diag := Verbose; @@ -2980,6 +2980,8 @@ package body TGen.Types.Translation is -- declarations or anonymous access types, both of which we don't -- intend to support. + Specialized_Res := (Success => True, others => <>); + Specialized_Res.Res.Set (Unsupported_Typ' (Reason => To_Unbounded_String @@ -2993,6 +2995,7 @@ package body TGen.Types.Translation is -- modular integer but for which we do not want to generate any -- values. + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Unsupported_Typ' (Reason => To_Unbounded_String ("System.Address unsupported"), others => <>)); @@ -3002,6 +3005,7 @@ package body TGen.Types.Translation is -- We are dealing with a private type declared in a nested package, -- consider this as unsupported. + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Unsupported_Typ' (Reason => To_Unbounded_String @@ -3009,6 +3013,7 @@ package body TGen.Types.Translation is & " supported"), others => <>)); elsif Root_Type.P_Is_Formal then + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Formal_Typ' (Reason => To_Unbounded_String ("Generic formal types are unsupported"), @@ -3020,10 +3025,12 @@ package body TGen.Types.Translation is (Node => N, Other_Type => N.P_Bool_Type.As_Base_Type_Decl) then + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Bool_Typ'(Is_Static => True, others => <>)); elsif Root_Type.P_Is_Enum_Type then if not Is_Static then + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Other_Enum_Typ' (Is_Static => False, others => <>)); end if; @@ -3045,6 +3052,7 @@ package body TGen.Types.Translation is if Is_Static then Specialized_Res := Translate_Float_Decl (N); else + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Float_Typ' (Is_Static => False, @@ -3059,6 +3067,7 @@ package body TGen.Types.Translation is if Is_Static then Specialized_Res := Translate_Ordinary_Fixed_Decl (N); else + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Ordinary_Fixed_Typ' (Is_Static => False, @@ -3068,6 +3077,7 @@ package body TGen.Types.Translation is if Is_Static then Specialized_Res := Translate_Decimal_Fixed_Decl (N); else + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Decimal_Fixed_Typ' (Is_Static => False, @@ -3081,6 +3091,7 @@ package body TGen.Types.Translation is elsif Root_Type.P_Is_Record_Type then if Root_Type.P_Is_Tagged_Type then + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Unsupported_Typ' (Reason => To_Unbounded_String ("tagged types not supported"), @@ -3090,12 +3101,14 @@ package body TGen.Types.Translation is end if; elsif Root_Type.P_Is_Access_Type then + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Access_Typ' (Reason => To_Unbounded_String ("Access types are not supported"), others => <>)); else + Specialized_Res := (Success => True, others => <>); Specialized_Res.Res.Set (Unsupported_Typ' (Reason => To_Unbounded_String ("Unknown type kind"), From 7d65f2a4460bad33bd6ec3adc36cedefb7c47267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Creuse?= Date: Mon, 9 Sep 2024 17:43:33 +0200 Subject: [PATCH 2/3] TGen: add API to set/querry array size limit --- src/tgen/tgen-libgen.adb | 18 ++++++++++++++++++ src/tgen/tgen-libgen.ads | 27 ++++++++++++++++++++++++--- src/tgen/tgen-marshalling.adb | 17 ++++++++++++++++- src/tgen/tgen-marshalling.ads | 15 +++++++++++++++ 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/tgen/tgen-libgen.adb b/src/tgen/tgen-libgen.adb index a6dc02c1..d8839f05 100644 --- a/src/tgen/tgen-libgen.adb +++ b/src/tgen/tgen-libgen.adb @@ -49,6 +49,10 @@ with TGen.Types.Translation; use TGen.Types.Translation; package body TGen.Libgen is + Array_Limit_Frozen : Boolean := False; + -- If True, calls to Set_Array_Limit will raise a Constraint_Error. + -- Should be set by any call to Include_Subp or Supported_Subprogram. + procedure Generate_Support_Library (Ctx : Libgen_Context; Pack_Name : Ada_Qualified_Name) with @@ -647,6 +651,7 @@ package body TGen.Libgen is Trans_Res : constant Translation_Result := Translate (Subp.As_Basic_Decl); begin + Array_Limit_Frozen := True; if Trans_Res.Success then Diags := Trans_Res.Res.Get.Get_Diagnostics; if Diags.Is_Empty then @@ -1352,4 +1357,17 @@ package body TGen.Libgen is raise; end Generate_Harness; + -------------------------- + -- Set_Array_Size_Limit -- + -------------------------- + + procedure Set_Array_Size_Limit (Limit : Positive) is + begin + if Array_Limit_Frozen then + raise Constraint_Error with + "Attempting to modify array size limit after it has been frozen."; + end if; + TGen.Marshalling.Set_Array_Size_Limit (Limit); + end Set_Array_Size_Limit; + end TGen.Libgen; diff --git a/src/tgen/tgen-libgen.ads b/src/tgen/tgen-libgen.ads index f8eb51db..ba861fcf 100644 --- a/src/tgen/tgen-libgen.ads +++ b/src/tgen/tgen-libgen.ads @@ -33,10 +33,11 @@ with GNATCOLL.VFS; with Libadalang.Analysis; with TGen.Context; -with TGen.Wrappers; use TGen.Wrappers; +with TGen.Marshalling; +with TGen.Parse_Strategy; use TGen.Parse_Strategy; with TGen.Strings; -with TGen.Types; use TGen.Types; -with TGen.Parse_Strategy; use TGen.Parse_Strategy; +with TGen.Types; use TGen.Types; +with TGen.Wrappers; use TGen.Wrappers; package TGen.Libgen is package LAL renames Libadalang.Analysis; @@ -139,6 +140,26 @@ package TGen.Libgen is -- generated by the harness under Test_Output_Dir, and will contain -- Default_Test_Num tests. + function Get_Array_Size_Limit return Positive renames + TGen.Marshalling.Get_Array_Size_Limit; + -- Return the size beyond which the marshallers will give up trying to load + -- arrays, to avoid allocating overly-large arrays on the stack. + -- + -- The default value is 1000, but this can be overridden either through the + -- TGEN_ARRAY_LIMIT environment variable, or the Set_Array_Size_Limit + -- procedure. The latter takes precedence over the former. + + procedure Set_Array_Size_Limit (Limit : Positive); + -- Set the array size limit beyond which marshallers will give up reading + -- array values, to avoid allocating overly large arrays on the stack. + -- + -- If used, this will override any value set through the TGEN_ARRAY_LIMIT + -- environment variable. + -- + -- This can only be called prior to the first call to Include_Subp or + -- Supported_Subprogram to ensure consistency of the array limit used in + -- all the marshallers, otherwise Constraint_Error is raised. + private use TGen.Strings; use TGen.Context; diff --git a/src/tgen/tgen-marshalling.adb b/src/tgen/tgen-marshalling.adb index 001cb9ec..be75cab6 100644 --- a/src/tgen/tgen-marshalling.adb +++ b/src/tgen/tgen-marshalling.adb @@ -783,7 +783,7 @@ package body TGen.Marshalling is 5 => Assoc ("COMP_PREFIX", Comp_Pref_Tag), 6 => Assoc ("ADA_DIM", Ada_Dim_Tag), 7 => Assoc ("IS_ENUM", Is_Enum_Tag), - 8 => Assoc ("ARR_LIMIT", Array_Length_Limit)]; + 8 => Assoc ("ARR_LIMIT", Get_Array_Size_Limit)]; begin Print_Header (Assocs); @@ -1134,6 +1134,21 @@ package body TGen.Marshalling is Append (Str.all, Ada.Characters.Latin_1.LF); end New_Line; + -------------------------- + -- Get_Array_Size_Limit -- + -------------------------- + + function Get_Array_Size_Limit return Positive is (Array_Length_Limit); + + -------------------------- + -- Set_Array_Size_Limit -- + -------------------------- + + procedure Set_Array_Size_Limit (Limit : Positive) is + begin + Array_Length_Limit := Limit; + end Set_Array_Size_Limit; + begin if Ada.Environment_Variables.Exists (Array_Length_Limit_Env_Var) then diff --git a/src/tgen/tgen-marshalling.ads b/src/tgen/tgen-marshalling.ads index 5da0bd05..d68abc78 100644 --- a/src/tgen/tgen-marshalling.ads +++ b/src/tgen/tgen-marshalling.ads @@ -47,6 +47,21 @@ package TGen.Marshalling is function Input_Fname_For_Typ (Typ_FQN : Ada_Qualified_Name) return String; -- Name of the input marshalling function for the given type + function Get_Array_Size_Limit return Positive; + -- Return the size beyond which the marshallers will give up trying to load + -- arrays, to avoid allocating overly-large arrays on the stack. + -- + -- The default value is 1000, but this can be overridden either through the + -- TGEN_ARRAY_LIMIT environment variable, or the Set_Array_Size_Limit + -- procedure. The latter takes precedence over the former. + + procedure Set_Array_Size_Limit (Limit : Positive); + -- Set the array size limit beyond which marshallers will give up reading + -- array values, to avoid allocating overly large arrays on the stack. + -- + -- If used, this will override any value set through the TGEN_ARRAY_LIMIT + -- environment variable. + Global_Prefix : constant String := "TGen_Marshalling"; type Spec_Part is (Pub, Priv); From 57026d304a3331df40c039a119814c532cb9db28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Creuse?= Date: Mon, 9 Sep 2024 17:49:45 +0200 Subject: [PATCH 3/3] TGen: Reject constrained array types with too many elements TGen already rejected at runtime unconstrained array values exceeding a configurable limit. With this change, constrained array types are also rejected when they define a type with more elements than the configured limit. --- src/tgen/tgen-types-translation.adb | 102 ++++++++++++++---- src/tgen/tgen_rts/tgen-types-array_types.adb | 37 +++++++ src/tgen/tgen_rts/tgen-types-array_types.ads | 4 + .../tests/test/194_const_array_limit/pkg.ads | 26 +++++ .../tests/test/194_const_array_limit/prj.gpr | 5 + .../tests/test/194_const_array_limit/test.out | 23 ++++ .../tests/test/194_const_array_limit/test.sh | 5 + .../test/194_const_array_limit/test.yaml | 10 ++ 8 files changed, 192 insertions(+), 20 deletions(-) create mode 100644 testsuite/tests/test/194_const_array_limit/pkg.ads create mode 100644 testsuite/tests/test/194_const_array_limit/prj.gpr create mode 100644 testsuite/tests/test/194_const_array_limit/test.out create mode 100644 testsuite/tests/test/194_const_array_limit/test.sh create mode 100644 testsuite/tests/test/194_const_array_limit/test.yaml diff --git a/src/tgen/tgen-types-translation.adb b/src/tgen/tgen-types-translation.adb index 3c5daf25..837ab7e4 100644 --- a/src/tgen/tgen-types-translation.adb +++ b/src/tgen/tgen-types-translation.adb @@ -42,6 +42,7 @@ with TGen.Types.Enum_Types; use TGen.Types.Enum_Types; with TGen.Types.Int_Types; use TGen.Types.Int_Types; with TGen.Types.Real_Types; use TGen.Types.Real_Types; with TGen.Types.Record_Types; use TGen.Types.Record_Types; +with TGen.Marshalling; with TGen.Numerics; package body TGen.Types.Translation is @@ -1220,6 +1221,7 @@ package body TGen.Types.Translation is Decl.P_Root_Type.P_Full_View.As_Type_Decl.F_Type_Def .As_Array_Type_Def.F_Component_Type; Num_Indices : Natural := 0; + Total_Size : Big_Integer; begin -- Compute the number of indices @@ -1355,9 +1357,6 @@ package body TGen.Types.Translation is end if; if not Has_Constraints then - Res_Typ.Index_Constraints (Current_Index) := - (Present => False); - -- Check if the index type is a subtype with constraints. If -- this is the case, update the constraints accordingly. @@ -1368,6 +1367,9 @@ package body TGen.Types.Translation is (Constraint.As_Identifier.P_Referenced_Decl .As_Base_Type_Decl); begin + + -- Create non-static constraints by default... + if Id_Type_Res.Success then declare FQN : constant String := @@ -1386,19 +1388,30 @@ package body TGen.Types.Translation is Max_Text := +(FQN & "'Last"); end if; end; - Res_Typ.Index_Constraints (Current_Index) := - (Present => True, - Discrete_Range => - (Low_Bound => - (Kind => Non_Static, - Text => +Min_Text), - High_Bound => - (Kind => Non_Static, - Text => +Max_Text))); - end if; + Has_Constraints := True; + Min_Static := False; + Max_Static := False; + -- ...But attempt to evaluate the subtype bounds, + -- this is still useful in practice to detect + -- arrays that could be too large. + + if As_Discrete_Typ (Id_Type_Res.Res).Is_Static then + Min_Static := True; + Max_Static := True; + Constraint_Min := + As_Discrete_Typ (Id_Type_Res.Res).Low_Bound; + Constraint_Max := + As_Discrete_Typ (Id_Type_Res.Res).High_Bound; + end if; + end if; end; end if; + end if; + + if not Has_Constraints then + Res_Typ.Index_Constraints (Current_Index) := + (Present => False); elsif Max_Static and then not Min_Static then Res_Typ.Index_Constraints (Current_Index) := (Present => True, @@ -1440,6 +1453,28 @@ package body TGen.Types.Translation is and then (for all Idx in 1 .. Res_Typ.Num_Dims => Static (Res_Typ.Index_Constraints (Idx))); + -- Check if the translated array type has less elements than what + -- is allowed. + + Total_Size := Res_Typ.Size; + if Total_Size > + To_Big_Integer (TGen.Marshalling.Get_Array_Size_Limit) + then + return Res : Translation_Result (Success => True) do + Res.Res.Set + (Unsupported_Typ' + (Reason => + +("array type " & To_Ada (Res_Typ.Name) + & "has more elements (" + & Trim (To_String (Total_Size)) + & ") than the configured limit (" + & Trim (Positive'Image + (TGen.Marshalling.Get_Array_Size_Limit)) + & ")"), + others => <>)); + end return; + end if; + return Res : Translation_Result (Success => True) do Res.Res.Set (Res_Typ); end return; @@ -2832,9 +2867,15 @@ package body TGen.Types.Translation is (N.As_Subtype_Indication.F_Constraint)))); end return; when Array_Typ_Range => - return Res : Translation_Result (Success => True) do - Res.Res.Set (Anonymous_Typ' - (Name => Ada_Identifier_Vectors.Empty_Vector, + + -- We need to check wether this anonymous array type isn't + -- going to be larger than what is supported by the + -- marshallers. + + declare + Anon_Typ : constant Anonymous_Typ := + (Name => + Ada_Identifier_Vectors.Empty_Vector, Last_Comp_Unit_Idx => 1, Named_Ancestor => Intermediate_Result.Res, Fully_Private => @@ -2843,10 +2884,31 @@ package body TGen.Types.Translation is Intermediate_Result.Res.Get.Private_Extension, Subtype_Constraints => new Index_Constraints' (Translate_Index_Constraints - (N.As_Subtype_Indication.F_Constraint, - As_Unconstrained_Array_Typ - (Intermediate_Result.Res).Num_Dims)))); - end return; + (N.As_Subtype_Indication.F_Constraint, + As_Unconstrained_Array_Typ + (Intermediate_Result.Res).Num_Dims))); + + Total_Size : constant Big_Integer := + As_Constrained_Array_Typ (Anon_Typ.As_Named_Typ).Size; + begin + if Total_Size > + To_Big_Integer (TGen.Marshalling.Get_Array_Size_Limit) + then + return Res : Translation_Result (Success => False) do + Res.Diagnostics := + +("array type has more elements (" + & Trim (To_String (Total_Size)) + & ") than the configured limit (" + & Trim (Positive'Image + (TGen.Marshalling.Get_Array_Size_Limit)) + & ")"); + end return; + else + return Res : Translation_Result (Success => True) do + Res.Res.Set (Anon_Typ); + end return; + end if; + end; when Record_Typ_Range => return Res : Translation_Result (Success => True) do pragma Assert (Kind (N.As_Subtype_Indication.F_Constraint) diff --git a/src/tgen/tgen_rts/tgen-types-array_types.adb b/src/tgen/tgen_rts/tgen-types-array_types.adb index 9b8ecadd..aa475dd8 100644 --- a/src/tgen/tgen_rts/tgen-types-array_types.adb +++ b/src/tgen/tgen_rts/tgen-types-array_types.adb @@ -113,6 +113,43 @@ package body TGen.Types.Array_Types is return To_String (Res); end Image; + ---------- + -- Size -- + ---------- + + function Size (Self : Constrained_Array_Typ) return Big_Integer is + Total_Size : Big_Integer := To_Big_Integer (1); + begin + for I in 1 .. Self.Num_Dims loop + if not Self.Index_Constraints (I).Present then + if As_Discrete_Typ (Self.Index_Types (I)).Is_Static then + Total_Size := Total_Size * + (As_Discrete_Typ (Self.Index_Types (I)).High_Bound + - As_Discrete_Typ (Self.Index_Types (I)).Low_Bound + + To_Big_Integer (1)); + else + Total_Size := To_Big_Integer (-1); + end if; + elsif Self.Index_Constraints (I).Discrete_Range.High_Bound.Kind + = Static + and then Self.Index_Constraints (I).Discrete_Range.Low_Bound.Kind + = Static + then + Total_Size := Total_Size * + (Self.Index_Constraints (I).Discrete_Range.High_Bound.Int_Val + - Self.Index_Constraints (I).Discrete_Range.Low_Bound.Int_Val + + To_Big_Integer (1)); + else + Total_Size := To_Big_Integer (-1); + end if; + end loop; + return Total_Size; + end Size; + + ---------------------------- + -- Callback_On_Constraint -- + ---------------------------- + procedure Callback_On_Constraint (Self : Constrained_Array_Typ; Var_Name : Unbounded_String; diff --git a/src/tgen/tgen_rts/tgen-types-array_types.ads b/src/tgen/tgen_rts/tgen-types-array_types.ads index 88ba863b..5fb9f761 100644 --- a/src/tgen/tgen_rts/tgen-types-array_types.ads +++ b/src/tgen/tgen_rts/tgen-types-array_types.ads @@ -104,6 +104,10 @@ package TGen.Types.Array_Types is (Self : Constrained_Array_Typ) return Strategy_Type'Class; -- Generate a strategy for the generation for a Constrained_Array_Typ + function Size (Self : Constrained_Array_Typ) return Big_Integer; + -- Return the size, in number of elements, of self, if all indices are + -- static. Return an unspecified negative value otherwise. + type Constr_Array_Enum_Strat is new Enum_Strategy_Type with record Arr_T : SP.Ref; -- Type of the array for which we are generating values diff --git a/testsuite/tests/test/194_const_array_limit/pkg.ads b/testsuite/tests/test/194_const_array_limit/pkg.ads new file mode 100644 index 00000000..1d5fa5f5 --- /dev/null +++ b/testsuite/tests/test/194_const_array_limit/pkg.ads @@ -0,0 +1,26 @@ +package Pkg is + + type Long_Array is array (Integer range 1 .. Integer'Last) of Integer; + + type Long_Array_2 is array (Integer) of Integer; + + subtype Large_Int is Positive range 1 .. 100000; + + subtype Long_Array_3 is String (Large_Int); + + type Big_Rec is record + Long_Component : String (Large_Int); + end record; + + function First (Arr : Long_Array) return Integer is (Arr (1)); + + function First (Arr : Long_Array_2) return Integer is (Arr (Integer'First)); + + function First (Arr : Long_Array_3) return Character is (Arr (1)); + + function First (X : Big_Rec) return Character is (X.Long_Component (1)); + + function Dummy (X : Positive) return Positive is (X); + -- Just here so there is something to generate. + +end Pkg; diff --git a/testsuite/tests/test/194_const_array_limit/prj.gpr b/testsuite/tests/test/194_const_array_limit/prj.gpr new file mode 100644 index 00000000..1c32e054 --- /dev/null +++ b/testsuite/tests/test/194_const_array_limit/prj.gpr @@ -0,0 +1,5 @@ +project Prj is + + for Object_Dir use "obj"; + +end Prj; diff --git a/testsuite/tests/test/194_const_array_limit/test.out b/testsuite/tests/test/194_const_array_limit/test.out new file mode 100644 index 00000000..2c4f5764 --- /dev/null +++ b/testsuite/tests/test/194_const_array_limit/test.out @@ -0,0 +1,23 @@ +gnattest: Error while processing : +pkg.first.Arr: pkg.long_array is not supported (array type has more elements (2147483647) than the configured limit (1000)) + +gnattest: Error while processing : +pkg.first.Arr: pkg.long_array_2 is not supported (array type has more elements (4294967296) than the configured limit (1000)) + +gnattest: Error while processing : +pkg.first.Arr: pkg.long_array_3 is not supported (array type has more elements (100000) than the configured limit (1000)) + +gnattest: Error while processing : +Failed to translate type of component: array type has more elements (100000) than the configured limit (1000) + +pkg.ads:23:4: info: corresponding test PASSED +pkg.ads:23:4: info: corresponding test PASSED +pkg.ads:23:4: info: corresponding test PASSED +pkg.ads:23:4: info: corresponding test PASSED +pkg.ads:23:4: info: corresponding test PASSED +pkg.ads:15:4: error: corresponding test FAILED: Test not implemented. (pkg-test_data-tests.adb:45) +pkg.ads:17:4: error: corresponding test FAILED: Test not implemented. (pkg-test_data-tests.adb:66) +pkg.ads:19:4: error: corresponding test FAILED: Test not implemented. (pkg-test_data-tests.adb:87) +pkg.ads:21:4: error: corresponding test FAILED: Test not implemented. (pkg-test_data-tests.adb:108) +pkg.ads:23:4: error: corresponding test FAILED: Test not implemented. (pkg-test_data-tests.adb:129) +10 tests run: 5 passed; 5 failed; 0 crashed. diff --git a/testsuite/tests/test/194_const_array_limit/test.sh b/testsuite/tests/test/194_const_array_limit/test.sh new file mode 100644 index 00000000..e3bf0d93 --- /dev/null +++ b/testsuite/tests/test/194_const_array_limit/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +gnattest -P prj.gpr --gen-test-vectors +gprbuild -P obj/gnattest/harness/test_driver.gpr -q +./obj/gnattest/harness/test_runner diff --git a/testsuite/tests/test/194_const_array_limit/test.yaml b/testsuite/tests/test/194_const_array_limit/test.yaml new file mode 100644 index 00000000..972f6f3c --- /dev/null +++ b/testsuite/tests/test/194_const_array_limit/test.yaml @@ -0,0 +1,10 @@ +description: + Check that TGen properly rejects constrained array types which are over the + configured array size limit. + We do this by attempting to generate tests with gnattest for a big array type, + and checking the emitted warnings. + +driver: shell_script + +control: + - [XFAIL, 'x86', 'Marshalling not working for 32bits (UB03-008)']