Skip to content

openvswitch reserve_sfa_size integer coercion OOB (CVE-2022-2639)

Open vSwitch'in reserve_sfa_size() fonksiyonundaki bir integer coercion hatası — karışık int/size_t aritmetiği MAX_ACTIONS_BUFSIZE - next_offset çıkarmasında bir integer underflow üretir — yeterince büyük bir action list'in -EMSGSIZE reddini atlamasına izin verir; böylece kernel, flow action'larını sw_flow_actions buffer'ı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_action iç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 -EMSGSIZE döndürür.
  • Unprivileged user namespace'leri devre dışı bırak (kernel.unprivileged_userns_clone=0) veya gerekmeyen yerlerde openvswitch modülünü blocklist'e al; böylece unprivileged user'lar için attack surface ortadan kalkar.

References