java-topology/defects/libvirt/patch/libvirt-0002-cpu-x86-model-from-cpu-feature-find-hashmap.md

117 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000592
# libvirt-0002: x86ModelFromCPU() x86FeatureFind O(C×F) linear scan per feature per VM start
## Classification
- **Severity**: MEDIUM
- **CWE**: CWE-407 (Algorithmic Complexity — Inefficient Algorithmic Complexity)
- **Component**: `src/cpu/cpu_x86.c`
## Location
`src/cpu/cpu_x86.c`, function `x86ModelFromCPU()`, lines 14071424
```c
for (i = 0; i < cpu->nfeatures; i++) { /* outer: all CPU features in def (~50200) */
...
if (!(feature = x86FeatureFind(map, cpu->features[i].name))) { /* O(F) linear scan */
virReportError(...);
return NULL;
}
...
}
```
`x86FeatureFind()` at line 416:
```c
for (i = 0; i < map->nfeatures; i++) { /* scans ALL ~500 global features */
if (STREQ(map->features[i]->name, name))
return map->features[i];
}
```
## Pattern
`x86ModelFromCPU()` is called during VM start, CPU capability check, and live
migration to build a CPU model from a `virCPUDef`. For each of the C explicit
CPU features in the domain definition it calls `x86FeatureFind()` which does a
full linear scan of the global feature map (F entries).
Total: O(C × F) ≈ 100 × 500 = 50,000 string comparisons per call.
`x86ModelFromCPU()` is called multiple times per VM start (at minimum twice from
`virCPUx86UpdateLive()` via `x86ModelFromCPU(cpu, map, -1)` and
`x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_DISABLE)`).
A parallel O(M) linear scan exists in `x86ModelFind()` (line 13551364) which
scans all M CPU model definitions by name.
## Call Path (semi-hot: every VM start + migration)
```
qemuProcessStart()
qemuProcessFetchGuestCPU()
virCPUx86UpdateLive()
x86ModelFromCPU(cpu, map, -1) # O(C×F)
x86ModelFromCPU(cpu, map, DISABLE) # O(C×F) again
for (i < cpu->nfeatures)
x86FeatureFind(map, name) # O(F) linear scan each call
```
## Speedup
At C=100, F=500: 50,000 comparisons → 100 hash lookups (500× op-count reduction).
Per VM start, with two calls: 100,000 → 200 (500× overall).
## Patch
Index `map->features` in a `GHashTable` keyed by feature name, built once when
the map is loaded. `x86ModelFind()` similarly benefits from a model-name hash.
```diff
--- a/src/cpu/cpu_x86.c
+++ b/src/cpu/cpu_x86.c
@@ -200,6 +200,8 @@ struct _virCPUx86Map {
virCPUx86Model **models;
size_t nmodels;
+ GHashTable *featureByName; /* char* → virCPUx86Feature*, built at load */
+ GHashTable *modelByName; /* char* → virCPUx86Model*, built at load */
};
@@ -416,7 +416,10 @@ x86FeatureFind(virCPUx86Map *map, const char *name)
- for (i = 0; i < map->nfeatures; i++) {
- if (STREQ(map->features[i]->name, name))
- return map->features[i];
- }
- return NULL;
+ if (!map->featureByName)
+ return NULL;
+ return g_hash_table_lookup(map->featureByName, name);
}
@@ -1211,6 +1211,9 @@ x86MapAddFeature(...)
VIR_APPEND_ELEMENT(map->features, map->nfeatures, feature);
+ if (!map->featureByName)
+ map->featureByName = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(map->featureByName, feature->name, feature);
}
@@ -1355,7 +1355,10 @@ x86ModelFind(virCPUx86Map *map, const char *name)
- for (i = 0; i < map->nmodels; i++) {
- if (STREQ(map->models[i]->name, name))
- return map->models[i];
- }
- return NULL;
+ if (!map->modelByName)
+ return NULL;
+ return g_hash_table_lookup(map->modelByName, name);
}
@@ -1769,6 +1769,9 @@ x86MapAddModel(...)
VIR_APPEND_ELEMENT(map->models, map->nmodels, model);
+ if (!map->modelByName)
+ map->modelByName = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(map->modelByName, model->name, model);
}
```
## Complexity
- Before: O(C × F) per `x86ModelFromCPU()` call; O(M) per `x86ModelFind()` call
- After: O(C) per `x86ModelFromCPU()` (hash lookup per feature); O(1) per `x86ModelFind()`