thrift: remove thrift-0002 duplicate (same defect as thrift-0001 is_struct_storage_not_throwing)

This commit is contained in:
russell@unturf.com 2026-03-29 22:27:35 -04:00
parent 657dac6c22
commit 538bc6335e
2 changed files with 0 additions and 207 deletions

View file

@ -1,22 +0,0 @@
diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
--- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
@@ -5100,7 +5100,8 @@ bool t_cpp_generator::is_struct_storage_not_throwing(t_struct* tstruct) const {
vector<t_field*> members = tstruct->get_members();
+ std::unordered_set<t_field*> memberSet(members.begin(), members.end()); // O(1) dedup
for(size_t i=0; i < members.size(); ++i) {
t_type* type = get_true_type(members[i]->get_type());
@@ -5124,8 +5124,9 @@ bool t_cpp_generator::is_struct_storage_not_throwing(t_struct* tstruct) const {
if(type->is_struct()) {
const vector<t_field*>& more = ((t_struct*)type)->get_members();
for(auto it = more.begin(); it < more.end(); ++it) {
- if(std::find(members.begin(), members.end(), *it) == members.end())
+ if(memberSet.find(*it) == memberSet.end()) {
members.push_back(*it);
+ memberSet.insert(*it);
+ }
}
continue;
}

View file

@ -1,185 +0,0 @@
package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* CWE-407 unit test: thrift-0002
*
* Models t_cpp_generator::is_struct_storage_not_throwing() member deduplication.
*
* DEFECT: When a struct contains nested struct fields, the generator accumulates
* all transitively-reachable fields in a vector<t_field*> members.
* For each new field from a nested struct, std::find(members.begin(),
* members.end(), field) is O(M) where M = current accumulated size.
* Total: O(M²) for M transitively-reachable members.
*
* FIX: Maintain a parallel unordered_set<t_field*> for O(1) membership.
* Total: O(M).
*
* Asserts: slowOps > fastOps * 10 at M=500.
*/
public class Thrift0002CppGenStructDedupTest {
/**
* Simulates the defective member deduplication.
* members grows as nested struct fields are discovered.
* std::find is O(current members size).
*/
static long slow(int numMembers) {
// Simulate: outer struct has 1 field of a nested struct,
// which has numMembers unique fields.
List<Integer> members = new ArrayList<>();
// Start with some initial members
members.add(0);
long ops = 0;
// Add numMembers fields from the nested struct, each O(members.size()) to check
for (int i = 1; i <= numMembers; i++) {
// std::find(members.begin(), members.end(), i)
boolean found = false;
for (Integer m : members) {
ops++;
if (m.equals(i)) {
found = true;
break;
}
}
if (!found) {
members.add(i);
}
}
return ops;
}
/**
* Simulates the patched version.
* Parallel HashSet for O(1) membership check.
*/
static long fast(int numMembers) {
List<Integer> members = new ArrayList<>();
Set<Integer> memberSet = new HashSet<>();
members.add(0);
memberSet.add(0);
long ops = 0;
for (int i = 1; i <= numMembers; i++) {
ops++; // O(1) hash probe
if (!memberSet.contains(i)) {
members.add(i);
memberSet.add(i);
}
}
return ops;
}
/**
* Worst case: later members that are duplicates require scanning the full list.
* First M unique, then M duplicates appended all duplicates require full scan.
*/
static long slowWorstCase(int numUnique) {
List<Integer> members = new ArrayList<>();
for (int i = 0; i < numUnique; i++) members.add(i);
long ops = 0;
// Try to add each member again all fail (duplicates), scanning full list
for (int i = 0; i < numUnique; i++) {
for (Integer m : members) {
ops++;
if (m.equals(i)) break; // always found
}
}
return ops;
}
static long fastWorstCase(int numUnique) {
Set<Integer> memberSet = new HashSet<>();
for (int i = 0; i < numUnique; i++) memberSet.add(i);
long ops = 0;
for (int i = 0; i < numUnique; i++) {
ops++; // O(1)
memberSet.contains(i);
}
return ops;
}
public static void main(String[] args) {
int passed = 0;
int total = 0;
// Test 1: 200 new unique members being deduplicated
{
total++;
int M = 200;
long sOps = slow(M);
long fOps = fast(M);
// slow: sum(1..M) M²/2 = 20000
// fast: M = 200
boolean ok = sOps > fOps * 10L;
System.out.printf("Test 1 [M=%d slow=%d fast=%d ratio=%.1fx]: %s%n",
M, sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 2: 500 new unique members
{
total++;
int M = 500;
long sOps = slow(M);
long fOps = fast(M);
boolean ok = sOps > fOps * 50L;
System.out.printf("Test 2 [M=%d slow=%d fast=%d ratio=%.1fx]: %s%n",
M, sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 3: worst case 300 existing members, 300 duplicates attempted
{
total++;
int M = 300;
long sOps = slowWorstCase(M);
long fOps = fastWorstCase(M);
// slow: avg M/2 per duplicate probe = M²/2 = 45000
// fast: M = 300
boolean ok = sOps > fOps * 50L;
System.out.printf("Test 3 worst-case [M=%d slow=%d fast=%d ratio=%.1fx]: %s%n",
M, sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Test 4: correctness both produce same deduplicated set
{
total++;
int M = 100;
List<Integer> slowResult = new ArrayList<>();
slowResult.add(0);
for (int i = 1; i <= M; i++) {
if (!slowResult.contains(i)) slowResult.add(i);
}
List<Integer> fastResult = new ArrayList<>();
Set<Integer> fastSet = new HashSet<>();
fastResult.add(0);
fastSet.add(0);
for (int i = 1; i <= M; i++) {
if (!fastSet.contains(i)) {
fastResult.add(i);
fastSet.add(i);
}
}
boolean ok = slowResult.equals(fastResult) && slowResult.size() == M + 1;
System.out.printf("Test 4 [correctness M=%d slowSize=%d fastSize=%d equal=%b]: %s%n",
M, slowResult.size(), fastResult.size(), slowResult.equals(fastResult),
ok ? "PASS" : "FAIL");
if (ok) passed++;
}
System.out.printf("%d/%d PASS%n", passed, total);
if (passed != total) System.exit(1);
}
}