GNU/Linux >> Znalost Linux >  >> Linux

Jak implementovat deterministický malloc

Zajímalo by mě, čeho se snažíte dosáhnout. Pokud je váš proces deterministický, pak by vzor alokace / dealokace měl být stejný.

Jediným možným rozdílem může být adresa vrácená malloc . Ale pravděpodobně byste na ně neměli být závislí (nejjednodušší způsob je nepoužívat ukazatele jako klíčovou mapu nebo jinou datovou strukturu). A i tak by zde měl být rozdíl pouze v případě, že alokace není provedena pomocí sbrk (glibc používá anonymní mmap pro velké alokace), nebo pokud používáte mmap (jako výchozí je adresa vybrána jádrem).

Pokud opravdu chcete mít přesně stejnou adresu, jednou z možností je mít velkou statickou vyrovnávací paměť a napsat vlastní alokátor, který používá paměť z této vyrovnávací paměti. To má nevýhodu v tom, že vás nutí znát předem maximální množství paměti, které kdy budete potřebovat. Ve spustitelném souboru bez PIE (gcc -fno-pie -no-pie ), statická vyrovnávací paměť bude mít pokaždé stejnou adresu. U spustitelného souboru PIE můžete zakázat randomizaci rozložení adresního prostoru jádra pro načítání programů. Ve sdílené knihovně by deaktivace ASLR a spuštění stejného programu dvakrát mělo vést ke stejným volbám dynamického linkeru, kam mapovat knihovny.

Pokud předem neznáte maximální velikost paměti, kterou chcete použít, nebo pokud nechcete znovu kompilovat pokaždé, když se tato velikost zvětší, můžete také použít mmap k mapování velké anonymní vyrovnávací paměti na pevné adrese. Jednoduše předejte vašemu procesu velikost vyrovnávací paměti a adresu, kterou chcete použít jako parametr, a použijte vrácenou paměť k implementaci vlastního malloc navrch.

static void* malloc_buffer = NULL;
static size_t malloc_buffer_len = 0;

void* malloc(size_t size) {
    // Use malloc_buffer & malloc_buffer_len to implement your
    // own allocator. If you don't read uninitialized memory,
    // it can be deterministic.
    return memory;
}

int main(int argc, char** argv) {
    size_t buf_size = 0;
    uintptr_t buf_addr = 0;
    for (int i = 0; i < argv; ++i) {
        if (strcmp(argv[i], "--malloc-size") == 0) {
            buf_size = atoi(argv[++i]);
        }
        if (strcmp(argv[i], "--malloc-addr") == 0) {
            buf_addr = atoi(argv[++i]);
        }
    }

    malloc_buffer = mmap((void*)buf_addr, buf_size, PROT_WRITE|PROT_READ,
                         MAP_FIXED|MAP_PRIVATE, 0, 0);
    // editor's note: omit MAP_FIXED since you're checking the result anyway
    if (malloc_buffer == MAP_FAILED || malloc_buffer != (void*)but_addr) {
        // Could not get requested memory block, fail.
        exit(1);
    }

    malloc_size = buf_size;
}

Pomocí MAP_FIXED , říkáme jádru, aby nahradilo všechna existující mapování, která se překrývají s tímto novým na buf_addr .

(Poznámka redakce:MAP_FIXED pravděpodobně není to, co chcete . Zadání buf_addr jako nápověda místo NULL již požaduje tuto adresu, pokud je to možné. S MAP_FIXED , mmap buď vrátí chybu nebo adresu, kterou jste mu zadali. malloc_buffer != (void*)but_addr check dává smysl pro non-FIXED případ, který nenahradí stávající mapování vašeho kódu nebo sdílenou knihovnu ani nic jiného. Linux 4.17 představil MAP_FIXED_NOREPLACE který můžete použít k tomu, aby mmap vrátil chybu místo paměti na nesprávné adrese, kterou nechcete používat. Ale přesto ponechte kontrolu, aby váš kód fungoval na starších jádrech.)

