openvswitch reserve_sfa_size integer coercion OOB (CVE-2022-2639)¶
Open vSwitch'in
reserve_sfa_size()fonksiyonundaki bir integer coercion hatası — karışıkint/size_taritmetiğiMAX_ACTIONS_BUFSIZE - next_offsetçıkarmasında bir integer underflow üretir — yeterince büyük bir action list'in-EMSGSIZEreddini atlamasına izin verir; böylece kernel, flow action'larınısw_flow_actionsbuffer'ının sınırları dışına yazar. (Red Hat bunu "integer underflow leads to out-of-bounds write" olarak, MITRE/NVD ise "integer coercion error" olarak sınıflar; ikisi de aynı kusuru tanımlar.)
Mechanism¶
Size check neden oversized action list'leri reddedemiyor
Open vSwitch, bir flow'un parse edilmiş action list'ini değişken
uzunlukta bir struct sw_flow_actions olarak temsil eder; bu yapının
actions[] dizisi, action'lar bir netlink message'ından kopyalandıkça
büyür. Yeni bir action eklemeden önce kernel, buffer'ın yeterince büyük
olduğundan emin olmak için reserve_sfa_size() çağırır ve gerekirse
buffer'ı büyütür (reallocate eder). Amaçlanan invariant şudur:
toplam rezerve edilen boyut asla MAX_ACTIONS_BUFSIZE'ı aşmamalıdır
(bir flow'un action buffer'ının kesin üst sınırı) ve bir istek bunu
aşacak olursa, fonksiyon -EMSGSIZE döndürmeli ki caller işlemi iptal
etsin.
Bug, yeni boyutun nasıl hesaplanıp karşılaştırıldığında. Kavramsal olarak:
static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
int attr_len, bool log)
{
struct sw_flow_actions *acts;
int new_acts_size;
size_t req_size = NLA_ALIGN(attr_len);
int next_offset = offsetof(struct sw_flow_actions, actions) +
(*sfa)->actions_len;
if (req_size <= (ksize(*sfa) - next_offset))
goto out; /* fits in current allocation */
new_acts_size = max(next_offset + req_size, ksize(*sfa) * 2);
if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
/* INTENDED: reject. But the path here mis-handles the case */
if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
return ERR_PTR(-EMSGSIZE);
new_acts_size = MAX_ACTIONS_BUFSIZE;
}
...
}
next_offset ve req_size operand'ları karışık int/size_t
tipindedir. İşleyen next_offset (ki actions_len tarafından
sürüklenir) zaten MAX_ACTIONS_BUFSIZE'a yakın olduğunda,
MAX_ACTIONS_BUFSIZE - next_offset çıkarması underflow yapar / öyle
coerce edilir ki < req_size guard'ı tetiklenmez. Fonksiyon,
-EMSGSIZE döndürmek yerine new_acts_size = MAX_ACTIONS_BUFSIZE
olarak clamp eder ve devam eder — ama buffer artık next_offset
konumundaki yeni action'ı tutamayacak kadar küçüktür. Dolayısıyla action
verisinin ardından gelen kopyalaması tahsis edilmiş sw_flow_actions'ın
sonunu aşar; bu, içeriği (action byte'ları) ve uzunluğu attacker
etkisinde olan kontrollü bir out-of-bounds heap write'tır.
Walkthrough¶
Trigger path'i, OVS netlink family üzerinden OVS datapath flow'larını
programlayabilen herhangi bir context tarafından erişilebilir. Unprivileged
user namespace'ler sayesinde bir attacker, bir namespace içinde
CAP_NET_ADMIN elde edebilir ve ovs_datapath Generic Netlink family ile
konuşabilir.
/* 1. Open a generic-netlink socket and resolve the ovs_datapath family */
int s = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
/* 2. Create a datapath + a flow whose action list is built from MANY
* small actions so actions_len marches up toward MAX_ACTIONS_BUFSIZE. */
/* 3. Append one final action whose req_size, combined with the near-cap
* next_offset, lands in the mishandled branch of reserve_sfa_size(). */
Pratik reçete (Red Hat analizinden): yeterince çok sayıda action
içeren bir flow gönder. Her action actions_len'i (dolayısıyla
next_offset'i) artırır; next_offset yeterince büyüdüğünde, yani
MAX_ACTIONS_BUFSIZE - next_offset artık bir sonraki req_size'ı aşmaz
hale geldiğinde, reserve_sfa_size() yeterince büyük olduğu raporlanan ama
aslında olmayan bir buffer döndürür ve copy_action() sınır dışına yazar.
Etki
Sonuç, kontrollü byte'ların yazıldığı bir slab out-of-bounds write'tır. Red Hat bunu local privilege escalation (en kötü ihtimalle bir crash) olarak derecelendirir. Komşu bir slab object'ine yapılan out-of-bounds write, gerçek bir exploit'in üzerine inşa edildiği temeldir (komşu bir object'in pointer veya length field'ını bozarak) ve diğer slab-OOB LPE'lerle aynı playbook'u izler.
Upstream fix
cefa91b2332d7009bc0be5d951d6cbbf349f90f8 commit'i ile düzeltildi
("openvswitch: fix OOB access in reserve_sfa_size()"); bu commit, size
karşılaştırmasını düzeltir, böylece üst sınırı aşan bir istek güvenilir
şekilde -EMSGSIZE ile reddedilir. Fix, Linux 5.18'de yayınlandı ve
stable ile enterprise kernel'lere backport edildi.
Detection¶
ovs_nla_copy_actions/reserve_sfa_size/copy_actioniçinden kaynaklanan beklenmedik SLUB/SLAB redzone veya freelist-corruption splat'ları.- Unprivileged process'lerin OVS datapath'leri oluşturup alışılmadık şekilde uzun action list'lerle flow programlaması çoğu host'ta anomali sayılır.
Mitigation¶
- Upstream fix'i uygula; düzeltilmiş bounds check
-EMSGSIZEdöndürür. - Unprivileged user namespace'leri devre dışı bırak
(
kernel.unprivileged_userns_clone=0) veya gerekmeyen yerlerdeopenvswitchmodülünü blocklist'e al; böylece unprivileged user'lar için attack surface ortadan kalkar.