From abed679679a4a80bc1b916f634e2378c6d3124cf Mon Sep 17 00:00:00 2001 From: Ladislav Vadkerti Date: Thu, 12 Mar 2026 14:40:06 +0100 Subject: [PATCH] [C#] Fix incorrect offset type in verifier (#8937) --- net/FlatBuffers/FlatBufferVerify.cs | 49 ++++++++----------- .../FlatBuffers.Test/FlatBuffers.Test.csproj | 19 +++++-- .../FlatBuffersExampleTests.cs | 16 ++++-- tests/FlatBuffers.Test/NetTest.bat | 38 +++++++++----- tests/FlatBuffers.Test/NetTest.sh | 40 +++++++++------ tests/FlatBuffers.Test/clean.sh | 6 +-- 6 files changed, 102 insertions(+), 66 deletions(-) diff --git a/net/FlatBuffers/FlatBufferVerify.cs b/net/FlatBuffers/FlatBufferVerify.cs index e32256725dd..ce65031198f 100644 --- a/net/FlatBuffers/FlatBufferVerify.cs +++ b/net/FlatBuffers/FlatBufferVerify.cs @@ -107,7 +107,7 @@ public Verifier() { // Verifier buffer verifier_buffer = null; - // Verifier settings + // Verifier settings verifier_options = null; // Depth counter depth_cnt = 0; @@ -236,26 +236,17 @@ private short ReadVOffsetT(ByteBuffer buf, int pos) private short GetVRelOffset(int pos, short vtableOffset) { short VOffset = 0; - // Used try/catch because pos typa as int 32bit - try + // First, get vtable offset + int vtable = pos - ReadSOffsetT(verifier_buffer, pos); + // Check that offset points to vtable area (is smaller than vtable size) + if (vtableOffset < ReadVOffsetT(verifier_buffer, vtable)) { - // First, get vtable offset - short vtable = Convert.ToInt16(pos - ReadSOffsetT(verifier_buffer, pos)); - // Check that offset points to vtable area (is smaller than vtable size) - if (vtableOffset < ReadVOffsetT(verifier_buffer, vtable)) - { - // Now, we can read offset value - TODO check this value against size of table data - VOffset = ReadVOffsetT(verifier_buffer, vtable + vtableOffset); - } - else - { - VOffset = 0; - } + // Now, we can read offset value - TODO check this value against size of table data + VOffset = ReadVOffsetT(verifier_buffer, vtable + vtableOffset); } - catch (Exception e) + else { - Console.WriteLine("Exception: {0}", e); - return VOffset; + VOffset = 0; } return VOffset; @@ -297,7 +288,7 @@ private bool CheckAlignment(uint element, ulong align) return (((element & (align - 1)) == 0) || (!options.alignmentCheck)); } - /// Check if element is valid in buffer area. + /// Check if element is valid in buffer area. /// Value defines the offset/position to element /// Size of element /// Return True when Element is correct @@ -340,7 +331,7 @@ private checkElementStruct CheckVectorOrString(uint pos, ulong elementSize) ulong max_elements = (FLATBUFFERS_MAX_BUFFER_SIZE / elementSize); if (size >= max_elements) { - // Protect against byte_size overflowing. + // Protect against byte_size overflowing. // result.elementValid = false; result.elementOffset = 0; return result; } @@ -457,7 +448,7 @@ private uint GetIndirectOffset(uint pos) /// Verify beginning of table /// Position in the Table - /// Return True when the verification of the beginning of the table is passed + /// Return True when the verification of the beginning of the table is passed // (this method is used internally by generated verification functions) public bool VerifyTableStart(uint tablePos) { @@ -500,7 +491,7 @@ public bool VerifyField(uint tablePos, short offsetId, ulong elementSize, ulong return !required; // it is OK if field is not required } - /// Verify string + /// Verify string /// Position in the Table /// Offset to the String element /// Required Value when the offset == 0 @@ -561,7 +552,7 @@ public bool VerifyVectorOfStrings(uint tablePos, short offsetId, bool required) return false; } var vecOffset = GetIndirectOffset(offset); - return CheckVectorOfObjects(vecOffset, CheckStringFunc); + return CheckVectorOfObjects(vecOffset, CheckStringFunc); } /// Verify vector of tables (objects). Tables are verified using generated verifyObjFunc @@ -657,7 +648,7 @@ public bool VerifyUnionData(uint pos, ulong elementSize, ulong align) /// Verify string referenced by absolute offset value /// Position of Union String in the Byte Buffer - /// Return True when the verification of the Union String is passed + /// Return True when the verification of the Union String is passed // (this method is used internally by generated verification functions) public bool VerifyUnionString(uint pos) { @@ -784,15 +775,15 @@ public bool VerifyVectorOfUnion(uint tablePos, short typeOffsetId, short offsetI // /* Verify Monster table. Buffer name is 'MONS' and contains data length prefix */ // isValid = verifier.verifyBuffer("MONS", true, MonsterVerify) /// Method verifies flatbuffer data using generated Table verification function - /// + /// /// The expected identifier of buffer data /// Flag should be true when buffer is prefixed with content size /// Function to be used for verification. This function is generated by compiler and included in each table definition file /// Return True when verification of FlatBuffer passed /// - /// Example 1. Verify Monster table. Ignore buffer name and assume buffer does not contain data length prefix + /// Example 1. Verify Monster table. Ignore buffer name and assume buffer does not contain data length prefix /// isValid = verifier.VerifyBuffer(bb, false, MonsterVerify) - /// Example 2. Verify Monster table. Buffer name is 'MONS' and contains data length prefix + /// Example 2. Verify Monster table. Buffer name is 'MONS' and contains data length prefix /// isValid = verifier.VerifyBuffer("MONS", true, MonsterVerify) /// public bool VerifyBuffer(string identifier, bool sizePrefixed, VerifyTableAction verifyAction) @@ -802,7 +793,7 @@ public bool VerifyBuffer(string identifier, bool sizePrefixed, VerifyTableAction numTables = 0; var start = (uint)(verifier_buffer.Position); - if (sizePrefixed) + if (sizePrefixed) { start = (uint)(verifier_buffer.Position) + SIZE_PREFIX_LENGTH; if(!CheckScalar((uint)(verifier_buffer.Position), SIZE_PREFIX_LENGTH)) @@ -814,7 +805,7 @@ public bool VerifyBuffer(string identifier, bool sizePrefixed, VerifyTableAction { return false; } - } + } return CheckBufferFromStart(identifier, start, verifyAction); } } diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj index c1e72331435..e49b022405d 100644 --- a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj +++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj @@ -2,7 +2,10 @@ Exe - net6.0;net8.0 + net6.0 + net6.0;net8.0 + net6.0;net8.0;net10.0 + $(SolutionDir).tmp @@ -19,11 +22,11 @@ $(DefineConstants);ENABLE_SPAN_T - + true - + true @@ -199,6 +202,16 @@ + + + + + + + diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index d5774571c86..d4886583908 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -20,6 +20,7 @@ using MyGame.Example; using optional_scalars; using KeywordTest; +using System; namespace Google.FlatBuffers.Test { @@ -66,6 +67,9 @@ private void CanCreateNewFlatBufferFromScratch(bool sizePrefix) var str = fbb.CreateString("MyMonster"); var test1 = fbb.CreateString("test1"); var test2 = fbb.CreateString("test2"); + var testArrayOfBoolsData = new bool[UInt16.MaxValue]; + + Array.Fill(testArrayOfBoolsData, true); Monster.StartInventoryVector(fbb, 5); @@ -90,6 +94,7 @@ private void CanCreateNewFlatBufferFromScratch(bool sizePrefix) fbb.AddOffset(test1.Value); var testArrayOfString = fbb.EndVector(); + var testArrayOfBools = Monster.CreateTestarrayofboolsVector(fbb, testArrayOfBoolsData); var longsVector = Monster.CreateVectorOfLongsVector(fbb, new long[] { 1, 100, 10000, 1000000, 100000000 }); var doublesVector = Monster.CreateVectorOfDoublesVector(fbb, new double[] { -1.7976931348623157e+308, 0, 1.7976931348623157e+308 }); @@ -105,6 +110,7 @@ private void CanCreateNewFlatBufferFromScratch(bool sizePrefix) Monster.AddTestarrayofstring(fbb, testArrayOfString); Monster.AddTestbool(fbb, true); Monster.AddTestarrayoftables(fbb, sortMons); + Monster.AddTestarrayofbools(fbb, testArrayOfBools); Monster.AddVectorOfLongs(fbb, longsVector); Monster.AddVectorOfDoubles(fbb, doublesVector); var mon = Monster.EndMonster(fbb); @@ -147,7 +153,7 @@ private void CanCreateNewFlatBufferFromScratch(bool sizePrefix) //Attempt to mutate Monster fields and check whether the buffer has been mutated properly // revert to original values after testing Monster monster = Monster.GetRootAsMonster(dataBuffer); - + // mana is optional and does not exist in the buffer so the mutation should fail // the mana field should retain its default value @@ -1150,8 +1156,8 @@ public void TestKeywordEscaping() { var offset = KeywordsInTable.CreateKeywordsInTable( fbb, KeywordTest.ABC.@stackalloc, KeywordTest.@public.NONE); fbb.Finish(offset.Value); - - KeywordsInTable keywordsInTable = + + KeywordsInTable keywordsInTable = KeywordsInTable.GetRootAsKeywordsInTable(fbb.DataBuffer); Assert.AreEqual(keywordsInTable.Is, KeywordTest.ABC.@stackalloc); @@ -1166,7 +1172,7 @@ public void AddOptionalEnum_WhenPassNull_ShouldWorkProperly() { ScalarStuff.AddMaybeEnum(fbb, null); var offset = ScalarStuff.EndScalarStuff(fbb); ScalarStuff.FinishScalarStuffBuffer(fbb, offset); - + ScalarStuff scalarStuff = ScalarStuff.GetRootAsScalarStuff(fbb.DataBuffer); Assert.AreEqual(null, scalarStuff.MaybeEnum); } @@ -1179,7 +1185,7 @@ public void SortKey_WithDefaultedValue_IsFindable() { // test for https://github.com/google/flatbuffers/issues/7380. var fbb = new FlatBufferBuilder(1); - // Create a vector of Stat objects, with Count being the key. + // Create a vector of Stat objects, with Count being the key. var stat_offsets = new Offset[4]; for(ushort i = 0; i < stat_offsets.Length; i++) { Stat.StartStat(fbb); diff --git a/tests/FlatBuffers.Test/NetTest.bat b/tests/FlatBuffers.Test/NetTest.bat index a7097a509e1..904fd8600d4 100644 --- a/tests/FlatBuffers.Test/NetTest.bat +++ b/tests/FlatBuffers.Test/NetTest.bat @@ -2,19 +2,35 @@ @REM Builds a .NET solution file, adds the project, builds it @REM and executes it. Cleans up all generated files and directories. +set CORE_NAME=FlatBuffers.Test +set PROJ_FILE=%CORE_NAME%.csproj +set PROJ_DIR=%cd% set TEMP_BIN=.tmp @REM Run the .NET tests -set CORE_FILE=FlatBuffers.Test -set CORE_PROJ_FILE=%CORE_FILE%.csproj -set CORE_SLN_FILE=%CORE_FILE%.sln -dotnet new sln --force --name %CORE_FILE% -dotnet sln %CORE_SLN_FILE% add %CORE_PROJ_FILE% -dotnet build -c Release -o %TEMP_BIN% -v quiet %CORE_PROJ_FILE% -%TEMP_BIN%\%CORE_FILE%.exe -del /f %CORE_SLN_FILE% +dotnet new sln +dotnet sln add %PROJ_FILE% -@REM TODO(dbaileychess): Support the other configurations in NetTest.sh +@REM Testing with default options. +dotnet msbuild -restore -target:Build -property:Configuration=Release,OutputDir=%TEMP_BIN% -verbosity:quiet %PROJ_FILE% +cd %TEMP_BIN% +.\%CORE_NAME%.exe +cd %PROJ_DIR% +rd /S /Q %TEMP_BIN% bin obj + +@REM Repeat with unsafe versions +dotnet msbuild -restore -target:Build -property:Configuration=Release,UnsafeByteBuffer=true,OutputDir=%TEMP_BIN% -verbosity:quiet %PROJ_FILE% +cd %TEMP_BIN% +.\%CORE_NAME%.exe +cd %PROJ_DIR% +rd /S /Q %TEMP_BIN% bin obj + +@REM Repeat with SpanT versions +dotnet msbuild -restore -target:Build -property:Configuration=Release,EnableSpanT=true,OutputDir=%TEMP_BIN% -verbosity:quiet %PROJ_FILE% +cd %TEMP_BIN% +.\%CORE_NAME%.exe +cd %PROJ_DIR% +rd /S /Q %TEMP_BIN% bin obj + +del /f %CORE_NAME%.sln* -@REM remove the temp bin directory, with files (/S) and quietly (/Q) -RD /S /Q %TEMP_BIN% diff --git a/tests/FlatBuffers.Test/NetTest.sh b/tests/FlatBuffers.Test/NetTest.sh index f2f678674d5..90916acdb94 100755 --- a/tests/FlatBuffers.Test/NetTest.sh +++ b/tests/FlatBuffers.Test/NetTest.sh @@ -1,36 +1,46 @@ #!/bin/sh -PROJ_FILE=FlatBuffers.Test.csproj +CORE_NAME=FlatBuffers.Test +PROJ_FILE=$CORE_NAME.csproj +PROJ_DIR=$PWD -TEMP_DOTNET_DIR=.dotnet_tmp +TEMP_DOTNET_DIR=$PWD/.dotnet_tmp TEMP_BIN=.tmp [ -d $TEMP_DOTNET_DIR ] || mkdir $TEMP_DOTNET_DIR [ -f dotnet-install.sh ] || curl -OL https://dot.net/v1/dotnet-install.sh -./dotnet-install.sh --version latest --install-dir $TEMP_DOTNET_DIR +chmod +x ./dotnet-install.sh +./dotnet-install.sh --version Latest --install-dir $TEMP_DOTNET_DIR || exit 1 +export DOTNET_ROOT=$TEMP_DOTNET_DIR +export PATH=$PATH:$DOTNET_DIR DOTNET=$TEMP_DOTNET_DIR/dotnet $DOTNET new sln $DOTNET sln add $PROJ_FILE -$DOTNET restore -r linux-x64 $PROJ_FILE # Testing with default options. -msbuild -property:Configuration=Release,OutputPath=$TEMP_BIN -verbosity:quiet $PROJ_FILE -$TEMP_BIN/FlatBuffers.Core.Test.exe -rm -fr $TEMP_BIN +$DOTNET msbuild -restore -target:Build -property:Configuration=Release,OutputDir=$TEMP_BIN -verbosity:quiet $PROJ_FILE +cd $TEMP_BIN +./$CORE_NAME +cd $PROJ_DIR +rm -fr $TEMP_BIN bin obj # Repeat with unsafe versions -msbuild -property:Configuration=Release,UnsafeByteBuffer=true,OutputPath=$TEMP_BIN -verbosity:quiet $PROJ_FILE -$TEMP_BIN/FlatBuffers.Core.Test.exe -rm -fr $TEMP_BIN +$DOTNET msbuild -restore -target:Build -property:Configuration=Release,UnsafeByteBuffer=true,OutputDir=$TEMP_BIN -verbosity:quiet $PROJ_FILE +cd $TEMP_BIN +./$CORE_NAME +cd $PROJ_DIR +rm -fr $TEMP_BIN bin obj # Repeat with SpanT versions -msbuild -property:Configuration=Release,EnableSpanT=true,OutputPath=$TEMP_BIN -verbosity:quiet $PROJ_FILE -$TEMP_BIN/FlatBuffers.Core.Test.exe -rm -fr $TEMP_BIN +$DOTNET msbuild -restore -target:Build -property:Configuration=Release,EnableSpanT=true,OutputDir=$TEMP_BIN -verbosity:quiet $PROJ_FILE +cd $TEMP_BIN +./$CORE_NAME +cd $PROJ_DIR +rm -fr $TEMP_BIN bin obj + +rm $CORE_NAME.sln* -rm FlatBuffers.Test.sln -rm -rf obj diff --git a/tests/FlatBuffers.Test/clean.sh b/tests/FlatBuffers.Test/clean.sh index 41f6a4a590d..b57bb563abc 100755 --- a/tests/FlatBuffers.Test/clean.sh +++ b/tests/FlatBuffers.Test/clean.sh @@ -4,7 +4,7 @@ # The script NetTest.sh installs these as needed. [ -d .dotnet_tmp ] && rm -rf .dotnet_tmp -[ -d packages ] && rm -rf packages [ -d .tmp ] && rm -rf .tmp -[ -f nuget.exe ] && rm nuget.exe -[ -f dotnet-intall.sh ] && rm dotnet-install.sh +[ -d bin ] && rm -rf bin +[ -d obj ] && rm -rf obj +[ -f dotnet-install.sh ] && rm dotnet-install.sh