Add 88 new defect entries to HIGH and MEDIUM tables:
HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002
MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
ovs-0001, onos-0003, odl-0002, jetty-0001
PDF: 976K
2.2 KiB
memcached-0001: slabs_clsid() O(n) linear scan over sorted array
Target: memcached/memcached
Severity: MEDIUM
CWE: CWE-407 (Inefficient Algorithmic Complexity)
File: slabs.c:77 (slabs_clsid)
Status: PATCHED
Description
slabs_clsid(size_t size) finds the smallest slab class whose chunk size
accommodates the requested allocation. The slab class array (slabclass[]) is
sorted in ascending order of size. The current implementation walks it
linearly with a while loop.
unsigned int slabs_clsid(const size_t size) {
int res = POWER_SMALLEST;
if (size == 0 || size > settings.item_size_max)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest)
return power_largest;
return res;
}
power_largest reaches up to 63 (MAX_NUMBER_OF_SLAB_CLASSES − 1 = 63).
Binary search over a sorted array of 63 entries requires at most 6 comparisons
(⌈log₂(63)⌉ = 6) vs. up to 63 comparisons in the linear case — a ~10× speedup.
slabs_clsid is called on every item allocation:
do_item_alloc— called for everySET/ADD/REPLACE/APPEND/PREPEND/CAScommanddo_item_alloc_chunk— called per chunk of large itemsitem_store_check— called duringSETpre-validation
Under high write throughput this is a hot path: a 200k SET/s workload calls
slabs_clsid ~200,000 times per second.
Fix
Replace the while loop with a binary search over slabclass[1..power_largest]:
unsigned int slabs_clsid(const size_t size) {
if (size == 0 || size > settings.item_size_max)
return 0;
int lo = POWER_SMALLEST, hi = power_largest;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (slabclass[mid].size < size)
lo = mid + 1;
else
hi = mid;
}
return lo;
}
This is safe because slabclass[].size is strictly monotonically increasing
(guaranteed by the slabs_init construction loop).
See patch: defects/memcached/patch/0001-slabs-clsid-binary-search.patch
Ops ratio (unit test)
63 slab classes, query for the largest size:
- slow ops: 63 (linear scan)
- fast ops: 6 (binary search, ⌈log₂(63)⌉)
- speedup: 10.5×