Pokud použijete tento blok k implementaci vlastního mallocu a nepoužijete ve svém kódu jiné nedeterministické operace, můžete mít úplnou kontrolu nad hodnotami ukazatelů.

Předpokládejme, že váš vzor použití malloc / free je deterministický. A že nepoužíváte knihovny, které jsou nedeterministické.

Myslím si však, že jednodušším řešením je ponechat vaše algoritmy deterministické a nezáviset na adresách. To je možné. Pracoval jsem na rozsáhlém projektu, kde muselo více počítačů aktualizovat stav deterministicky (takže každý program měl stejný stav a pouze vysílal vstupy). Pokud nepoužíváte ukazatel pro jiné věci než pro odkazování na objekty (nejdůležitější věcí je nikdy nepoužívat hodnotu ukazatele pro nic, ne jako hash, ne jako klíč v mapě, ...), pak váš stav zůstane deterministický .

Pokud nechcete udělat snímek celé paměti procesu a provést binární rozdíl, abyste zjistili divergenci. Myslím, že je to špatný nápad, protože jak budete vědět, že oba dosáhli stejného bodu ve svém výpočtu? Je mnohem snazší porovnat výstup nebo nechat proces vypočítat hash stavu a použít jej ke kontrole, že jsou synchronizované, protože můžete ovládat, kdy se to stane (a tím se také stane deterministickým, jinak je vaše měření nedeterministické).


Co není deterministické, není pouze malloc ale mmap (základní systémové volání pro získání více paměťového prostoru; není to funkce, je to systémové volání, takže je z pohledu aplikace elementární nebo atomické; takže jej nemůžete přepsat v aplikaci) kvůli randomizaci rozložení adresního prostoru na Linuxu.

Můžete to deaktivovat pomocí

 echo 0 > /proc/sys/kernel/randomize_va_space

jako root nebo přes sysctl.

Pokud nezakážete randomizaci rozložení adresního prostoru, uvíznete.

A podobnou otázku jste položili již dříve, kde jsem vysvětlil, že vaše malloc -s nebude vždy deterministické.

Stále si myslím, že pro některé praktické aplikace malloc nemůže být deterministický. Představte si například program s hashovací tabulkou klíčovanou pid -s podřízených procesů, které spouští. Kolize v této tabulce nebude stejná ve všech vašich procesech atd.

Takže věřím, že se vám nepodaří vytvořit malloc deterministické ve vašem smyslu, ať už se budete snažit cokoli (pokud se neomezíte na velmi úzkou třídu aplikací pro kontrolní bod, tak úzkou, že váš software nebude příliš užitečný).


Jednoduše řečeno, jak uvedli jiní:pokud je provádění instrukcí vašeho programu deterministické, pak paměť vrací malloc() bude deterministický. To předpokládá, že implementace vašeho systému nemá nějaké volání random() nebo něco v tom smyslu. Pokud si nejste jisti, přečtěte si kód nebo dokumentaci pro váš systém malloc .

To je snad s výjimkou ASLR, jak uvedli i ostatní. Pokud nemáte oprávnění root, můžete je pro každý proces zakázat pomocí personality(2) syscall a parametr ADDR_NO_RANDOMIZE. Více informací o osobnostech naleznete zde.

Edit:Také bych měl říci, pokud si to neuvědomujete:to, co děláte, se nazývá bisimulace a je to dobře prostudovaná technika. Pokud neznáte terminologii, mohlo by pomoci mít toto klíčové slovo pro vyhledávání.


Linux
  1. Jak číst IP adresu pozpátku?

  2. Jak se přihlásit do cPanelu

  3. Jak zakázat IP adresu v SmarterMail

  1. Jak odmítnout IP adresu v cPanel

  2. Jak přidat IP adresu na seznam povolených v SmarterMail

  3. Jak zakoupit vyhrazenou IP adresu

  1. Jak zjistit svou IP adresu v Linuxu

  2. Jak změnit IP adresu na AlmaLinux

  3. Jak najdu svou IP adresu v Ubuntu